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