Skyward boardcore
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
BMX160.cpp
Go to the documentation of this file.
1/* Copyright (c) 2020 Skyward Experimental Rocketry
2 * Author: Davide Mor
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "BMX160.h"
24
25#include <assert.h>
26#include <utils/Constants.h>
27
28namespace Boardcore
29{
30
31BMX160::BMX160(SPIBusInterface& bus, miosix::GpioPin cs, BMX160Config config)
32 : BMX160(bus, cs, config, SPIBusConfig{})
33{
35 oldMag.magneticFieldTimestamp = 0.0f;
36 oldGyr.angularSpeedTimestamp = 0.0f;
37 oldAcc.accelerationTimestamp = 0.0f;
38}
39
40BMX160::BMX160(SPIBusInterface& bus, miosix::GpioPin cs, BMX160Config config,
41 SPIBusConfig bus_config)
42 : spiSlave(bus, cs, bus_config), config(config)
43{
44 oldMag.magneticFieldTimestamp = 0.0f;
45 oldGyr.angularSpeedTimestamp = 0.0f;
46 oldAcc.accelerationTimestamp = 0.0f;
47}
48
50{
51#ifdef DEBUG
52 assert(!isInit && "init() should be called once");
53#endif
54
55 if (!checkChipid())
56 {
57 LOG_ERR(logger, "Got bad CHIPID");
59 return false;
60 }
61
62 softReset();
63
64 if (!setPowerMode())
65 {
66 LOG_ERR(logger, "Not all interfaces are up and running!");
68 return false;
69 }
70
71 initAcc();
72 initGyr();
73 initMag();
74
75 // sleep in order to let the sensors initialize correctly
76 miosix::Thread::sleep(30);
77
78 initFifo();
79 initInt();
80
81 return isInit = true;
82}
83
85{
86#ifdef DEBUG
87 assert(isInit && "init() was not called"); // linter off
88#endif
89
90 // The device will enter in an unusable state when testing.
91 isInit = false;
92
93 if (!testAcc() || !testGyr() || !testMag())
94 {
96 return false;
97 }
98 else
99 {
100 // Finally reinitialize the device into a known state
101 return init();
102 }
103}
104
106{
107 // Prevent interrupts while reading fifo
108 if (irqEnabled)
110}
111
113{
114#ifdef DEBUG
115 assert(isInit && "init() was not called");
116#endif
117 // Reset any errors.
119
120 // Read temperature
121 if (config.temperatureDivider != 0 &&
122 tempCounter % config.temperatureDivider == 0)
123 {
124 readTemp();
125 }
126
127 tempCounter++;
128
129 // Delete old samples
130 lastFifoLevel = 0;
131
132 switch (config.fifoMode)
133 {
135 // Just push one sample
136 readData();
137 break;
138
140 // Read whole FIFO (headerless)
141 readFifo(true);
142 break;
143
145 // Read whole FIFO (header)
146 readFifo(false);
147 break;
148 }
149
151 {
152 // Something went wrong, return dummy data
153 return BMX160Data{};
154 }
155 else
156 {
157 return lastFifo[lastFifoLevel - 1];
158 }
159}
160
168
170
171void BMX160::sendCmd(SPITransaction& spi, BMX160Defs::Cmd cmd,
173{
175 static_cast<uint8_t>(cmd) | static_cast<uint8_t>(pmu));
176}
177
178void BMX160::pushSample(BMX160Data sample)
179{
181}
182
183void BMX160::confMag(SPITransaction& spi, uint8_t value)
184{
185 spi.writeRegister(BMX160Defs::REG_MAG_IF_0, value);
186 miosix::Thread::sleep(10);
187}
188
189void BMX160::mapMag(SPITransaction& spi, uint8_t reg)
190{
191 spi.writeRegister(BMX160Defs::REG_MAG_IF_1, reg);
192 miosix::Thread::sleep(10);
193}
194
195uint8_t BMX160::readMag(SPITransaction& spi, uint8_t reg)
196{
197 mapMag(spi, reg);
198 return spi.readRegister(BMX160Defs::REG_DATA_MAG);
199}
200
201void BMX160::readMag(SPITransaction& spi, uint8_t reg, uint8_t* data,
202 size_t size)
203{
204 while (size != 0)
205 {
206 int burst = 0;
207 if (size >= 8)
208 {
209 confMag(spi,
211 burst = 8;
212 }
213 else if (size >= 6)
214 {
215 confMag(spi,
217 burst = 6;
218 }
219 else if (size >= 2)
220 {
221 confMag(spi,
223 burst = 2;
224 }
225 else
226 {
227 confMag(spi,
229 burst = 1;
230 }
231
232 mapMag(spi, reg);
233 spi.readRegisters(BMX160Defs::REG_DATA_MAG, data, burst);
234
235 reg += burst;
236 data += burst;
237 size -= burst;
238 }
239
240 confMag(spi, BMX160Defs::MAG_IF_0_MANUAL);
241}
242
243void BMX160::writeMag(SPITransaction& spi, uint8_t reg, uint8_t value)
244{
245 spi.writeRegister(BMX160Defs::REG_MAG_IF_3, value);
246 spi.writeRegister(BMX160Defs::REG_MAG_IF_2, reg);
247 miosix::Thread::sleep(10);
248}
249
250bool BMX160::checkChipid()
251{
252 SPITransaction spi(spiSlave);
253 auto chipId = spi.readRegister(BMX160Defs::REG_CHIPID);
254
255#ifdef DEBUG
256 if (chipId != BMX160Defs::CHIPID)
257 LOG_ERR(logger, "CHIPID = {:02x}", chipId);
258#endif
259
260 return chipId == BMX160Defs::CHIPID;
261}
262
263void BMX160::softReset()
264{
265 SPITransaction spi(spiSlave);
266
267 // Reset the state of the device, just to be sure.
268 sendCmd(spi, BMX160Defs::Cmd::SOFTRESET);
269 miosix::Thread::sleep(10);
270
271 // Dummy read of REG_COMM_TEST to enable SPI
272 spi.readRegister(BMX160Defs::REG_COMM_TEST);
273 miosix::Thread::sleep(10);
274}
275
276bool BMX160::setPowerMode()
277{
278 SPITransaction spi(spiSlave);
279
282 miosix::Thread::sleep(80);
283
286 miosix::Thread::sleep(80);
287
290 miosix::Thread::sleep(80);
291
292 // Check if all sensors are up and running
293 return (spi.readRegister(BMX160Defs::REG_PMU_STATUS) &
296}
297
298void BMX160::initAcc()
299{
300 // Calculate accelerometer sensibility
301 switch (config.accelerometerRange)
302 {
304 accSensibility = 1.0f / 16384.0f;
305 break;
307 accSensibility = 1.0f / 8192.0f;
308 break;
310 accSensibility = 1.0f / 4096.0f;
311 break;
313 accSensibility = 1.0f / 2048.0f;
314 break;
315 }
316
317 SPITransaction spi(spiSlave);
318
319 spi.writeRegister(BMX160Defs::REG_ACC_CONF,
320 static_cast<uint8_t>(config.accelerometerDataRate) |
321 static_cast<uint8_t>(config.accelerometerBandwidth));
322 spi.writeRegister(BMX160Defs::REG_ACC_RANGE,
323 static_cast<uint8_t>(config.accelerometerRange));
324}
325
326void BMX160::initGyr()
327{
328 // Calculate gyro sensibility
329 switch (config.gyroscopeRange)
330 {
332 gyrSensibility = 1.0f / 16.4f;
333 break;
335 gyrSensibility = 1.0f / 32.8f;
336 break;
338 gyrSensibility = 1.0f / 65.6f;
339 break;
341 gyrSensibility = 1.0f / 131.2f;
342 break;
344 gyrSensibility = 1.0f / 262.4f;
345 break;
346 }
347
348 SPITransaction spi(spiSlave);
349 spi.writeRegister(BMX160Defs::REG_GYR_CONF,
350 static_cast<uint8_t>(config.gyroscopeDataRate) |
351 static_cast<uint8_t>(config.gyroscopeBandwidth));
352 spi.writeRegister(BMX160Defs::REG_GYR_RANGE,
353 static_cast<uint8_t>(config.gyroscopeRange));
354}
355
356void BMX160::initMag()
357{
358 /*
359 Little explanation of this:
360 The magnetometer is not controlled directly,
361 instead we have a secondary controller, BMM150,
362 with its own register accessed with REG_MAG_IF_[1-3]
363 */
364
365 SPITransaction spi(spiSlave);
366
367 // Enable manual configuration mode
368 confMag(spi, BMX160Defs::MAG_IF_0_MANUAL);
369
370 // Put MAG into sleep mode (from suspend mode)
371 writeMag(spi, BMX160Defs::MAG_REG_RESET,
373
376
377 if (config.enableCompensation)
378 boschReadTrim(spi);
379
380 // Magic sequence to init it
382 mapMag(spi, BMX160Defs::MAG_REG_DATA);
383
384 // Set mag output data rate
385 spi.writeRegister(BMX160Defs::REG_MAG_CONF,
386 static_cast<uint8_t>(config.magnetometerRate));
387 miosix::Thread::sleep(10);
388
389 // Disable manual configuration mode
390 confMag(spi, 0);
391}
392
393void BMX160::initFifo()
394{
396 {
397 SPITransaction spi(spiSlave);
398
399 uint8_t configByte = BMX160Defs::FIFO_CONFIG_1_ACC_EN |
402
405
406 spi.writeRegister(BMX160Defs::REG_FIFO_CONFIG_1, configByte);
407
408 configByte = (config.fifoGyroscopeDownsampling & 3) |
409 ((config.fifoAccelerationDownsampling & 3) << 4);
410
411 if (config.fifoAccelerometerFiltered)
413
414 if (config.fifoGyroscopeFiltered)
416
417 spi.writeRegister(BMX160Defs::REG_FIFO_DOWNS, configByte);
418
419 sendCmd(spi, BMX160Defs::Cmd::FIFO_FLUSH);
420 }
421}
422
423void BMX160::initInt()
424{
426 {
427 SPITransaction spi(spiSlave);
428
429 // Select mode between PUSH_PULL or OPEN_DRAIN
430 uint8_t outCtrl = 0;
433
438
439 // Enable both interrupt pins, otherwise they'll just float.
440 // We configure both of them as push-pull and active-low
441 spi.writeRegister(BMX160Defs::REG_INT_OUT_CTRL, outCtrl);
442
443 // Set fifo watermark
444 spi.writeRegister(BMX160Defs::REG_FIFO_CONFIG_0, config.fifoWatermark);
445
446 // Enable FIFO full interrupt and fifo watermark
447 spi.writeRegister(BMX160Defs::REG_INT_EN_1,
450
451 // Enable interrupt pin map
453 {
454 // Configure to use INT1
455 spi.writeRegister(BMX160Defs::REG_INT_MAP_1,
458 }
459 else
460 {
461 // Configure to use INT2
462 spi.writeRegister(BMX160Defs::REG_INT_MAP_1,
465 }
466 }
467}
468
469bool BMX160::testAcc()
470{
471 const uint16_t SELF_TEST_LIMIT = 8192;
472 const uint8_t ACC_CONF_TEST = 0x2C;
473 const uint8_t ACC_RANGE_TEST = 0x08;
474
475 SPITransaction spi(spiSlave);
476
477 // The acc will complain otherwise...
478 spi.writeRegister(BMX160Defs::REG_ACC_CONF, ACC_CONF_TEST);
479 spi.writeRegister(BMX160Defs::REG_ACC_RANGE, ACC_RANGE_TEST);
480
481 // Enable acc self-test + positive force + self-test deflection
482 spi.writeRegister(BMX160Defs::REG_SELF_TEST,
486 miosix::Thread::sleep(50);
487
488 int16_t posAcc[3];
489 spi.readRegisters(BMX160Defs::REG_DATA_ACC,
490 reinterpret_cast<uint8_t*>(posAcc), sizeof(posAcc));
491
492 // Enable acc self-test + negative force + self-test deflection
493 spi.writeRegister(
496 miosix::Thread::sleep(50);
497
498 int16_t negAcc[3];
499 spi.readRegisters(BMX160Defs::REG_DATA_ACC,
500 reinterpret_cast<uint8_t*>(negAcc), sizeof(negAcc));
501
502 if ((negAcc[0] - posAcc[0]) < SELF_TEST_LIMIT ||
503 (negAcc[1] - posAcc[1]) < SELF_TEST_LIMIT ||
504 (negAcc[2] - posAcc[2]) < SELF_TEST_LIMIT)
505 {
506 LOG_ERR(logger, "Accelerometer self-test failed!");
507 LOG_ERR(logger, "posAcc: {} {} {}", posAcc[0], posAcc[1], posAcc[2]);
508 LOG_ERR(logger, "negAcc: {} {} {}", negAcc[0], negAcc[1], negAcc[2]);
509
510 return false;
511 }
512
513 // Reset self-test
514 spi.writeRegister(BMX160Defs::REG_SELF_TEST, 0);
515 return true;
516}
517
518bool BMX160::testGyr()
519{
520 // Start gyro self-test
521 SPITransaction spi(spiSlave);
522
524
525 miosix::Thread::sleep(50);
526
527 // Read back the results
528 if (!(spi.readRegister(BMX160Defs::REG_STATUS) & 2))
529 {
530 LOG_ERR(logger, "Gyroscope self-test failed!");
531 return false;
532 }
533 else
534 {
535 return true;
536 }
537}
538
539bool BMX160::testMag()
540{
541 SPITransaction spi(spiSlave);
542
543 // Enable manual configuration mode
544 confMag(spi, BMX160Defs::MAG_IF_0_MANUAL);
545
546 // Enable self-test and put magnetometer in sleep
547 writeMag(spi, BMX160Defs::MAG_REG_CONTROL,
549 miosix::Thread::sleep(200);
550
551 // Check if it has finished
552 if (readMag(spi, BMX160Defs::MAG_REG_CONTROL) &
554 {
555 LOG_ERR(logger, "Magnetometer didn't finish self-test!");
556 return false;
557 }
558
559 // Read back test results
560 int16_t mag[4];
561 readMag(spi, BMX160Defs::MAG_REG_DATA, reinterpret_cast<uint8_t*>(mag),
562 sizeof(mag));
563
564 // Test results are stored in the lower bit of the 3 axis
565 if (!(mag[0] & 1) || !(mag[1] & 1) || !(mag[2] & 1))
566 {
567 LOG_ERR(logger, "Magnetometer self-test failed!");
568 LOG_ERR(logger, "result: %d %d %d %d\n", mag[0], mag[1], mag[2],
569 mag[3]);
570 return false;
571 }
572 else
573 {
574 return true;
575 }
576}
577
578MagnetometerData BMX160::buildMagData(BMX160Defs::MagRaw data,
579 uint64_t timestamp)
580{
581 // Strip the lower 3 bits for xy
582 data.x >>= 3;
583 data.y >>= 3;
584 // Strip the lower 1 bit for z
585 data.z >>= 1;
586
587 if (config.enableCompensation)
588 {
589 return MagnetometerData{timestamp,
590 boschMagCompensateX(data.x, data.rhall),
591 boschMagCompensateY(data.y, data.rhall),
592 boschMagCompensateZ(data.z, data.rhall)};
593 }
594 else
595 {
596 return MagnetometerData{timestamp, data.x * BMX160Defs::MAG_SENSIBILITY,
599 }
600}
601
602AccelerometerData BMX160::buildAccData(BMX160Defs::AccRaw data,
603 uint64_t timestamp)
604{
605 using namespace Constants;
606
607 return AccelerometerData{timestamp, data.x * accSensibility * g,
608 data.y * accSensibility * g,
609 data.z * accSensibility * g};
610}
611
612GyroscopeData BMX160::buildGyrData(BMX160Defs::GyrRaw data, uint64_t timestamp)
613{
614 using namespace Constants;
615
617 {
618 return GyroscopeData{timestamp, data.x * gyrSensibility,
619 data.y * gyrSensibility, data.z * gyrSensibility};
620 }
621 else
622 {
623 return GyroscopeData{timestamp,
624 data.x * gyrSensibility * DEGREES_TO_RADIANS,
625 data.y * gyrSensibility * DEGREES_TO_RADIANS,
626 data.z * gyrSensibility * DEGREES_TO_RADIANS};
627 }
628}
629
630const char* BMX160::debugErr(SPITransaction& spi)
631{
632 uint8_t err = spi.readRegister(BMX160Defs::REG_ERR);
633
634 if (err & 1)
635 {
636 return "Chip not operable";
637 }
638 else if (err & 64)
639 {
640 return "Dropped command to register 0x7E";
641 }
642 else
643 {
644 // Mask error code
645 err = (err >> 1) & 0x0F;
646 switch (err)
647 {
648 case 0:
649 return "No error";
650 case 1:
651 case 2:
652 return "Generic error";
653 case 3:
654 return "LPM and interrupt uses pre-filtered data";
655 case 6:
656 return "ODR do not match";
657 case 7:
658 return "LPM uses pre-filtered data";
659 default:
660 return "Reserved error";
661 }
662 }
663}
664
665uint64_t BMX160::odrToTimeOffset(BMX160Config::OutputDataRate odr,
666 uint8_t downs)
667{
668 // Adjust ODR for downsampling
669 uint8_t realOdr = static_cast<uint64_t>(odr) - (downs & 3);
670
671 // Hz = 100 / 2^(8-odr)
672 // Sec = 2^(13-odr) / 3200
673 // Micro = (2^(13-odr)) * 10000 / 32;
674
675 return ((1 << (13 - realOdr)) * 10000) >> 5;
676}
677
678void BMX160::readTemp()
679{
680 SPITransaction spi(spiSlave);
681
682 int16_t val = spi.readRegister(BMX160Defs::REG_TEMPERATURE_0) |
683 (spi.readRegister(BMX160Defs::REG_TEMPERATURE_1) << 8);
684
685 // Correct for sensibility and offset
686 temperature = (val * BMX160Defs::TEMP_SENSIBILITY) + 23.0f;
687}
688
689void BMX160::readData()
690{
691 SPITransaction spi(spiSlave);
692
693 uint8_t buf[20];
694 spi.readRegisters(BMX160Defs::REG_DATA, buf, sizeof(buf));
695
696 int idx = 0;
697 auto magRaw = parseStruct<BMX160Defs::MagRaw>(buf, idx);
698 auto gyrRaw = parseStruct<BMX160Defs::GyrRaw>(buf, idx);
699 auto accRaw = parseStruct<BMX160Defs::AccRaw>(buf, idx);
700
701 auto timestamp = TimestampTimer::getTimestamp();
702 // Push a new sample into the fifo
703 pushSample(BMX160Data{
704 buildAccData(accRaw, timestamp),
705 buildGyrData(gyrRaw, timestamp),
706 buildMagData(magRaw, timestamp),
707 });
708}
709
710void BMX160::readFifo(bool headerless)
711{
712 irqEnabled = false;
713
714 SPITransaction spi(spiSlave);
715
716 int len = spi.readRegister(BMX160Defs::REG_FIFO_LENGTH_0) |
717 ((spi.readRegister(BMX160Defs::REG_FIFO_LENGTH_1) & 7) << 8);
718
719 if (len == 0)
720 {
721 // The buffer is empty, return early
723 return;
724 }
725
726 uint8_t buf[FIFO_BUF_SIZE];
727
728#ifdef DEBUG
729 assert(len <= static_cast<int>(sizeof(buf)) && "Buffer overflow!");
730#endif
731
732 // Shift the old timestamps, this allows to use old timestamps with the
733 // current frame.
734 if (oldMag.magneticFieldTimestamp != 0)
736 if (oldGyr.angularSpeedTimestamp != 0)
738 if (oldAcc.accelerationTimestamp != 0)
740
741 // Calculate time offset
742 uint64_t timeOffset = std::min({
743 odrToTimeOffset(config.magnetometerRate, 0),
744 odrToTimeOffset(config.gyroscopeDataRate,
746 odrToTimeOffset(config.accelerometerDataRate,
748 });
749
750 spi.readRegisters(BMX160Defs::REG_FIFO_DATA, buf, len);
751 uint64_t timestamp = 0;
752 uint64_t watermarkTimestamp = 0;
753 int idx = 0;
754 miosix::Lock<miosix::FastMutex> l(mutex);
755
756 while (idx < len && buf[idx] != BMX160Defs::FIFO_STOP_BYTE)
757 {
758 if (headerless)
759 {
760 auto magRaw = parseStruct<BMX160Defs::MagRaw>(buf, idx);
761 auto gyrRaw = parseStruct<BMX160Defs::GyrRaw>(buf, idx);
762 auto accRaw = parseStruct<BMX160Defs::AccRaw>(buf, idx);
763
764 oldMag = buildMagData(magRaw, timestamp);
765 oldGyr = buildGyrData(gyrRaw, timestamp);
766 oldAcc = buildAccData(accRaw, timestamp);
767
768 // Push a new sample into the fifo
769 pushSample(BMX160Data{oldAcc, oldGyr, oldMag});
770
771 if (watermarkTimestamp == 0 && idx >= (config.fifoWatermark * 4))
772 watermarkTimestamp = timestamp;
773
774 timestamp += timeOffset;
775 }
776 else
777 {
778 uint8_t header = buf[idx++];
779
780 if ((header & BMX160Defs::FIFO_HEADER_MODE_MASK) ==
782 {
783 // This is a regular packet
784
785 // Mask out everything but fh_parm
787
788 // This contains magnet data
790 {
791 auto magRaw = parseStruct<BMX160Defs::MagRaw>(buf, idx);
792 oldMag = buildMagData(magRaw, timestamp);
793 }
794
795 // This contains gyro data
797 {
798 auto gyrRaw = parseStruct<BMX160Defs::GyrRaw>(buf, idx);
799 oldGyr = buildGyrData(gyrRaw, timestamp);
800 }
801
802 // This contains accel data
804 {
805 auto accRaw = parseStruct<BMX160Defs::AccRaw>(buf, idx);
806 oldAcc = buildAccData(accRaw, timestamp);
807 }
808
809 // Push a new sample into the fifo
810 pushSample(BMX160Data{oldAcc, oldGyr, oldMag});
811
812 if (watermarkTimestamp == 0 &&
813 idx >= (config.fifoWatermark * 4))
814 {
815 watermarkTimestamp = timestamp;
816 }
817
818 timestamp += timeOffset;
819 }
820 else if ((header & BMX160Defs::FIFO_HEADER_MODE_MASK) ==
822 {
823 // This is a control packet
824
825 // Mask out everything but fh_parm
827
829 {
830 // Skip frame
831 idx += 1;
832 }
834 {
835 // Sensortime frame
836 idx += 3;
837 }
838 else if (header == BMX160Defs::FIFO_HEADER_PARM_CONFIG)
839 {
840 // FIFO_input_config_frame
841 idx += 1;
842 }
843 }
844 else
845 {
846 LOG_ERR(logger, "Malformed packet! Aborting fifo transfer...");
847
848 lastError =
850 break;
851 }
852 }
853 }
854
855 // Update fifo statistics
857 stats.watermarkTimestamp = watermarkTimestamp;
858 stats.fifoDuration = timestamp;
860 stats.len = len;
861
862 // Adjust timestamps
863 for (int i = 0; i < lastFifoLevel; i++)
864 {
865 lastFifo[i].accelerationTimestamp +=
866 lastInterruptTimestamp - watermarkTimestamp;
867 lastFifo[i].angularSpeedTimestamp +=
868 lastInterruptTimestamp - watermarkTimestamp;
869 lastFifo[i].magneticFieldTimestamp +=
870 lastInterruptTimestamp - watermarkTimestamp;
871 }
872
873 irqEnabled = true;
874}
875
876template <typename T>
877T BMX160::parseStruct(uint8_t* buf, int& idx)
878{
879 T data;
880 memcpy(&data, buf + idx, sizeof(T));
881 idx += sizeof(T);
882
883 return data;
884}
885
893void BMX160::boschReadTrim(SPITransaction& spi)
894{
895 uint8_t trimX1y1[2] = {0};
896 uint8_t trimXyzData[4] = {0};
897 uint8_t trimXy1xy2[10] = {0};
898
899 readMag(spi, BMX160Defs::MAG_REG_DIG_X1, trimX1y1, sizeof(trimX1y1));
900 readMag(spi, BMX160Defs::MAG_REG_DIG_Z4_0, trimXyzData,
901 sizeof(trimXyzData));
902 readMag(spi, BMX160Defs::MAG_REG_DIG_Z2_0, trimXy1xy2, sizeof(trimXy1xy2));
903
904 // Read trim registers
905 trimData.digX1 = trimX1y1[0];
906 trimData.digY1 = trimX1y1[1];
907 trimData.digX2 = trimXyzData[2];
908 trimData.digY2 = trimXyzData[3];
909 trimData.digZ1 = trimXy1xy2[2] | (trimXy1xy2[3] << 8);
910 trimData.digZ2 = trimXy1xy2[0] | (trimXy1xy2[1] << 8);
911 trimData.digZ3 = trimXy1xy2[6] | (trimXy1xy2[7] << 8);
912 trimData.digZ4 = trimXyzData[0] | (trimXyzData[1] << 8);
913 trimData.digXY1 = trimXy1xy2[9];
914 trimData.digXY2 = trimXy1xy2[8];
915 trimData.digXYZ1 = trimXy1xy2[4] | ((trimXy1xy2[5] & 0x7F) << 8);
916}
917
918float BMX160::boschMagCompensateX(int16_t x, uint16_t rhall)
919{
920 /* clang-format off */
921 float retval = 0;
922 float processCompX0;
923 float processCompX1;
924 float processCompX2;
925 float processCompX3;
926 float processCompX4;
927
928 /* Processing compensation equations */
929 processCompX0 = (((float)trimData.digXYZ1) * 16384.0f / rhall);
930 retval = (processCompX0 - 16384.0f);
931 processCompX1 = ((float)trimData.digXY2) * (retval * retval / 268435456.0f);
932 processCompX2 = processCompX1 + retval * ((float)trimData.digXY1) / 16384.0f;
933 processCompX3 = ((float)trimData.digX2) + 160.0f;
934 processCompX4 = x * ((processCompX2 + 256.0f) * processCompX3);
935 retval = ((processCompX4 / 8192.0f) + (((float)trimData.digX1) * 8.0f)) / 16.0f;
936
937 return retval;
938 /* clang-format on */
939}
940
941float BMX160::boschMagCompensateY(int16_t y, uint16_t rhall)
942{
943 /* clang-format off */
944 float retval = 0;
945 float processCompY0;
946 float processCompY1;
947 float processCompY2;
948 float processCompY3;
949 float processCompY4;
950
951 /* Processing compensation equations */
952 processCompY0 = ((float)trimData.digXYZ1) * 16384.0f / rhall;
953 retval = processCompY0 - 16384.0f;
954 processCompY1 = ((float)trimData.digXY2) * (retval * retval / 268435456.0f);
955 processCompY2 = processCompY1 + retval * ((float)trimData.digXY1) / 16384.0f;
956 processCompY3 = ((float)trimData.digY2) + 160.0f;
957 processCompY4 = y * (((processCompY2) + 256.0f) * processCompY3);
958 retval = ((processCompY4 / 8192.0f) + (((float)trimData.digY1) * 8.0f)) / 16.0f;
959
960 return retval;
961 /* clang-format on */
962}
963
964float BMX160::boschMagCompensateZ(int16_t z, uint16_t rhall)
965{
966 /* clang-format off */
967 float retval = 0;
968 float processCompZ0;
969 float processCompZ1;
970 float processCompZ2;
971 float processCompZ3;
972 float processCompZ4;
973 float processCompZ5;
974
975 processCompZ0 = ((float)z) - ((float)trimData.digZ4);
976 processCompZ1 = ((float)rhall) - ((float)trimData.digXYZ1);
977 processCompZ2 = (((float)trimData.digZ3) * processCompZ1);
978 processCompZ3 = ((float)trimData.digZ1) * ((float)rhall) / 32768.0f;
979 processCompZ4 = ((float)trimData.digZ2) + processCompZ3;
980 processCompZ5 = (processCompZ0 * 131072.0f) - processCompZ2;
981 retval = (processCompZ5 / ((processCompZ4)*4.0f)) / 16.0f;
982
983 return retval;
984 /* clang-format on */
985}
986
987} // namespace Boardcore
#define LOG_ERR(logger,...)
SensorErrors lastError
Definition Sensor.h:54
BMX160 Driver.
Definition BMX160.h:45
BMX160Temperature getTemperature()
Get last read temperature.
Definition BMX160.cpp:161
BMX160Data sampleImpl() override
Gather data from FIFO/data registers and temperature sensor.
Definition BMX160.cpp:112
bool init() override
Initialize the driver.
Definition BMX160.cpp:49
BMX160FifoStats getFifoStats()
Retrieve last fifo stats.
Definition BMX160.cpp:169
BMX160(SPIBusInterface &bus, miosix::GpioPin cs, BMX160Config config={})
BMX160 Constructor.
Definition BMX160.cpp:31
bool selfTest() override
Perform selftest on the device.
Definition BMX160.cpp:84
void IRQupdateTimestamp(uint64_t ts) override
Sometimes the sensor pulls down the interrupt pin while reading data. We override this method and upd...
Definition BMX160.cpp:105
Interface for low level access of a SPI bus as a master.
Provides high-level access to the SPI Bus for a single transaction.
void writeRegister(uint8_t reg, uint8_t data)
Writes an 8 bit register.
std::array< BMX160Data, FifoSize > lastFifo
Definition Sensor.h:153
virtual void IRQupdateTimestamp(uint64_t ts)
Called by the interrupt handling routine: provides the timestamp of the last interrupt (if FIFO is di...
Definition Sensor.h:189
void sample() override
Sample the sensor.
Definition Sensor.h:116
miosix::FastMutex mutex
Definition Sensor.h:107
@ MAG_IF_0_BURST_8
8 byte of burst operation.
Definition BMX160Defs.h:132
@ MAG_IF_0_BURST_2
2 byte of burst operation.
Definition BMX160Defs.h:130
@ MAG_IF_0_BURST_1
1 byte of burst operation.
Definition BMX160Defs.h:129
@ MAG_IF_0_BURST_6
6 byte of burst operation.
Definition BMX160Defs.h:131
const uint8_t PMU_STATUS_ALL_MASK
Mask for PMU_STATUS register (Power Mode Unit).
Definition BMX160Defs.h:138
const float TEMP_SENSIBILITY
Temperature sensor sensibility.
Definition BMX160Defs.h:44
const uint8_t CHIPID
BMX160 Chip Id.
Definition BMX160Defs.h:54
const uint8_t FIFO_HEADER_MODE_MASK
Mask for fifo header mode.
Definition BMX160Defs.h:153
const uint8_t PMU_STATUS_ALL_NORMAL
Mask for PMU_STATUS register, normal status for all sensors.
Definition BMX160Defs.h:143
const uint8_t FIFO_STOP_BYTE
This value indicates that the data in the FIFO stops prematurely.
Definition BMX160Defs.h:148
@ MAG_IF_SET_PMU_MODE
Sets the PMU mode for the magnetometer.
@ FIFO_FLUSH
Clears all data in the fifo.
@ ACC_SET_PMU_MODE
Sets the PMU mode for the accelerometer.
@ GYR_SET_PMU_MODE
Sets the PMU mode for the gyroscope.
@ SOFTRESET
Triggers a reset including a reboot.
@ SELF_TEST_ACC_AMP
Select amplitude of the selftest deflection.
Definition BMX160Defs.h:67
@ SELF_TEST_GYR
Starts selftest of the gyroscope.
Definition BMX160Defs.h:66
@ SELF_TEST_ACC_ENABLE
Starts selftest of the accelerometer.
Definition BMX160Defs.h:69
@ SELF_TEST_ACC_SIGN
Select sign of selftest exitation.
Definition BMX160Defs.h:68
@ FIFO_CONFIG_1_ACC_EN
Store accelerometer data in fifo.
Definition BMX160Defs.h:77
@ FIFO_CONFIG_1_GYR_EN
Store gyroscope data in fifo.
Definition BMX160Defs.h:78
@ FIFO_CONFIG_1_HEADER_EN
Stores an header for each frame.
Definition BMX160Defs.h:80
@ FIFO_CONFIG_1_MAG_EN
Store magnetometer data in fifo.
Definition BMX160Defs.h:79
const uint8_t FIFO_HEADER_PARM_MASK
Mask for fifo header parm.
Definition BMX160Defs.h:158
@ INT_EN_1_FIFO_FULL
Enables interrupt for FIFO full.
Definition BMX160Defs.h:120
@ INT_EN_1_FIFO_WATERMARK
Enables interrupt for FIFO watermark.
Definition BMX160Defs.h:119
const float MAG_SENSIBILITY
Magnetometer fixed sensibility.
Definition BMX160Defs.h:49
@ INT_OUT_CTRL_INT2_OD
Open drain enable for INT2 pin.
Definition BMX160Defs.h:98
@ INT_OUT_CTRL_INT1_OD
Open drain enable for INT1 pin.
Definition BMX160Defs.h:100
@ INT_OUT_CTRL_INT1_OUT_EN
Output enable for INT1 pin.
Definition BMX160Defs.h:99
@ INT_OUT_CTRL_INT2_OUT_EN
Output enable for INT2 pin.
Definition BMX160Defs.h:97
PrintLogger l
Definition CanDriver.cpp:41
uint64_t getTimestamp()
Returns the current timer value in microseconds.
Driver for the VN100S IMU.
SensorErrors
Generic error codes that a sensor can generate.
Definition SensorData.h:39
@ SELF_TEST_FAIL
Definition SensorData.h:45
@ INVALID_WHOAMI
Definition SensorData.h:41
BMX160 Configuration.
uint8_t fifoWatermark
Fifo watermark to use, in multiples of 4.
uint8_t fifoGyroscopeDownsampling
Fifo gyroscope downsampling (between 0 and 15).
bool fifoGyroscopeFiltered
Should the fifo use gyroscope filtered data?
BandwidthParameter accelerometerBandwidth
bool fifoAccelerometerFiltered
Should the fifo use accelerometer filtered data?
AccelerometerRange accelerometerRange
uint8_t fifoAccelerationDownsampling
Fifo accelerometer downsampling (between 0 and 15).
OutputDataRate magnetometerRate
int temperatureDivider
Divide the temperature sampling rate.
@ DEG_2000
Gyroscope range 2000°/sec.
@ DEG_500
Gyroscope range 500°/sec.
@ DEG_125
Gyroscope range 125°/sec.
@ DEG_250
Gyroscope range 250°/sec.
@ DEG_1000
Gyroscope range 1000°/sec.
@ OPEN_DRAIN
Open drain behaviour.
OutputDataRate accelerometerDataRate
BandwidthParameter gyroscopeBandwidth
bool enableCompensation
Enable magnetometer data compensation.
@ DISABLED
The interrupts are completely disabled.
@ PIN_INT1
Interrupts are enabled on pin 2.
GyroscopeRange gyroscopeRange
uint8_t magnetometerRepetitionsZ
Repetitions for the Z axis.
FifoInterruptPin fifoInterrupt
GyroscopeMeasureUnit gyroscopeUnit
OutputDataRate gyroscopeDataRate
uint8_t magnetometerRepetitionsXY
Repetitions for the XY axis.
OutputDataRate
Output Data Rate expressed in Hz.
@ DISABLED
The fifo is completely disabled.
@ HEADER
Sensors can have different odr.
@ HEADERLESS
Sensors MUST have the same odr.
BMX160 fifo statistics.
Definition BMX160Data.h:66
uint64_t fifoDuration
Total fifo duration.
Definition BMX160Data.h:71
int len
Fifo length in bytes.
Definition BMX160Data.h:75
uint64_t watermarkTimestamp
Watermark timestamp (from the start of.
Definition BMX160Data.h:69
SPI Bus configuration for a specific slave.
SPI::ClockDivider clockDivider
< Peripheral clock division