DevLab_ICM20948 1.0.2
ICM-20948 9-Axis Motion Sensor Driver
Loading...
Searching...
No Matches
DevLab_ICM20948.cpp
Go to the documentation of this file.
1#include "DevLab_ICM20948.h"
2
3/** - Construct with default I2C address; call begin() to attach bus */
5
6bool DevLab_ICM20948::beginI2C(uint8_t address, TwoWire &i2cPort, uint32_t i2cSpeed)
7{
8 // Free previous BusIO instance
9 if (bus)
10 {
11 delete bus;
12 bus = nullptr;
13 }
14
15 // Assign I2C interface implementation
16 iface = &i2c;
17
18 // Initialize I2C (400kHz) and probe device
19 if (!i2c.beginI2C(address, i2cPort, i2cSpeed))
20 return false;
21
22 // Create BusIO abstraction layer
23 bus = new BusIO_7Semi<Interface_7Semi>(*iface);
24 if (!bus)
25 return false;
26
27 // Allow device to stabilize after power-up
28 delay(100);
29
30 // Read WHO_AM_I register
31 uint8_t who_am_i;
32 if (!readWhoAmI(who_am_i))
33 return false;
34
35 // Validate device ID (expected 0xEA)
36 if (who_am_i != WHO_AM_I_VAL)
37 return false;
38
39 if (!selectBank(2))
40 return false;
41
42 if (!bus->write(ODR_ALIGN_EN, (uint8_t)0x01))
43 return false;
44
45 if (!selectBank(0)) return false;
46
47 /*In case of error substitute the following code with the previous one
48 if (!bus->writeBit(USER_CTRL, 4, (uint8_t)0x00))
49 return false;
50 */
51 // Enable I2C interface (I2C_IF_DIS = 0)
52 if (!bus->write(USER_CTRL, (uint8_t)0x00))
53 return false;
54
55 // Set clock source to PLL (auto select best clock)
56 if (!bus->write(PWR_MGMT_1, (uint8_t)0x01))
57 return false;
58
60 return false;
61
62 // Initialization successful
63 return true;
64}
65
66bool DevLab_ICM20948::beginSPI(uint8_t csPin, SPIClass &spiPort, uint32_t spiSpeed)
67{
68// Free previous BusIO instance
69 if (bus)
70 {
71 delete bus;
72 bus = nullptr;
73 }
74
75 // Assign SPI interface implementation
76 iface = &spi;
77
78 // Initialize SPI (1MHz) and probe device
79 if (!spi.beginSPI(csPin, spiPort, spiSpeed))
80 return false;
81
82 // Create BusIO abstraction layer
83 bus = new BusIO_7Semi<Interface_7Semi>(*iface);
84 if (!bus)
85 return false;
86
87 softReset();
88
89 // Allow device to stabilize after power-up
90 delay(100);
91
92
93 // Read WHO_AM_I register
94 uint8_t who_am_i;
95
96 if (!readWhoAmI(who_am_i))
97 return false;
98
99 // Validate device ID (expected 0xEA)
100 if (who_am_i != WHO_AM_I_VAL)
101 return false;
102
103 if (!selectBank(2))
104 return false;
105
106 if (!bus->write(ODR_ALIGN_EN, (uint8_t)0x01))
107 return false;
108
109 if (!selectBank(0))
110 return false;
111 // Enable I2C interface (I2C_IF_DIS = 0)
112
113if (!bus->writeBit(USER_CTRL, 4, (uint8_t)1)) return false;
114
115 Serial.println("I2C interface enabled");
116 // Wake device from sleep mode (SLEEP = 0)
117 if (!bus->write(PWR_MGMT_1,(uint8_t)0x01))
118 return false;
119
120 if(!applyBasicDefaults())
121 return false;
122
123 // Initialization successful
124 return true;
125}
126
127bool DevLab_ICM20948::readWhoAmI(uint8_t &whoAmI)
128{
129 // Select USER BANK 0
130 if (!selectBank(0))
131 return false;
132
133 // Read WHO_AM_I register
134 if (!bus->read(WHO_AM_I, whoAmI))
135 return false;
136
137 // Success
138 return true;
139}
140
142{
143 if (bank > 3)
144 return false;
145
146 // Mask bank value to valid range (0–3) and shift to bits [5:4]
147 uint8_t v = (bank & 0x03) << 4;
148
149 // Write bank selection to REG_BANK_SEL register
150 return bus->write(REG_BANK_SEL, v);
151}
152
154{
155 // Select USER BANK 0
156 if (!selectBank(0))
157 return false;
158
159 // Set DEVICE_RESET bit (bit 7)
160 if (!bus->write(PWR_MGMT_1, (uint8_t)0x80))
161 return false;
162
163 // Allow device to reset
164 delay(100);
165
166 selectBank(2);
167 bus->write(ODR_ALIGN_EN, (uint8_t)0x01);
168
169 // Success
170 return true;
171}
172
174{
175 // Select USER BANK 0
176 if (!selectBank(0))
177 return false;
178 uint8_t v = 1;
179 if(en)
180 v|= 1<<6;
181
182 // Set or clear SLEEP bit (bit 6)
183 if (!bus->write(PWR_MGMT_1, v))
184 return false;
185
186 // Success
187 return true;
188}
189
191{
192 // Select USER BANK 0
193 if (!selectBank(0))
194 return false;
195
196 // Wake device and set clock source (CLKSEL=1, SLEEP=0)
197 if (!bus->write(PWR_MGMT_1, (uint8_t)0x01))
198 return false;
199
200 if (!bus->write(PWR_MGMT_2, (uint8_t)0x00))
201 return false;
202
203
204 // Allow device to stabilize
205 delay(10);
206
207 // Disable duty-cycling (LP mode off)
208 if (!bus->write(LP_CONFIG, (uint8_t)0x00))
209 return false;
210
211 // Select USER BANK 2
212 if (!selectBank(2))
213 return false;
214
215 // Configure gyro (DLPF enabled, FS = 2000 dps)
216 if (!bus->write(GYRO_CONFIG_1, (uint8_t)0x1F))
217 return false;
218
219 // Set gyro sample rate divider (SRD = 0 → max rate)
220 if (!bus->write(GYRO_SMPLRT_DIV, (uint8_t)0x00))
221 return false;
222
223 // Configure accel (DLPF enabled, FS = 2g)
224 if (!bus->write(ACCEL_CONFIG, (uint8_t)0x19)) //Change to 0x1F for 16g
225 return false;
226
227 // Set accel sample rate divider high byte
228 if (!bus->write(ACCEL_SMPLRT_DIV_1, (uint8_t)0x00))
229 return false;
230
231 // Set accel sample rate divider low byte
232 if (!bus->write(ACCEL_SMPLRT_DIV_2, (uint8_t)0x00))
233 return false;
234
235 // Select USER BANK 0
236 if (!selectBank(0))
237 return false;
238
239 // Configure interrupt pin (open-drain, active-low, latch)
240 if (!bus->write(INT_PIN_CFG, (uint8_t)0x30))
241 return false;
242
243 // Configuration successful
244 return true;
245}
246
247bool DevLab_ICM20948::readAccel(float &x, float &y, float &z)
248{
249 // Select USER BANK 0
250 if (!selectBank(0))
251 return false;
252
253 // Read 6 bytes (X, Y, Z)
254 uint8_t raw[6];
255 if (!bus->read(ACCEL_XOUT_H, raw, 6))
256 return false;
257
258 // Convert raw data to g
259 x = (int16_t)((raw[0] << 8) | raw[1]) / mg_per_lsb;
260 y = (int16_t)((raw[2] << 8) | raw[3]) / mg_per_lsb;
261 z = (int16_t)((raw[4] << 8) | raw[5]) / mg_per_lsb;
262
263 // Success
264 return true;
265}
266
267bool DevLab_ICM20948::readGyro(float &x, float &y, float &z)
268{
269 // Select USER BANK 0
270 if (!selectBank(0))
271 return false;
272
273 // Read 6 bytes (X, Y, Z)
274 uint8_t raw[6];
275 if (!bus->read(GYRO_XOUT_H, raw, 6))
276 return false;
277
278 // Convert raw data to dps
279 x = (int16_t)((raw[0] << 8) | raw[1]) / degree_per_second;
280 y = (int16_t)((raw[2] << 8) | raw[3]) / degree_per_second;
281 z = (int16_t)((raw[4] << 8) | raw[5]) / degree_per_second;
282
283 // Success
284 return true;
285}
286
287bool DevLab_ICM20948::readTemperature(float &temperature)
288{
289 // Select USER BANK 0
290 if (!selectBank(0))
291 return false;
292
293 // Initialize accumulator
294 temperature = 0;
295
296 // Read and average 5 samples
297 for (int i = 0; i < 5; i++)
298 {
299 // Read temperature registers
300 uint8_t raw[2];
301 if (!bus->read(TEMP_OUT_H, raw, 2))
302 return false;
303
304 // Convert raw to signed value
305 int16_t temp = (int16_t)((raw[0] << 8) | raw[1]);
306
307 // Convert to °C and accumulate
308 temperature += temp / 333.87f + 21.0f;
309 }
310
311 // Compute average temperature
312 temperature /= 5.0f;
313
314 // Success
315 return true;
316}
317
318
319bool DevLab_ICM20948::readMag(float &x, float &y, float &z) {
320 if (!selectBank(0)) return false;
321
322 uint8_t buf[9];
323
324 if (!bus->read(EXT_SLV_SENS_DATA_00, buf, 9))
325 return false;
326
327
328 if (buf[8] & 0x08) return false;
329
330 int16_t mx = (int16_t)(buf[1] | (buf[2] << 8));
331 int16_t my = (int16_t)(buf[3] | (buf[4] << 8));
332 int16_t mz = (int16_t)(buf[5] | (buf[6] << 8));
333
334 x = mx * 0.15f;
335 y = my * 0.15f;
336 z = mz * 0.15f;
337
338 return true;
339}
340
342{
343 if (!bus)
344 return false;
345
346 softReset();
347
348 sleep(false);
349
350 if (!selectBank(0))
351 return false;
352 // Enable I2C master mode and disable I2C slave mode
353 if (!bus->write(USER_CTRL, (uint8_t)USER_CTRL_I2C_MST_EN))
354 return false;
355
356 if (!selectBank(3))
357 return false;
358 if (!bus->write(I2C_MST_ODR_CONFIG, (uint8_t)0x00))
359 return false;
360
361 if (!bus->write(I2C_MST_CTRL, (uint8_t)0x07))
362 return false;
363 if(!bus->write(I2C_MST_DELAY_CTRL, (uint8_t)0x01))
364 return false;
365 delay(10);
366
367 //Soft Reset
368 writeSlave4(AK_CNTL3, 0x01);
369
370 delay(100);
371
372 uint8_t i, who;
373 for (i = 0; i < 10; i++)
374 {
375 readSlave4(AK_WIA2, who);
376 if (who == AK_WIA2_VAL)
377 {
378 //Mode 3 (continuous measurement 100Hz)
379 writeSlave4(AK_CNTL2, 0x08);
380
381 delay(10);
382
383 // ---- Setup auto-read (SLV0) ----
384 if (!selectBank(3))
385 return false;
386
387 if (!bus->write(I2C_SLV0_ADDR, (uint8_t)(AK09916_I2C_ADDR | 0x80)))
388 return false;
389
390 if (!bus->write(I2C_SLV0_REG, (uint8_t)AK_ST1))
391 return false;
392
393 if (!bus->write(I2C_SLV0_CTRL, (uint8_t)(I2C_SLVx_EN | 9)))
394 return false;
395
396 return true;
397 }
398
399 writeSlave4(AK_CNTL2, 0x08);
400
401 delay(10);
402
403 // ---- Setup auto-read (SLV0) ----
404 if (!selectBank(3))
405 return false;
406
407 if (!bus->write(I2C_SLV0_ADDR, (uint8_t)(AK09916_I2C_ADDR | 0x80)))
408 return false;
409
410 if (!bus->write(I2C_SLV0_REG, (uint8_t)AK_ST1))
411 return false;
412
413 if (!bus->write(I2C_SLV0_CTRL, (uint8_t)(I2C_SLVx_EN | 9)))
414 return false;
415
416 return false;
417 }
418}
419
421{
422 // Write operation mode to magnetometer
423 writeSlave4(AK_CNTL2, (uint8_t)opMode);
424
425 return true;
426}
427
428bool DevLab_ICM20948::writeSlave4(uint8_t reg, uint8_t value) {
429 if (!selectBank(3)) return false;
430
431 bus->write(I2C_SLV4_ADDR, (uint8_t)(AK09916_I2C_ADDR)); // write mode
432 bus->write(I2C_SLV4_REG, (uint8_t)reg);
433 bus->write(I2C_SLV4_DO, (uint8_t)value);
434 bus->write(I2C_SLV4_CTRL, (uint8_t)0x80); // iniciar transacción
435
436 // Cambiar a Bank 0 para verificar I2C_MST_STATUS
437 if (!selectBank(0)) return false;
438
439 uint32_t start = millis();
440 uint8_t status;
441 while (millis() - start < 20) {
442 if (bus->read(I2C_MST_STATUS, status))
443 if (status & 0x40) // bit 6 = I2C_SLV4_DONE
444 return true;
445 }
446 return false; // timeout
447}
448
449bool DevLab_ICM20948::readSlave4(uint8_t reg, uint8_t &value) {
450 if (!selectBank(3)) return false;
451
452 bus->write(I2C_SLV4_ADDR, (uint8_t)(AK09916_I2C_ADDR | 0x80)); // read mode
453 bus->write(I2C_SLV4_REG, (uint8_t)reg);
454 bus->write(I2C_SLV4_CTRL, (uint8_t)0x80); // iniciar transacción
455
456 // Cambiar a Bank 0 para verificar I2C_MST_STATUS
457 if (!selectBank(0)) return false;
458
459 uint32_t start = millis();
460 uint8_t status;
461 while (millis() - start < 20) {
462 if (bus->read(I2C_MST_STATUS, status))
463 if (status & 0x40) { // I2C_SLV4_DONE
464 if (!selectBank(3)) return false;
465 return bus->read(I2C_SLV4_DI, value);
466 }
467 }
468 return false; // timeout
469}
470
472{
473 // Select USER BANK 0
474 if (!selectBank(0))
475 return false;
476
477 // Set or clear LP_EN bit (bit 5)
478 if (!bus->writeBit(PWR_MGMT_1, 5, (uint8_t)enable))
479 return false;
480
481 // Success
482 return true;
483}
484
486{
487 // Select USER BANK 0
488 if (!selectBank(0))
489 return false;
490
491 // Read LP_EN bit (bit 5)
492 uint8_t v = 0;
493 if (!bus->readBit(PWR_MGMT_1, 5, v))
494 return false;
495
496 // Convert bit value to boolean
497 enable = (v == 1);
498
499 // Success
500 return true;
501}
502
504{
505 // Select USER BANK 0
506 if (!selectBank(0))
507 return false;
508
509 // Set CLKSEL bits [2:0]
510 if (!bus->write(PWR_MGMT_1, (uint8_t)clock))
511 return false;
512
513 // Success
514 return true;
515}
516
517bool DevLab_ICM20948::getClock(uint8_t &clock)
518{
519 // Select USER BANK 0
520 if (!selectBank(0))
521 return false;
522
523 // Read CLKSEL bits [2:0]
524 if (!bus->read(PWR_MGMT_1, clock))
525 return false;
526
527 clock &= 0x07;
528
529 // Success
530 return true;
531}
532
534{
535 // Validate sample rate range (approx 4.3 Hz to 1100 Hz)
536 if ((sampleRate < 4.3f) || (sampleRate > 1100.0f))
537 return false;
538
539 // Compute divider value
540 uint8_t v = (uint8_t)((1100.0f / sampleRate) - 1.0f);
541
542 // Select USER BANK 2
543 if (!selectBank(2))
544 return false;
545
546 // Write sample rate divider
547 if (!bus->write(GYRO_SMPLRT_DIV, v))
548 return false;
549
550 // Success
551 return true;
552}
553
555{
556 // Validate bus pointer
557 if (!bus)
558 return false;
559
560 // Select USER BANK 2
561 if (!selectBank(2))
562 return false;
563
564 // Read sample rate divider
565 uint8_t v = 0;
566 if (!bus->read(GYRO_SMPLRT_DIV, v))
567 return false;
568
569 // Compute sample rate
570 sampleRate = 1100.0f / (1.0f + v);
571
572 // Success
573 return true;
574}
575
577{
578 // Validate bus pointer
579 if (!bus)
580 return false;
581
582 // Select USER BANK 2
583 if (!selectBank(2))
584 return false;
585
586 // Enable or disable DLPF bypass (bit 0)
587 if (!bus->writeBit(GYRO_CONFIG_1, 0, (uint8_t)bypass))
588 return false;
589
590 // If bypass enabled, skip DLPF configuration
591 if (bypass)
592 return true;
593
594 // Set DLPF configuration bits [5:3]
595 if (!bus->writeBits(GYRO_CONFIG_1, 3, 3, (uint8_t)dlpf))
596 return false;
597
598 // Success
599 return true;
600}
601
602bool DevLab_ICM20948::getDLPF(uint8_t &dlpf, bool &bypass)
603{
604 // Validate bus pointer
605 if (!bus)
606 return false;
607
608 // Select USER BANK 2
609 if (!selectBank(2))
610 return false;
611
612 // Read GYRO_CONFIG_1 register
613 uint8_t v = 0;
614 if (!bus->read(GYRO_CONFIG_1, v))
615 return false;
616
617 // Extract bypass bit (bit 0)
618 bypass = (v & 0x01);
619
620 // Extract DLPF bits [5:3]
621 dlpf = (v >> 3) & 0x07;
622
623 // Success
624 return true;
625}
626
628{
629 // Validate bus pointer
630 if (!bus)
631 return false;
632
633 // Select USER BANK 2
634 if (!selectBank(2))
635 return false;
636
637 // Set full-scale range bits [2:1]
638 if (!bus->writeBits(GYRO_CONFIG_1, 1, 2, (uint8_t)fullScale))
639 return false;
640
641 // Update internal scale (LSB per g)
642 degree_per_second = 32768.0f / (250.0f * (1 << ((uint8_t)fullScale)));
643
644 // Success
645 return true;
646}
647
648bool DevLab_ICM20948::getGyroScale(uint8_t &fullScale)
649{
650 // Validate bus pointer
651 if (!bus)
652 return false;
653
654 // Select USER BANK 2
655 if (!selectBank(2))
656 return false;
657
658 // Read full-scale range bits [2:1]
659 if (!bus->readBits(GYRO_CONFIG_1, 1, 2, fullScale))
660 return false;
661
662 // Success
663 return true;
664}
665
666bool DevLab_ICM20948::selfTestGyro(bool x, bool y, bool z)
667{
668 // Validate bus pointer
669 if (!bus)
670 return false;
671
672 // Select USER BANK 2
673 if (!selectBank(2))
674 return false;
675
676 // Set Z-axis self-test (bit 3)
677 if (!bus->writeBit(GYRO_CONFIG_2, 3, (uint8_t)z))
678 return false;
679
680 // Set Y-axis self-test (bit 4)
681 if (!bus->writeBit(GYRO_CONFIG_2, 4, (uint8_t)y))
682 return false;
683
684 // Set X-axis self-test (bit 5)
685 if (!bus->writeBit(GYRO_CONFIG_2, 5, (uint8_t)x))
686 return false;
687
688 // Success
689 return true;
690}
691
692bool DevLab_ICM20948::setAccelSampleRate(uint16_t sampleRate)
693{
694 // Validate bus pointer
695 if (!bus)
696 return false;
697
698 // Select USER BANK 2
699 if (!selectBank(2))
700 return false;
701
702 // Validate input
703 if (sampleRate == 0)
704 return false;
705
706 // Clamp maximum rate
707 if (sampleRate >= 1125)
708 return false;
709
710 // Compute divider (rounded)
711 uint16_t div = (1125.0f / sampleRate) - 1;
712
713 if (div > 4095u)
714 div = 4095u;
715
716 // Split divider into high and low bytes
717 uint8_t msb = (uint8_t)((div >> 8) & 0x0F);
718 uint8_t lsb = (uint8_t)(div & 0xFF);
719
720 // Write divider high byte
721 if (!bus->write(ACCEL_SMPLRT_DIV_1, msb))
722 return false;
723
724 // Write divider low byte
725 if (!bus->write(ACCEL_SMPLRT_DIV_2, lsb))
726 return false;
727
728 // Success
729 return true;
730}
732{
733 if (!bus)
734 return false;
735
736 // Select USER BANK 2
737 if (!selectBank(2))
738 return false;
739
740 // Validate input
741 if (divisor == 0)
742 return false;
743
744 if (divisor > 4095u)
745 divisor = 4095u;
746
747 // Split divider into high and low bytes
748 uint8_t msb = (uint8_t)((divisor >> 8) & 0x0F);
749 uint8_t lsb = (uint8_t)(divisor & 0xFF);
750
751 // Write divider high byte
752 if (!bus->write(ACCEL_SMPLRT_DIV_1, msb))
753 return false;
754
755 // Write divider low byte
756 if (!bus->write(ACCEL_SMPLRT_DIV_2, lsb))
757 return false;
758
759 // Success
760 return true;
761}
762
764{
765 if (!bus)
766 return false;
767 // Select USER BANK 2
768 if (!selectBank(2))
769 return false;
770
771 // Write sample rate divider
772 if (!bus->write(GYRO_SMPLRT_DIV, divisor))
773 return false;
774
775 // Success
776 return true;
777}
778
780{
781 // Validate bus pointer
782 if (!bus)
783 return false;
784
785 // Select USER BANK 2
786 if (!selectBank(2))
787 return false;
788
789 // Read divider high byte
790 uint8_t msb = 0;
791 if (!bus->read(ACCEL_SMPLRT_DIV_1, msb))
792 return false;
793
794 // Read divider low byte
795 uint8_t lsb = 0;
796 if (!bus->read(ACCEL_SMPLRT_DIV_2, lsb))
797 return false;
798
799 // Combine 12-bit divider
800 uint16_t div = ((msb & 0x0F) << 8) | lsb;
801
802 // Compute sample rate
803 sampleRate = 1125.0f / (1.0f + div);
804
805 // Success
806 return true;
807}
808
809bool DevLab_ICM20948::selfTestAccel(bool x, bool y, bool z)
810{
811 // Validate bus pointer
812 if (!bus)
813 return false;
814
815 // Select USER BANK 2
816 if (!selectBank(2))
817 return false;
818
819 if (x)
820 { // Set X-axis self-test (bit 7)
821 if (!bus->writeBit(ACCEL_CONFIG_2, 7, (uint8_t)x))
822 return false;
823 }
824
825 if (y)
826 { // Set Y-axis self-test (bit 6)
827 if (!bus->writeBit(ACCEL_CONFIG_2, 6, (uint8_t)y))
828 return false;
829 }
830
831 if (z)
832 { // Set Z-axis self-test (bit 5)
833 if (!bus->writeBit(ACCEL_CONFIG_2, 5, (uint8_t)z))
834 return false;
835 }
836 // Success
837 return true;
838}
839
841{
842 // Validate bus pointer
843 if (!bus)
844 return false;
845
846 // Select USER BANK 2
847 if (!selectBank(2))
848 return false;
849
850 // Set full-scale bits [2:1]
851 if (!bus->writeBits(ACCEL_CONFIG, 1, 2, (uint8_t)fullScale))
852 return false;
853
854 // Update internal scale (LSB per g)
855 mg_per_lsb = 16384.0f / (1 << (uint8_t)fullScale);
856
857 // Success
858 return true;
859}
860
861bool DevLab_ICM20948::getAccelScale(uint8_t &fullScale)
862{
863 // Validate bus pointer
864 if (!bus)
865 return false;
866
867 // Select USER BANK 2
868 if (!selectBank(2))
869 return false;
870
871 // Read full-scale bits [2:1]
872 if (!bus->readBits(ACCEL_CONFIG, 1, 2, fullScale))
873 return false;
874
875 // Success
876 return true;
877}
878
879
880bool DevLab_ICM20948::setAccelDLPF(uint8_t dlpf, bool bypass)
881{
882 // Validate bus pointer
883 if (!bus)
884 return false;
885
886 // Select USER BANK 2
887 if (!selectBank(2))
888 return false;
889
890 // Set or clear DLPF bypass (bit 0)
891 if (!bus->writeBit(ACCEL_CONFIG, 0, (uint8_t)bypass))
892 return false;
893
894 // If bypass enabled, skip DLPF configuration
895 if (bypass)
896 return true;
897
898 // Limit DLPF value to valid range
899 dlpf &= 0x07;
900
901 // Set DLPF configuration bits [5:3]
902 if (!bus->writeBits(ACCEL_CONFIG, 3, 3, dlpf))
903 return false;
904
905 // Success
906 return true;
907}
908
909bool DevLab_ICM20948::getAccelDLPF(uint8_t &dlpf, bool &bypass)
910{
911 // Validate bus pointer
912 if (!bus)
913 return false;
914
915 // Select USER BANK 2
916 if (!selectBank(2))
917 return false;
918
919 // Read ACCEL_CONFIG register
920 uint8_t v = 0;
921 if (!bus->read(ACCEL_CONFIG, v))
922 return false;
923
924 // Extract bypass bit (bit 0)
925 bypass = (v & 0x01);
926
927 // Extract DLPF bits [5:3]
928 dlpf = (v >> 3) & 0x07;
929
930 // Success
931 return true;
932}
933
934
936{
937 // Validate bus pointer
938 if (!bus)
939 return false;
940
941 // Select USER BANK 2
942 if (!selectBank(2))
943 return false;
944
945 // Set DEC3 averaging bits [1:0]
946 if (!bus->writeBits(ACCEL_CONFIG_2, 0, 2, (uint8_t)avg))
947 return false;
948
949 // Success
950 return true;
951}
952
954{
955 // Validate bus pointer
956 if (!bus)
957 return false;
958
959 // Select USER BANK 2
960 if (!selectBank(2))
961 return false;
962
963 // Set DEC3 averaging bits [1:0]
964 if (!bus->writeBits(GYRO_CONFIG_2, 0, 2, (uint8_t)avg))
965 return false;
966
967 // Success
968 return true;
969}
970
972{
973 // Validate bus pointer
974 if (!bus)
975 return false;
976
977 // Select USER BANK 2
978 if (!selectBank(2))
979 return false;
980
981 // Read DEC3 averaging bits [1:0]
982 if (!bus->readBits(ACCEL_CONFIG_2, 0, 2, avg))
983 return false;
984
985 // Success
986 return true;
987}
988
989bool DevLab_ICM20948::setSensors(bool accel_on, bool gyro_on, bool temp_on)
990{
991 // Validate bus pointer
992 if (!bus)
993 return false;
994
995 // Select USER BANK 0
996 if (!selectBank(0))
997 return false;
998
999 // Build PWR_MGMT_2 mask
1000 uint8_t v = 0;
1001
1002 // Disable accel axes if not enabled
1003 if (!accel_on)
1004 v |= 0x38; // bits [5:3]
1005
1006 // Disable gyro axes if not enabled
1007 if (!gyro_on)
1008 v |= 0x07; // bits [2:0]
1009
1010 // Write sensor enable/disable mask
1011 if (!bus->write(PWR_MGMT_2, v))
1012 return false;
1013
1014 // Set or clear TEMP_DIS bit (bit 3)
1015 if (!bus->writeBit(PWR_MGMT_1, 3, (uint8_t)!temp_on))
1016 return false;
1017
1018 // Success
1019 return true;
1020}
1021
1022bool DevLab_ICM20948::getSensors(bool &accel_on, bool &gyro_on, bool &temp_on)
1023{
1024 // Validate bus pointer
1025 if (!bus)
1026 return false;
1027
1028 // Select USER BANK 0
1029 if (!selectBank(0))
1030 return false;
1031
1032 // Read PWR_MGMT_2 register
1033 uint8_t v = 0;
1034 if (!bus->read(PWR_MGMT_2, v))
1035 return false;
1036
1037 // Decode gyro state (bits [2:0])
1038 gyro_on = ((v & 0x07) == 0);
1039
1040 // Decode accel state (bits [5:3])
1041 accel_on = ((v & 0x38) == 0);
1042
1043 // Read TEMP_DIS bit (bit 3)
1044 uint8_t t = 0;
1045 if (!bus->readBit(PWR_MGMT_1, 3, t))
1046 return false;
1047
1048 // Decode temperature state
1049 temp_on = (t == 0);
1050
1051 // Success
1052 return true;
1053}
1054
1055bool DevLab_ICM20948::setGyroOffset(uint16_t offsetX, uint16_t offsetY, uint16_t offsetZ)
1056{
1057 // Validate bus pointer
1058 if (!bus)
1059 return false;
1060
1061 // Select USER BANK 2
1062 if (!selectBank(2))
1063 return false;
1064
1065 // Pack offset values into byte array
1066 uint8_t data[6] = {
1067 (uint8_t)(offsetX >> 8),
1068 (uint8_t)(offsetX & 0xFF),
1069 (uint8_t)(offsetY >> 8),
1070 (uint8_t)(offsetY & 0xFF),
1071 (uint8_t)(offsetZ >> 8),
1072 (uint8_t)(offsetZ & 0xFF)};
1073
1074 // Write offset registers
1075 if (!bus->write(XG_OFFS_USRH, data, 6))
1076 return false;
1077
1078 // Success
1079 return true;
1080}
1081
1082bool DevLab_ICM20948::getGyroOffset(int16_t &offsetX, int16_t &offsetY, int16_t &offsetZ)
1083{
1084 // Validate bus pointer
1085 if (!bus)
1086 return false;
1087
1088 // Select USER BANK 2
1089 if (!selectBank(2))
1090 return false;
1091
1092 // Read offset registers
1093 uint8_t data[6];
1094 if (!bus->read(XG_OFFS_USRH, data, 6))
1095 return false;
1096
1097 // Convert to signed values
1098 offsetX = (int16_t)((data[0] << 8) | data[1]);
1099 offsetY = (int16_t)((data[2] << 8) | data[3]);
1100 offsetZ = (int16_t)((data[4] << 8) | data[5]);
1101
1102 // Success
1103 return true;
1104}
1105
1106
1107bool DevLab_ICM20948::setAccelOffset(int16_t offsetX, int16_t offsetY, int16_t offsetZ)
1108{
1109 // Validate bus pointer
1110 if (!bus)
1111 return false;
1112
1113 // Select USER BANK 1 (accel offsets are here)
1114 if (!selectBank(1))
1115 return false;
1116
1117 // Pack offset values into byte array
1118 uint8_t data[6] = {
1119 (uint8_t)(offsetX >> 8),
1120 (uint8_t)(offsetX & 0xFF),
1121 (uint8_t)(offsetY >> 8),
1122 (uint8_t)(offsetY & 0xFF),
1123 (uint8_t)(offsetZ >> 8),
1124 (uint8_t)(offsetZ & 0xFF)};
1125
1126 // Write offset registers
1127 if (!bus->write(XA_OFFS_H, data, 6))
1128 return false;
1129
1130 // Success
1131 return true;
1132}
1133
1134bool DevLab_ICM20948::getAccelOffset(int16_t &offsetX, int16_t &offsetY, int16_t &offsetZ)
1135{
1136 // Validate bus pointer
1137 if (!bus)
1138 return false;
1139
1140 // Select USER BANK 1
1141 if (!selectBank(1))
1142 return false;
1143
1144 // Read offset registers
1145 uint8_t data[6];
1146 if (!bus->read(XA_OFFS_H, data, 6))
1147 return false;
1148
1149 // Convert to signed values
1150 offsetX = (int16_t)((data[0] << 8) | data[1]);
1151 offsetY = (int16_t)((data[2] << 8) | data[3]);
1152 offsetZ = (int16_t)((data[4] << 8) | data[5]);
1153
1154 // Success
1155 return true;
1156}
1157
1158
1160 // Validate bus pointer
1161 if (!bus)
1162 return false;
1163
1164 if(!selectBank(0))
1165 return false;
1166
1167 uint8_t reg = (cfg.activeLevel << 7) |
1168 (cfg.driveMode << 6) |
1169 (cfg.latchMode << 5) |
1170 (cfg.clearMode << 4) |
1171 (cfg.fsyncActLevel << 3) |
1172 (cfg.fsyncIntEn << 2) |
1173 (cfg.bypassEn << 1);
1174 // Initialize INT1
1175 // Configure interrupt pin (open-drain, active-low, latch)
1176 if (!bus->write(INT_PIN_CFG, reg))
1177 return false;
1178
1179 // Configuration successful
1180 return true;
1181}
1182
1184{
1185 if (!bus) return false;
1186 if (!selectBank(0)) return false;
1187
1188 // INT_ENABLE
1189 uint8_t reg0 = (cfg.wofEn << 7) |
1190 (cfg.womIntEn << 3) |
1191 (cfg.pllRdyEn << 2) |
1192 (cfg.dmpInt1En << 1) |
1193 (cfg.i2cMstIntEn << 0);
1194 if (!bus->write(INT_ENABLE, reg0)) return false;
1195
1196 // INT_ENABLE_1
1197 if (!bus->write(INT_ENABLE_1, (uint8_t)(cfg.rawDataRdyEn & 0x01))) return false;
1198
1199 // INT_ENABLE_2 — construye máscara desde el array
1200 uint8_t ovfMask = 0;
1201 for (uint8_t i = 0; i < 5; i++)
1202 ovfMask |= (cfg.fifoOvfEn[i] ? BIT(i) : 0);
1203 if (!bus->write(INT_ENABLE_2, ovfMask)) return false;
1204
1205 // INT_ENABLE_3 — igual
1206 uint8_t wmMask = 0;
1207 for (uint8_t i = 0; i < 5; i++)
1208 wmMask |= (cfg.fifoWmEn[i] ? BIT(i) : 0);
1209 if (!bus->write(INT_ENABLE_3, wmMask)) return false;
1210
1211 return true;
1212}
1213
1215{
1216 if (!bus) return false;
1217 if (!selectBank(0)) return false;
1218
1219 // Leer los 4 registros de status en una sola operación de burst
1220 uint8_t raw[4];
1221 if (!bus->read(INT_STATUS, raw, 4)) return false;
1222
1223 // INT_STATUS (0x19)
1224 status.womInt = (raw[0] & STS_WOM_INT) ? 1 : 0;
1225 status.pllRdyInt = (raw[0] & STS_PLL_RDY_INT) ? 1 : 0;
1226 status.dmpInt1 = (raw[0] & STS_DMP_INT1) ? 1 : 0;
1227 status.i2cMstInt = (raw[0] & STS_I2C_MST_INT) ? 1 : 0;
1228
1229 // INT_STATUS_1 (0x1A)
1230 status.rawDataRdy = (raw[1] & STS_RAW_DATA_0_RDY_INT) ? 1 : 0;
1231
1232 // INT_STATUS_2 (0x1B) — FIFO overflow canal por canal
1233 for (uint8_t i = 0; i < 5; i++)
1234 status.fifoOvf[i] = (raw[2] & BIT(i)) ? 1 : 0;
1235
1236 // INT_STATUS_3 (0x1C) — FIFO watermark canal por canal
1237 for (uint8_t i = 0; i < 5; i++)
1238 status.fifoWm[i] = (raw[3] & BIT(i)) ? 1 : 0;
1239
1240 return true;
1241}
1242
1244{
1245 // BANK 0: asegurarse que BYPASS_EN=0 e I2C_MST_EN=1
1246 if (!selectBank(0)) return false;
1247
1248 // Limpiar BYPASS_EN (bit 1 de INT_PIN_CFG)
1249 if (!bus->writeBit(INT_PIN_CFG, 1, (uint8_t)0)) return false;
1250
1251 // Activar I2C_MST_EN (bit 5 de USER_CTRL)
1252 if (!bus->writeBit(USER_CTRL, 5, (uint8_t)1)) return false;
1253
1254 // BANK 3: configurar clock del master auxiliar
1255 if (!selectBank(3)) return false;
1256
1257 // clkFreq típico: 0x07 = ~345.6 kHz | 0x0D = ~400 kHz
1258 if (!bus->writeBits(I2C_MST_CTRL, 0, 4, clkFreq)) return false;
1259
1260 // Volver a BANK 0
1261 return selectBank(0);
1262}
1263
1264bool DevLab_ICM20948::auxWriteByte(uint8_t slaveAddr, uint8_t reg, uint8_t data)
1265{
1266 if (!selectBank(3)) return false;
1267
1268 // Dirección del slave, bit 7=0 para escritura
1269 if (!bus->write(I2C_SLV4_ADDR, (uint8_t)(slaveAddr & 0x7F))) return false;
1270
1271 // Registro destino que queremos escribir en el slave
1272 if (!bus->write(I2C_SLV4_REG, reg)) return false;
1273
1274 // Dato a escribir
1275 if (!bus->write(I2C_SLV4_DO, data)) return false;
1276
1277 // Disparar transacción (I2C_SLV4_EN, se auto-limpia al terminar)
1278 if (!bus->write(I2C_SLV4_CTRL, (uint8_t)I2C_SLVx_EN)) return false;
1279
1280 // Esperar a que complete — verificar SLV4_DONE en BANK 0
1281 if (!selectBank(0)) return false;
1282
1283 uint8_t status = 0;
1284 uint8_t timeout = 50;
1285 do {
1286 delay(1);
1287 if (!bus->read(I2C_MST_STATUS, status)) return false;
1288 if (--timeout == 0) return false; // timeout
1289 } while (!(status & MST_SLV4_DONE));
1290
1291 // Verificar que no hubo NACK
1292 return !(status & MST_SLV4_NACK);
1293}
1294
1295bool DevLab_ICM20948::auxReadByte(uint8_t slaveAddr, uint8_t reg, uint8_t &data)
1296{
1297 if (!selectBank(3)) return false;
1298
1299 // Dirección del slave, bit 7=1 para lectura
1300 if (!bus->write(I2C_SLV4_ADDR, (uint8_t)((slaveAddr & 0x7F) | I2C_SLVx_RNW))) return false;
1301
1302 // Registro origen que queremos leer del slave
1303 if (!bus->write(I2C_SLV4_REG, reg)) return false;
1304
1305 // Disparar transacción (I2C_SLV4_EN, se auto-limpia al terminar)
1306 if (!bus->write(I2C_SLV4_CTRL, (uint8_t)I2C_SLVx_EN)) return false;
1307
1308 // Esperar a que complete — verificar SLV4_DONE en BANK 0
1309 if (!selectBank(0)) return false;
1310
1311 uint8_t status = 0;
1312 uint8_t timeout = 50;
1313 do {
1314 delay(1);
1315 if (!bus->read(I2C_MST_STATUS, status)) return false;
1316 if (--timeout == 0) return false; // timeout
1317 } while (!(status & MST_SLV4_DONE));
1318
1319 // Verificar que no hubo NACK
1320 if (status & MST_SLV4_NACK) return false;
1321
1322 // Leer el dato recibido del slave (BANK 3)
1323 if (!selectBank(3)) return false;
1324 if (!bus->read(I2C_SLV4_DI, data)) return false;
1325
1326 return selectBank(0);
1327}
1328
1329bool DevLab_ICM20948::auxWriteCommand(uint8_t slaveAddr, uint8_t cmd)
1330{
1331 if (!selectBank(3)) return false;
1332
1333 // Dirección del slave, bit 7=0 para escritura
1334 if (!bus->write(I2C_SLV4_ADDR, (uint8_t)(slaveAddr & 0x7F))) return false;
1335
1336 // Byte de comando a enviar (único byte de la transacción)
1337 if (!bus->write(I2C_SLV4_DO, cmd)) return false;
1338
1339 // EN + REG_DIS: el SLV4 envía solo I2C_SLV4_DO, sin byte de registro
1340 if (!bus->write(I2C_SLV4_CTRL, (uint8_t)(I2C_SLVx_EN | I2C_SLVx_REG_DIS))) return false;
1341
1342 // Esperar a que complete — verificar SLV4_DONE en BANK 0
1343 if (!selectBank(0)) return false;
1344
1345 uint8_t status = 0;
1346 uint8_t timeout = 50;
1347 do {
1348 delay(1);
1349 if (!bus->read(I2C_MST_STATUS, status)) return false;
1350 if (--timeout == 0) return false; // timeout
1351 } while (!(status & MST_SLV4_DONE));
1352
1353 // Verificar que no hubo NACK
1354 return !(status & MST_SLV4_NACK);
1355}
1356
1357 bool DevLab_ICM20948::auxConfigSlave(uint8_t slaveAddr, uint8_t reg, uint8_t numBytes)
1358{
1359 if (!selectBank(3)) return false;
1360
1361 // Slave 0: dirección + bit READ
1362 if (!bus->write(I2C_SLV0_ADDR, (uint8_t)(slaveAddr | I2C_SLVx_RNW))) return false;
1363
1364 // Registro de inicio
1365 if (!bus->write(I2C_SLV0_REG, reg)) return false;
1366
1367 // Habilitar + número de bytes
1368 if (!bus->write(I2C_SLV0_CTRL, (uint8_t)(I2C_SLVx_EN | (numBytes & 0x0F)))) return false;
1369
1370 return selectBank(0);
1371}
1372
1373// Para leer los datos que el ICM leyó automáticamente:
1374bool DevLab_ICM20948::auxReadSensorData(uint8_t *buf, uint8_t len)
1375{
1376 if (!selectBank(0)) return false;
1377 return bus->read(EXT_SLV_SENS_DATA_00, buf, len);
1378}
1379
1380bool DevLab_ICM20948::auxReadResponse(uint8_t slaveAddr, uint8_t &data)
1381{
1382 if (!selectBank(3)) return false;
1383
1384 // Slave address con bit READ, sin byte de registro (REG_DIS)
1385 // Genera: [START, addr+R, lee 1 byte, STOP]
1386 if (!bus->write(I2C_SLV4_ADDR, (uint8_t)((slaveAddr & 0x7F) | I2C_SLVx_RNW))) return false;
1387 if (!bus->write(I2C_SLV4_CTRL, (uint8_t)(I2C_SLVx_EN | I2C_SLVx_REG_DIS))) return false;
1388
1389 if (!selectBank(0)) return false;
1390
1391 uint8_t status = 0;
1392 uint8_t timeout = 50;
1393 do {
1394 delay(1);
1395 if (!bus->read(I2C_MST_STATUS, status)) return false;
1396 if (--timeout == 0) return false;
1397 } while (!(status & MST_SLV4_DONE));
1398
1399 if (status & MST_SLV4_NACK) return false;
1400
1401 if (!selectBank(3)) return false;
1402 if (!bus->read(I2C_SLV4_DI, data)) return false;
1403
1404 return selectBank(0);
1405}
1406
1408{
1409 uint8_t buf[2];
1410 if (!auxReadSensorData(buf, 2)) return false;
1411
1412 // LSB primero (little-endian): buf[0] = byte bajo, buf[1] = byte alto
1413 raw = (uint16_t)(buf[0] | (buf[1] << 8)) & 0x0FFF;
1414
1415 return true;
1416}
ICM20948_Gyro_FullScale
ICM20948_Clock_Source
ICM20948_Gyro_Average
ICM20948_Accel_Average
ICM20948_Accel_FullScale
ICM20948_Gyro_DLPF
ICM20948_Op_Mode
#define GYRO_XOUT_H
#define I2C_SLVx_EN
#define STS_I2C_MST_INT
#define XA_OFFS_H
#define I2C_SLV0_REG
#define I2C_MST_DELAY_CTRL
#define STS_WOM_INT
#define ODR_ALIGN_EN
#define PWR_MGMT_2
#define I2C_MST_CTRL
#define I2C_SLV4_ADDR
#define STS_PLL_RDY_INT
#define I2C_SLV4_DO
#define AK_CNTL3
#define INT_ENABLE_2
#define EXT_SLV_SENS_DATA_00
#define INT_ENABLE_3
#define WHO_AM_I_VAL
#define AK09916_I2C_ADDR
#define ACCEL_SMPLRT_DIV_2
#define I2C_SLV0_ADDR
#define INT_STATUS
#define XG_OFFS_USRH
#define STS_DMP_INT1
#define I2C_SLV4_CTRL
#define I2C_SLV4_DI
#define I2C_MST_STATUS
#define ACCEL_CONFIG_2
#define I2C_SLV4_REG
#define I2C_SLVx_RNW
#define AK_ST1
#define ACCEL_XOUT_H
#define PWR_MGMT_1
#define I2C_MST_ODR_CONFIG
#define LP_CONFIG
#define REG_BANK_SEL
#define WHO_AM_I
#define AK_CNTL2
#define INT_PIN_CFG
#define ACCEL_CONFIG
#define USER_CTRL
#define INT_ENABLE
#define GYRO_CONFIG_1
#define ACCEL_SMPLRT_DIV_1
#define MST_SLV4_DONE
#define GYRO_SMPLRT_DIV
#define AK_WIA2
#define GYRO_CONFIG_2
#define I2C_SLV0_CTRL
#define MST_SLV4_NACK
#define AK_WIA2_VAL
#define STS_RAW_DATA_0_RDY_INT
#define TEMP_OUT_H
#define INT_ENABLE_1
#define USER_CTRL_I2C_MST_EN
#define I2C_SLVx_REG_DIS
bool readBit(uint8_t reg, uint8_t pos, uint8_t &value)
bool writeBits(uint8_t reg, uint8_t pos, uint8_t len, uint8_t value)
Definition BusIO_7Semi.h:98
bool read(uint8_t reg, uint8_t &value)
Definition BusIO_7Semi.h:17
bool write(uint8_t reg, uint8_t value)
Definition BusIO_7Semi.h:39
bool writeBit(uint8_t reg, uint8_t pos, uint8_t value)
bool readBits(uint8_t reg, uint8_t pos, uint8_t len, uint8_t &value)
Definition BusIO_7Semi.h:60
bool readGyro(float &x, float &y, float &z)
bool setMagOpMode(ICM20948_Op_Mode opMode)
bool setDLPF(ICM20948_Gyro_DLPF dlpf, bool bypass)
bool auxWriteByte(uint8_t slaveAddr, uint8_t reg, uint8_t data)
bool setGyroDivRate(uint8_t divisor)
bool setClock(ICM20948_Clock_Source clock)
bool setAccelScale(ICM20948_Accel_FullScale fullScale)
bool auxReadByte(uint8_t slaveAddr, uint8_t reg, uint8_t &data)
bool auxRead12bit(uint16_t &raw)
bool readAccel(float &x, float &y, float &z)
bool intEnableConfig(const ICM20948_IntEnableConfig &cfg)
bool getGyroOffset(int16_t &offsetX, int16_t &offsetY, int16_t &offsetZ)
bool sleep(bool en)
bool beginSPI(uint8_t csPin, SPIClass &spiPort=SPI, uint32_t spiSpeed=1000000)
bool auxReadResponse(uint8_t slaveAddr, uint8_t &data)
bool getGyroScale(uint8_t &fullScale)
bool setAccelAveraging(ICM20948_Accel_Average avg)
bool checkIntStatus(ICM20948_IntStatus &status)
bool setGyroOffset(uint16_t offsetX, uint16_t offsetY, uint16_t offsetZ)
bool setGyroScale(ICM20948_Gyro_FullScale fullScale)
bool setLowPower(bool enable)
bool setGyroSampleRate(float sampleRate)
bool getGyroSampleRate(float &sampleRate)
bool setGyroAveraging(ICM20948_Gyro_Average avg)
bool setAccelOffset(int16_t offsetX, int16_t offsetY, int16_t offsetZ)
bool getSensors(bool &accel_on, bool &gyro_on, bool &temp_on)
bool readWhoAmI(uint8_t &whoAmI)
bool auxReadSensorData(uint8_t *buf, uint8_t len)
bool beginI2C(uint8_t address=0x69, TwoWire &i2cPort=Wire, uint32_t i2cSpeed=400000)
bool getAccelDLPF(uint8_t &dlpf, bool &bypass)
bool getLowPower(bool &enable)
bool selfTestGyro(bool x, bool y, bool z)
bool getAccelSampleRate(float &sampleRate)
bool readMag(float &x, float &y, float &z)
bool readTemperature(float &temperature)
bool selectBank(uint8_t bank)
bool auxMasterEnable(uint8_t clkFreq)
bool intInit(const ICM20948_IntPinConfig &cfg)
bool getDLPF(uint8_t &dlpf, bool &bypass)
bool auxConfigSlave(uint8_t slaveAddr, uint8_t reg, uint8_t numBytes)
bool auxWriteCommand(uint8_t slaveAddr, uint8_t cmd)
bool getClock(uint8_t &clock)
bool setAccelDLPF(uint8_t dlpf, bool bypass)
bool setAccelDivRate(uint16_t divisor)
bool getAccelScale(uint8_t &fullScale)
bool selfTestAccel(bool x, bool y, bool z)
bool getAccelAveraging(uint8_t &avg)
bool getAccelOffset(int16_t &offsetX, int16_t &offsetY, int16_t &offsetZ)
bool setSensors(bool accel_on, bool gyro_on, bool temp_on)
bool setAccelSampleRate(uint16_t sampleRate)
bool beginI2C(uint8_t addr, TwoWire &wire, uint32_t speed, uint8_t sda=255, uint8_t scl=255)
bool beginSPI(uint8_t csPin, SPIClass &spiPort, uint32_t spiSpeed, uint8_t sck=255, uint8_t miso=255, uint8_t mosi=255) override
const ICM20948_Op_Mode opMode[]
Magnetometer operating modes tested by the sweep.
Definition mag_test.ino:33