Skip to content

Commit e6be93d

Browse files
committed
Trying to fix the DMP magnetometer readings
1 parent 38eee73 commit e6be93d

File tree

11 files changed

+2644
-655
lines changed

11 files changed

+2644
-655
lines changed

examples/Arduino/Example9_DMP_MultipleSensors/Example9_DMP_MultipleSensors.ino

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ void setup()
6767
WIRE_PORT.setClock(400000);
6868
#endif
6969

70-
//myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
70+
myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
7171

7272
bool initialized = false;
7373
while (!initialized)
@@ -101,6 +101,41 @@ void setup()
101101

102102
bool success = true; // Use success to show if the configuration was successful
103103

104+
// Normally, when the DMP is not enabled, startupMagnetometer (called by startupDefault, called by begin) configures the AK09916 magnetometer
105+
// to run at 100Hz by setting the CNTL2 register (0x31) to 0x08. Then the ICM20948's I2C_SLV0 is configured to read
106+
// nine bytes from the mag every sample, starting from the STATUS1 register (0x10). ST1 includes the DRDY (Data Ready) bit.
107+
// Next are the six magnetometer readings (little endian). After a dummy byte, the STATUS2 register (0x18) contains the HOFL (Overflow) bit.
108+
//
109+
// But looking very closely at the InvenSense example code, we can see in inv_icm20948_resume_akm (in Icm20948AuxCompassAkm.c) that
110+
// when the DMP is running, the magnetometer is set to Single Measurement (SM) mode and that ten bytes are read, starting from the reserved
111+
// RSV2 register (0x03). The datasheet does not define what registers 0x04 to 0x0C contain. There is definately some secret sauce in here...
112+
// The magnetometer data appears to be big endian (not little endian like the HX/Y/Z registers) and starts at register 0x04.
113+
// We had to examine the I2C traffic between the master and the AK09916 on the AUX_DA and AUX_CL pins to discover this...
114+
//
115+
// So, we need to set up I2C_SLV0 to do the ten byte reading. The parameters passed to i2cControllerConfigurePeripheral are:
116+
// 0: use I2C_SLV0
117+
// MAG_AK09916_I2C_ADDR: the I2C address of the AK09916 magnetometer (0x0C unshifted)
118+
// AK09916_REG_RSV2: we start reading here (0x03). Secret sauce...
119+
// 10: we read 10 bytes each cycle
120+
// true: set the I2C_SLV0_RNW ReadNotWrite bit so we read the 10 bytes (not write them)
121+
// true: set the I2C_SLV0_CTRL I2C_SLV0_EN bit to enable reading from the peripheral at the sample rate
122+
// false: clear the I2C_SLV0_CTRL I2C_SLV0_REG_DIS (we want to write the register value)
123+
// true: set the I2C_SLV0_CTRL I2C_SLV0_GRP bit to show the register pairing starts at byte 1+2 (copied from inv_icm20948_resume_akm)
124+
// true: set the I2C_SLV0_CTRL I2C_SLV0_BYTE_SW to byte-swap the data from the mag (copied from inv_icm20948_resume_akm)
125+
success &= (myICM.i2cControllerConfigurePeripheral(0, MAG_AK09916_I2C_ADDR, AK09916_REG_RSV2, 10, true, true, false, true, true) == ICM_20948_Stat_Ok);
126+
//
127+
// We should also set up I2C_SLV1 to do the Single Measurement triggering:
128+
// AK09916_REG_CNTL2: we start writing here (0x31)
129+
// 1: not sure why, but the write does not happen if this is set to zero
130+
// false: clear the I2C_SLV0_RNW ReadNotWrite bit so we write the dataOut byte
131+
// true: set the I2C_SLV0_CTRL I2C_SLV0_EN bit. Not sure why, but the write does not happen if this is clear
132+
// false: clear the I2C_SLV0_CTRL I2C_SLV0_REG_DIS (we want to write the register value)
133+
// false: clear the I2C_SLV0_CTRL I2C_SLV0_GRP bit
134+
// false: clear the I2C_SLV0_CTRL I2C_SLV0_BYTE_SW bit
135+
// AK09916_mode_single: tell I2C_SLV1 to write the Single Measurement command each sample
136+
// except doing that causes the magnetometer to stall and give out stale data. So, for now, the next line needs to stay commented!
137+
//success &= (myICM.i2cControllerConfigurePeripheral(1, MAG_AK09916_I2C_ADDR, AK09916_REG_CNTL2, 1, false, true, false, false, false, AK09916_mode_single) == ICM_20948_Stat_Ok);
138+
104139
// Configure clock source through PWR_MGMT_1
105140
// ICM_20948_Clock_Auto selects the best available clock source – PLL if ready, else use the Internal oscillator
106141
success &= (myICM.setClockSource(ICM_20948_Clock_Auto) == ICM_20948_Stat_Ok); // This is shorthand: success will be set to false if setClockSource fails
@@ -258,8 +293,9 @@ void setup()
258293
success &= (myICM.writeDMPmems(ACCEL_CAL_RATE, 2, &accelCalRate[0]) == ICM_20948_Stat_Ok);
259294

260295
// Configure the Compass Time Buffer. The compass (magnetometer) is set to 100Hz (AK09916_mode_cont_100hz)
261-
// in startupMagnetometer. We need to set CPASS_TIME_BUFFER to 100 too.
296+
// in startupMagnetometer. I think we need to set CPASS_TIME_BUFFER to 100 too.
262297
const unsigned char compassRate[2] = {0x00, 0x64}; // 100Hz
298+
//const unsigned char compassRate[2] = {0x04, 0x65}; // 1125Hz - same as the accelerometer sample rate
263299
success &= (myICM.writeDMPmems(CPASS_TIME_BUFFER, 2, &compassRate[0]) == ICM_20948_Stat_Ok);
264300

265301
// Enable DMP interrupt

keywords.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ startupMagnetometer KEYWORD2
7979
magWhoIAm KEYWORD2
8080
readMag KEYWORD2
8181
writeMag KEYWORD2
82+
resetMag KEYWORD2
8283
enableFIFO KEYWORD2
8384
resetFIFO KEYWORD2
8485
setFIFOmode KEYWORD2

src/ICM_20948.cpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -767,17 +767,17 @@ ICM_20948_Status_e ICM_20948::i2cMasterReset()
767767
return status;
768768
}
769769

770-
ICM_20948_Status_e ICM_20948::i2cControllerConfigurePeripheral(uint8_t peripheral, uint8_t addr, uint8_t reg, uint8_t len, bool Rw, bool enable, bool data_only, bool grp, bool swap)
770+
ICM_20948_Status_e ICM_20948::i2cControllerConfigurePeripheral(uint8_t peripheral, uint8_t addr, uint8_t reg, uint8_t len, bool Rw, bool enable, bool data_only, bool grp, bool swap, uint8_t dataOut)
771771
{
772-
status = ICM_20948_i2c_controller_configure_peripheral(&_device, peripheral, addr, reg, len, Rw, enable, data_only, grp, swap);
772+
status = ICM_20948_i2c_controller_configure_peripheral(&_device, peripheral, addr, reg, len, Rw, enable, data_only, grp, swap, dataOut);
773773
return status;
774774
}
775775

776776
//Provided for backward-compatibility only. Please update to i2cControllerConfigurePeripheral and i2cControllerPeriph4Transaction.
777777
//https://www.oshwa.org/2020/06/29/a-resolution-to-redefine-spi-pin-names/
778778
ICM_20948_Status_e ICM_20948::i2cMasterConfigureSlave(uint8_t peripheral, uint8_t addr, uint8_t reg, uint8_t len, bool Rw, bool enable, bool data_only, bool grp, bool swap)
779779
{
780-
return (i2cControllerConfigurePeripheral(peripheral, addr, reg, len, Rw, enable, data_only, grp, swap));
780+
return (i2cControllerConfigurePeripheral(peripheral, addr, reg, len, Rw, enable, data_only, grp, swap, 0));
781781
}
782782

783783
ICM_20948_Status_e ICM_20948::i2cControllerPeriph4Transaction(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len, bool Rw, bool send_reg_addr)
@@ -856,7 +856,7 @@ ICM_20948_Status_e ICM_20948::startupDefault(bool minimal)
856856
return status;
857857
}
858858

859-
retval = startupMagnetometer();
859+
retval = startupMagnetometer(minimal); // Pass the minimal startup flag to startupMagnetometer
860860
if (retval != ICM_20948_Stat_Ok)
861861
{
862862
debugPrint(F("ICM_20948::startupDefault: startupMagnetometer returned: "));
@@ -956,6 +956,17 @@ ICM_20948_Status_e ICM_20948::writeMag(AK09916_Reg_Addr_e reg, uint8_t *pdata)
956956
return status;
957957
}
958958

959+
ICM_20948_Status_e ICM_20948::resetMag()
960+
{
961+
uint8_t SRST = 1;
962+
// SRST: Soft reset
963+
// “0”: Normal
964+
// “1”: Reset
965+
// When “1” is set, all registers are initialized. After reset, SRST bit turns to “0” automatically.
966+
status = i2cMasterSingleW(MAG_AK09916_I2C_ADDR, AK09916_REG_CNTL3, SRST);
967+
return status;
968+
}
969+
959970
// FIFO
960971

961972
ICM_20948_Status_e ICM_20948::enableFIFO(bool enable)
@@ -1185,13 +1196,15 @@ ICM_20948_Status_e ICM_20948_I2C::begin(TwoWire &wirePort, bool ad0val, uint8_t
11851196
return status;
11861197
}
11871198

1188-
ICM_20948_Status_e ICM_20948::startupMagnetometer(void)
1199+
ICM_20948_Status_e ICM_20948::startupMagnetometer(bool minimal)
11891200
{
11901201
ICM_20948_Status_e retval = ICM_20948_Stat_Ok;
11911202

11921203
i2cMasterPassthrough(false); //Do not connect the SDA/SCL pins to AUX_DA/AUX_CL
11931204
i2cMasterEnable(true);
11941205

1206+
resetMag();
1207+
11951208
//After a ICM reset the Mag sensor may stop responding over the I2C master
11961209
//Reset the Master I2C until it responds
11971210
uint8_t tries = 0;
@@ -1230,6 +1243,7 @@ ICM_20948_Status_e ICM_20948::startupMagnetometer(void)
12301243
//Set up magnetometer
12311244
AK09916_CNTL2_Reg_t reg;
12321245
reg.MODE = AK09916_mode_cont_100hz;
1246+
reg.reserved_0 = 0; // Make sure the unused bits are clear. Probably redundant, but prevents confusion when looking at the I2C traffic
12331247
retval = writeMag(AK09916_REG_CNTL2, (uint8_t *)&reg);
12341248
if (retval != ICM_20948_Stat_Ok)
12351249
{
@@ -1240,6 +1254,13 @@ ICM_20948_Status_e ICM_20948::startupMagnetometer(void)
12401254
return status;
12411255
}
12421256

1257+
//Return now if minimal is true. The mag will be configured manually for the DMP
1258+
if (minimal) // Return now if minimal is true
1259+
{
1260+
debugPrintln(F("ICM_20948::startupMagnetometer: minimal startup complete!"));
1261+
return status;
1262+
}
1263+
12431264
retval = i2cControllerConfigurePeripheral(0, MAG_AK09916_I2C_ADDR, AK09916_REG_ST1, 9, true, true, false, false, false);
12441265
if (retval != ICM_20948_Stat_Ok)
12451266
{

src/ICM_20948.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class ICM_20948
138138
ICM_20948_Status_e i2cMasterReset();
139139

140140
//Used for configuring peripherals 0-3
141-
ICM_20948_Status_e i2cControllerConfigurePeripheral(uint8_t peripheral, uint8_t addr, uint8_t reg, uint8_t len, bool Rw = true, bool enable = true, bool data_only = false, bool grp = false, bool swap = false);
141+
ICM_20948_Status_e i2cControllerConfigurePeripheral(uint8_t peripheral, uint8_t addr, uint8_t reg, uint8_t len, bool Rw = true, bool enable = true, bool data_only = false, bool grp = false, bool swap = false, uint8_t dataOut = 0);
142142
ICM_20948_Status_e i2cControllerPeriph4Transaction(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len, bool Rw, bool send_reg_addr = true);
143143

144144
//Provided for backward-compatibility only. Please update to i2cControllerConfigurePeripheral and i2cControllerPeriph4Transaction.
@@ -158,10 +158,11 @@ class ICM_20948
158158
ICM_20948_Status_e write(uint8_t reg, uint8_t *pdata, uint32_t len);
159159

160160
//Mag specific
161-
ICM_20948_Status_e startupMagnetometer(void);
161+
ICM_20948_Status_e startupMagnetometer(bool minimal = false); // If minimal is true, several startup steps are skipped. The mag then needs to be set up manually for the DMP.
162162
ICM_20948_Status_e magWhoIAm(void);
163163
uint8_t readMag(AK09916_Reg_Addr_e reg);
164164
ICM_20948_Status_e writeMag(AK09916_Reg_Addr_e reg, uint8_t *pdata);
165+
ICM_20948_Status_e resetMag();
165166

166167
//FIFO
167168
ICM_20948_Status_e enableFIFO(bool enable = true);

src/util/AK09916_REGISTERS.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ typedef enum
77
{
88
AK09916_REG_WIA1 = 0x00,
99
AK09916_REG_WIA2,
10-
// discontinuity
10+
AK09916_REG_RSV1,
11+
AK09916_REG_RSV2, // Reserved register. We start reading here when using the DMP. Secret sauce...
12+
// discontinuity - containing another nine reserved registers? Secret sauce...
1113
AK09916_REG_ST1 = 0x10,
1214
AK09916_REG_HXL,
1315
AK09916_REG_HXH,

0 commit comments

Comments
 (0)