Skip to content

Commit f63c33b

Browse files
committed
Updates after reading the InvenSense App Note
1 parent d9d1bf9 commit f63c33b

File tree

10 files changed

+356
-370
lines changed

10 files changed

+356
-370
lines changed

examples/Arduino/Example5_DMP/Example5_DMP.ino

Lines changed: 119 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66
* Based on original code by:
77
* Owen Lyke @ SparkFun Electronics
88
* Original Creation Date: April 17 2019
9+
*
10+
* ** This example is based on the InvenSense Application Note "Programming Sequence for DMP Hardware Functions".
11+
* ** We are grateful to InvenSense for providing this.
12+
*
13+
* ** Important note: by default the DMP functionality is disabled in the library. This is to save program memory.
14+
* ** The DMP firmware takes up 14290 Bytes of program memory. To use the DMP, you will need to:
15+
* ** Edit ICM_20948_C.h
16+
* ** Uncomment line 29: #define ICM_20948_USE_DMP
17+
* ** Save changes
18+
* ** If you are using Windows, you can find ICM_20948_C.h in Documents\Arduino\libraries\SparkFun_ICM-20948_ArduinoLibrary\src\util
919
*
1020
* Please see License.md for the license information.
1121
*
@@ -59,6 +69,8 @@ void setup() {
5969
bool initialized = false;
6070
while( !initialized ){
6171

72+
// Initialize the ICM-20948
73+
// If the DMP is enabled, .begin performs a minimal startup. We need to configure the sample mode etc. manually.
6274
#ifdef USE_SPI
6375
myICM.begin( CS_PIN, SPI_PORT );
6476
#else
@@ -77,35 +89,121 @@ void setup() {
7789

7890
SERIAL_PORT.println("Device connected!");
7991

80-
myICM.enableDMP(); // Enable the DMP
92+
// The ICM-20948 is awake and ready but hasn't been configured. Let's step through the configuration
8193

82-
if( myICM.status == ICM_20948_Stat_Ok )
83-
SERIAL_PORT.println("DMP enabled!");
84-
else
85-
{
86-
SERIAL_PORT.print("Enable DMP failed! Status is: ");
87-
SERIAL_PORT.println( myICM.statusString() );
88-
}
89-
90-
myICM.enableFIFO(); // Enable the FIFO
94+
bool success = true; // Use success to show if the configuration was successful
9195

92-
if( myICM.status == ICM_20948_Stat_Ok )
93-
SERIAL_PORT.println("FIFO enabled!");
94-
else
95-
{
96-
SERIAL_PORT.print("Enable FIFO failed! Status is: ");
97-
SERIAL_PORT.println( myICM.statusString() );
98-
}
96+
// Configure clock source through PWR_MGMT_1
97+
// Auto selects the best available clock source – PLL if ready, else use the Internal oscillator
98+
success &= (myICM.setClockSource(ICM_20948_Clock_Auto) == ICM_20948_Stat_Ok); // This is shorthand: success will be set to false if setClockSource fails
99+
100+
// Enable accel and gyro sensors through PWR_MGMT_2
101+
// Enable Accelerometer (all axes) and Gyroscope (all axes) by writing zero to PWR_MGMT_2
102+
uint8_t zero = 0;
103+
success &= (myICM.write(AGB0_REG_PWR_MGMT_2, &zero, 1) == ICM_20948_Stat_Ok); // Write one byte to the PWR_MGMT_2 register
104+
105+
// Configure Gyro/Accel in Low power mode with LP_CONFIG
106+
success &= (myICM.setSampleMode( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Cycled ) == ICM_20948_Stat_Ok);
107+
108+
// Set Gyro FSR (Full scale range) to 2000dps through GYRO_CONFIG_1
109+
// Set Accel FSR (Full scale range) to 4g through ACCEL_CONFIG
110+
ICM_20948_fss_t myFSS; // This uses a "Full Scale Settings" structure that can contain values for all configurable sensors
111+
myFSS.a = gpm4; // (ICM_20948_ACCEL_CONFIG_FS_SEL_e)
112+
// gpm2
113+
// gpm4
114+
// gpm8
115+
// gpm16
116+
myFSS.g = dps2000; // (ICM_20948_GYRO_CONFIG_1_FS_SEL_e)
117+
// dps250
118+
// dps500
119+
// dps1000
120+
// dps2000
121+
success &= (myICM.setFullScale( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myFSS ) == ICM_20948_Stat_Ok);
122+
123+
// Enable interrupt for FIFO overflow from FIFOs through INT_ENABLE_2
124+
//success &= (myICM.intEnableOverflowFIFO( 0x1F ) == ICM_20948_Stat_Ok); // Enable the interrupt on all FIFOs
125+
126+
// Turn off what goes into the FIFO through FIFO_EN, FIFO_EN_2
127+
// Stop the peripheral data from being written to the FIFO by writing zero to FIFO_EN_1
128+
success &= (myICM.write(AGB0_REG_FIFO_EN_1, &zero, 1) == ICM_20948_Stat_Ok);
129+
// Stop the accelerometer, gyro and temperature data from being written to the FIFO by writing zero to FIFO_EN_2
130+
success &= (myICM.write(AGB0_REG_FIFO_EN_2, &zero, 1) == ICM_20948_Stat_Ok);
131+
132+
// Turn off data ready interrupt through INT_ENABLE_1
133+
success &= (myICM.intEnableRawDataReady(false) == ICM_20948_Stat_Ok);
134+
135+
// Reset FIFO through FIFO_RST
136+
success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok);
137+
138+
// Set gyro sample rate divider with GYRO_SMPLRT_DIV
139+
// Set accel sample rate divider with ACCEL_SMPLRT_DIV_2
140+
ICM_20948_smplrt_t mySmplrt;
141+
mySmplrt.g = 43; // ODR is computed as follows: 1.1 kHz/(1+GYRO_SMPLRT_DIV[7:0]). 43 = 25Hz
142+
mySmplrt.a = 44; // ODR is computed as follows: 1.125 kHz/(1+ACCEL_SMPLRT_DIV[11:0]). 44 = 25Hz
143+
myICM.setSampleRate( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), mySmplrt );
99144

100-
myICM.enableSensor(INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR); // Enable the Game Rotation Vector
145+
// Setup DMP start address through PRGM_STRT_ADDRH/PRGM_STRT_ADDRL
146+
success &= (myICM.setDMPstartAddress() == ICM_20948_Stat_Ok); // Defaults to DMP_START_ADDRESS
101147

102-
if( myICM.status == ICM_20948_Stat_Ok )
103-
SERIAL_PORT.println("INV_ICM20948_SENSOR_ROTATION_VECTOR enabled!");
148+
// Now load the DMP firmware
149+
success &= (myICM.loadDMPFirmware() == ICM_20948_Stat_Ok);
150+
151+
// Write the 2 byte Firmware Start Value to ICM PRGM_STRT_ADDRH/PRGM_STRT_ADDRL
152+
success &= (myICM.setDMPstartAddress() == ICM_20948_Stat_Ok); // Defaults to DMP_START_ADDRESS
153+
154+
// Configure Accel scaling to DMP
155+
// The DMP scales accel raw data internally to align 1g as 2^25
156+
// In order to align internal accel raw data 2^25 = 1g write 0x4000000 when FSR is 4g
157+
const unsigned char accScale[4] = {0x40, 0x00, 0x00, 0x00};
158+
success &= (myICM.writeDMPmems(ACC_SCALE, 4, &accScale[0]) == ICM_20948_Stat_Ok); // Write 0x4000000 to ACC_SCALE DMP register
159+
// In order to output hardware unit data as configured FSR write 0x40000 when FSR is 4g
160+
const unsigned char accScale2[4] = {0x00, 0x04, 0x00, 0x00};
161+
success &= (myICM.writeDMPmems(ACC_SCALE2, 4, &accScale2[0]) == ICM_20948_Stat_Ok); // Write 0x40000 to ACC_SCALE2 DMP register
162+
163+
// Configure Compass mount matrix and scale to DMP
164+
// The mount matrix write to DMP register is used to align the compass axes with accel/gyro.
165+
// This mechanism is also used to convert hardware unit to uT. The value is expressed as 1uT = 2^30.
166+
// Each compass axis will be converted as below:
167+
// X = raw_x * CPASS_MTX_00 + raw_y * CPASS_MTX_01 + raw_z * CPASS_MTX_02
168+
// Y = raw_x * CPASS_MTX_10 + raw_y * CPASS_MTX_11 + raw_z * CPASS_MTX_12
169+
// Z = raw_x * CPASS_MTX_20 + raw_y * CPASS_MTX_21 + raw_z * CPASS_MTX_22
170+
const unsigned char mountMultiplierZero[4] = {0x00, 0x00, 0x00, 0x00};
171+
const unsigned char mountMultiplierPlus[4] = {0x40, 0x00, 0x00, 0x00};
172+
const unsigned char mountMultiplierMinus[4] = {0xC0, 0x00, 0x00, 0x00};
173+
success &= (myICM.writeDMPmems(CPASS_MTX_00, 4, &mountMultiplierPlus[0]) == ICM_20948_Stat_Ok);
174+
success &= (myICM.writeDMPmems(CPASS_MTX_01, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
175+
success &= (myICM.writeDMPmems(CPASS_MTX_02, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
176+
success &= (myICM.writeDMPmems(CPASS_MTX_10, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
177+
success &= (myICM.writeDMPmems(CPASS_MTX_11, 4, &mountMultiplierPlus[0]) == ICM_20948_Stat_Ok);
178+
success &= (myICM.writeDMPmems(CPASS_MTX_12, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
179+
success &= (myICM.writeDMPmems(CPASS_MTX_20, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
180+
success &= (myICM.writeDMPmems(CPASS_MTX_21, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
181+
success &= (myICM.writeDMPmems(CPASS_MTX_22, 4, &mountMultiplierPlus[0]) == ICM_20948_Stat_Ok);
182+
183+
// Enable the FIFO
184+
success &= (myICM.enableFIFO() == ICM_20948_Stat_Ok);
185+
186+
// Reset FIFO
187+
success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok);
188+
189+
// Enable the DMP
190+
success &= (myICM.enableDMP() == ICM_20948_Stat_Ok);
191+
192+
// Reset DMP
193+
success &= (myICM.resetDMP() == ICM_20948_Stat_Ok);
194+
195+
// Enable DMP interrupt
196+
//success &= (myICM.intEnableDMP(true) == ICM_20948_Stat_Ok);
197+
198+
// Check success
199+
if( success )
200+
SERIAL_PORT.println("DMP enabled!");
104201
else
105202
{
106-
SERIAL_PORT.print("INV_ICM20948_SENSOR_ROTATION_VECTOR failed! Status is: ");
203+
SERIAL_PORT.print("Enable DMP failed! Status is: ");
107204
SERIAL_PORT.println( myICM.statusString() );
108205
}
206+
109207
}
110208

111209
void loop()

src/ICM_20948.cpp

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ uint8_t ICM_20948::i2cMasterSingleR(uint8_t addr, uint8_t reg)
769769
return data;
770770
}
771771

772-
ICM_20948_Status_e ICM_20948::startupDefault(void)
772+
ICM_20948_Status_e ICM_20948::startupDefault(bool minimal)
773773
{
774774
ICM_20948_Status_e retval = ICM_20948_Stat_Ok;
775775

@@ -783,19 +783,6 @@ ICM_20948_Status_e ICM_20948::startupDefault(void)
783783
return status;
784784
}
785785

786-
if (_device._dmp_firmware_available == true) // Should we attempt to load the DMP firmware?
787-
{
788-
retval = loadDMPFirmware();
789-
if (retval != ICM_20948_Stat_Ok)
790-
{
791-
debugPrint(F("ICM_20948::startupDefault: loadDMPFirmware returned: "));
792-
debugPrintStatus(retval);
793-
debugPrintln(F(""));
794-
status = retval;
795-
return status;
796-
}
797-
}
798-
799786
retval = swReset();
800787
if (retval != ICM_20948_Stat_Ok)
801788
{
@@ -827,6 +814,12 @@ ICM_20948_Status_e ICM_20948::startupDefault(void)
827814
return status;
828815
}
829816

817+
if (minimal) // Return now if minimal is true
818+
{
819+
debugPrintln(F("ICM_20948::startupDefault: minimal startup complete!"));
820+
return status;
821+
}
822+
830823
retval = setSampleMode((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Continuous); // options: ICM_20948_Sample_Mode_Continuous or ICM_20948_Sample_Mode_Cycled
831824
if (retval != ICM_20948_Stat_Ok)
832825
{
@@ -872,6 +865,7 @@ ICM_20948_Status_e ICM_20948::startupDefault(void)
872865
status = retval;
873866
return status;
874867
}
868+
875869
retval = enableDLPF(ICM_20948_Internal_Gyr, false);
876870
if (retval != ICM_20948_Stat_Ok)
877871
{
@@ -881,6 +875,7 @@ ICM_20948_Status_e ICM_20948::startupDefault(void)
881875
status = retval;
882876
return status;
883877
}
878+
884879
retval = startupMagnetometer();
885880
if (retval != ICM_20948_Stat_Ok)
886881
{
@@ -891,7 +886,7 @@ ICM_20948_Status_e ICM_20948::startupDefault(void)
891886
return status;
892887
}
893888

894-
return status; // Leave status unchanged - return whatever it was when startupDefault was called
889+
return status;
895890
}
896891

897892
// direct read/write
@@ -956,8 +951,12 @@ ICM_20948_Status_e ICM_20948::readFIFO(uint8_t *data)
956951

957952
ICM_20948_Status_e ICM_20948::enableDMP(bool enable)
958953
{
959-
status = ICM_20948_enable_DMP(&_device, enable);
960-
return status;
954+
if (_device._dmp_firmware_available == true) // Should we attempt to enable the DMP?
955+
{
956+
status = ICM_20948_enable_DMP(&_device, enable);
957+
return status;
958+
}
959+
return ICM_20948_Stat_DMPNotSupported;
961960
}
962961

963962
ICM_20948_Status_e ICM_20948::resetDMP(void)
@@ -968,14 +967,52 @@ ICM_20948_Status_e ICM_20948::resetDMP(void)
968967

969968
ICM_20948_Status_e ICM_20948::loadDMPFirmware(void)
970969
{
971-
status = ICM_20948_firmware_load(&_device);
972-
return status;
970+
if (_device._dmp_firmware_available == true) // Should we attempt to load the DMP firmware?
971+
{
972+
status = ICM_20948_firmware_load(&_device);
973+
return status;
974+
}
975+
return ICM_20948_Stat_DMPNotSupported;
976+
}
977+
978+
ICM_20948_Status_e ICM_20948::setDMPstartAddress(unsigned short address)
979+
{
980+
if (_device._dmp_firmware_available == true) // Should we attempt to set the start address?
981+
{
982+
status = ICM_20948_set_dmp_start_address(&_device, address);
983+
return status;
984+
}
985+
return ICM_20948_Stat_DMPNotSupported;
973986
}
974987

975988
ICM_20948_Status_e ICM_20948::enableSensor(enum inv_icm20948_sensor sensor, bool enable)
976989
{
990+
if (_device._dmp_firmware_available == true) // Should we attempt to set the start address?
991+
{
977992
status = inv_icm20948_enable_sensor(&_device, sensor, enable == true ? 1 : 0);
978993
return status;
994+
}
995+
return ICM_20948_Stat_DMPNotSupported;
996+
}
997+
998+
ICM_20948_Status_e ICM_20948::writeDMPmems(unsigned short reg, unsigned int length, const unsigned char *data)
999+
{
1000+
if (_device._dmp_firmware_available == true) // Should we attempt to write to the DMP?
1001+
{
1002+
status = inv_icm20948_write_mems(&_device, reg, length, data);
1003+
return status;
1004+
}
1005+
return ICM_20948_Stat_DMPNotSupported;
1006+
}
1007+
1008+
ICM_20948_Status_e ICM_20948::readDMPmems(unsigned short reg, unsigned int length, unsigned char *data)
1009+
{
1010+
if (_device._dmp_firmware_available == true) // Should we attempt to read from the DMP?
1011+
{
1012+
status = inv_icm20948_read_mems(&_device, reg, length, data);
1013+
return status;
1014+
}
1015+
return ICM_20948_Stat_DMPNotSupported;
9791016
}
9801017

9811018
// I2C
@@ -1027,7 +1064,8 @@ ICM_20948_Status_e ICM_20948_I2C::begin(TwoWire &wirePort, bool ad0val, uint8_t
10271064
_device._firmware_loaded = false; // Initialize _firmware_loaded
10281065

10291066
// Perform default startup
1030-
status = startupDefault();
1067+
// Do a minimal startupDefault if using the DMP. User can always call startupDefault(false) manually if required.
1068+
status = startupDefault(_device._dmp_firmware_available);
10311069
if (status != ICM_20948_Stat_Ok)
10321070
{
10331071
debugPrint(F("ICM_20948_I2C::begin: startupDefault returned: "));
@@ -1205,7 +1243,8 @@ ICM_20948_Status_e ICM_20948_SPI::begin(uint8_t csPin, SPIClass &spiPort, uint32
12051243
_device._firmware_loaded = false; // Initialize _firmware_loaded
12061244

12071245
// Perform default startup
1208-
status = startupDefault();
1246+
// Do a minimal startupDefault if using the DMP. User can always call startupDefault(false) manually if required.
1247+
status = startupDefault(_device._dmp_firmware_available);
12091248
if (status != ICM_20948_Stat_Ok)
12101249
{
12111250
debugPrint(F("ICM_20948_SPI::begin: startupDefault returned: "));

src/ICM_20948.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ class ICM_20948
151151
uint8_t i2cMasterSingleR(uint8_t addr, uint8_t reg);
152152

153153
// Default Setup
154-
ICM_20948_Status_e startupDefault(void);
154+
ICM_20948_Status_e startupDefault(bool minimal = false); // If minimal is true, several startup steps are skipped. If ICM_20948_USE_DMP is defined, .begin will call startupDefault with minimal set to true.
155155

156156
// direct read/write
157157
ICM_20948_Status_e read(uint8_t reg, uint8_t *pdata, uint32_t len);
@@ -174,7 +174,10 @@ class ICM_20948
174174
ICM_20948_Status_e enableDMP(bool enable = true);
175175
ICM_20948_Status_e resetDMP(void);
176176
ICM_20948_Status_e loadDMPFirmware(void);
177+
ICM_20948_Status_e setDMPstartAddress(unsigned short address = DMP_START_ADDRESS);
177178
ICM_20948_Status_e enableSensor(enum inv_icm20948_sensor sensor, bool enable = true);
179+
ICM_20948_Status_e writeDMPmems(unsigned short reg, unsigned int length, const unsigned char *data);
180+
ICM_20948_Status_e readDMPmems(unsigned short reg, unsigned int length, unsigned char *data);
178181
};
179182

180183
// I2C

0 commit comments

Comments
 (0)