Skip to content

Commit 3935877

Browse files
authored
Merge pull request #31 from sparkfun/DMP
Adding (limited) DMP functionality
2 parents 8402d6f + 390853e commit 3935877

File tree

7 files changed

+2469
-35
lines changed

7 files changed

+2469
-35
lines changed
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
/****************************************************************
2+
* Example5_DMP_Quat9_Orientation.ino
3+
* ICM 20948 Arduino Library Demo
4+
* Initialize the DMP based on the TDK InvenSense ICM20948_eMD_nucleo_1.0 example-icm20948
5+
* Paul Clark, February 15th 2021
6+
* Based on original code by:
7+
* Owen Lyke @ SparkFun Electronics
8+
* Original Creation Date: April 17 2019
9+
*
10+
* ** This example is based on InvenSense's _confidential_ Application Note "Programming Sequence for DMP Hardware Functions".
11+
* ** We are grateful to InvenSense for sharing this with us.
12+
*
13+
* ** Important note: by default the DMP functionality is disabled in the library
14+
* ** as the DMP firmware takes up 14290 Bytes of program memory.
15+
* ** To use the DMP, you will need to:
16+
* ** Edit ICM_20948_C.h
17+
* ** Uncomment line 29: #define ICM_20948_USE_DMP
18+
* ** Save changes
19+
* ** If you are using Windows, you can find ICM_20948_C.h in:
20+
* ** Documents\Arduino\libraries\SparkFun_ICM-20948_ArduinoLibrary\src\util
21+
*
22+
* Please see License.md for the license information.
23+
*
24+
* Distributed as-is; no warranty is given.
25+
***************************************************************/
26+
27+
#include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU
28+
29+
//#define USE_SPI // Uncomment this to use SPI
30+
31+
#define SERIAL_PORT Serial
32+
33+
#define SPI_PORT SPI // Your desired SPI port. Used only when "USE_SPI" is defined
34+
#define CS_PIN 2 // Which pin you connect CS to. Used only when "USE_SPI" is defined
35+
36+
#define WIRE_PORT Wire // Your desired Wire port. Used when "USE_SPI" is not defined
37+
#define AD0_VAL 1 // The value of the last bit of the I2C address.
38+
// On the SparkFun 9DoF IMU breakout the default is 1, and when
39+
// the ADR jumper is closed the value becomes 0
40+
41+
#ifdef USE_SPI
42+
ICM_20948_SPI myICM; // If using SPI create an ICM_20948_SPI object
43+
#else
44+
ICM_20948_I2C myICM; // Otherwise create an ICM_20948_I2C object
45+
#endif
46+
47+
48+
void setup() {
49+
50+
SERIAL_PORT.begin(115200); // Start the serial console
51+
SERIAL_PORT.println(F("ICM-20948 Example"));
52+
53+
delay(100);
54+
55+
while (SERIAL_PORT.available()) // Make sure the serial RX buffer is empty
56+
SERIAL_PORT.read();
57+
58+
SERIAL_PORT.println(F("Press any key to continue..."));
59+
60+
while (!SERIAL_PORT.available()) // Wait for the user to press a key (send any serial character)
61+
;
62+
63+
#ifdef USE_SPI
64+
SPI_PORT.begin();
65+
#else
66+
WIRE_PORT.begin();
67+
WIRE_PORT.setClock(400000);
68+
#endif
69+
70+
//myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
71+
72+
bool initialized = false;
73+
while( !initialized ){
74+
75+
// Initialize the ICM-20948
76+
// If the DMP is enabled, .begin performs a minimal startup. We need to configure the sample mode etc. manually.
77+
#ifdef USE_SPI
78+
myICM.begin( CS_PIN, SPI_PORT );
79+
#else
80+
myICM.begin( WIRE_PORT, AD0_VAL );
81+
#endif
82+
83+
SERIAL_PORT.print( F("Initialization of the sensor returned: ") );
84+
SERIAL_PORT.println( myICM.statusString() );
85+
if( myICM.status != ICM_20948_Stat_Ok ){
86+
SERIAL_PORT.println( "Trying again..." );
87+
delay(500);
88+
}else{
89+
initialized = true;
90+
}
91+
}
92+
93+
SERIAL_PORT.println("Device connected!");
94+
95+
// The ICM-20948 is awake and ready but hasn't been configured. Let's step through the configuration
96+
// sequence from InvenSense's _confidential_ Application Note "Programming Sequence for DMP Hardware Functions".
97+
98+
bool success = true; // Use success to show if the configuration was successful
99+
100+
// Configure clock source through PWR_MGMT_1
101+
// ICM_20948_Clock_Auto selects the best available clock source – PLL if ready, else use the Internal oscillator
102+
success &= (myICM.setClockSource(ICM_20948_Clock_Auto) == ICM_20948_Stat_Ok); // This is shorthand: success will be set to false if setClockSource fails
103+
104+
// Enable accel and gyro sensors through PWR_MGMT_2
105+
// Enable Accelerometer (all axes) and Gyroscope (all axes) by writing zero to PWR_MGMT_2
106+
uint8_t zero = 0;
107+
success &= (myICM.write(AGB0_REG_PWR_MGMT_2, &zero, 1) == ICM_20948_Stat_Ok); // Write one byte to the PWR_MGMT_2 register
108+
109+
// Configure Gyro/Accel in Low Power Mode (cycled) with LP_CONFIG
110+
success &= (myICM.setSampleMode( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Cycled ) == ICM_20948_Stat_Ok);
111+
112+
// Set Gyro FSR (Full scale range) to 2000dps through GYRO_CONFIG_1
113+
// Set Accel FSR (Full scale range) to 4g through ACCEL_CONFIG
114+
ICM_20948_fss_t myFSS; // This uses a "Full Scale Settings" structure that can contain values for all configurable sensors
115+
myFSS.a = gpm4; // (ICM_20948_ACCEL_CONFIG_FS_SEL_e)
116+
// gpm2
117+
// gpm4
118+
// gpm8
119+
// gpm16
120+
myFSS.g = dps2000; // (ICM_20948_GYRO_CONFIG_1_FS_SEL_e)
121+
// dps250
122+
// dps500
123+
// dps1000
124+
// dps2000
125+
success &= (myICM.setFullScale( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myFSS ) == ICM_20948_Stat_Ok);
126+
127+
// Enable interrupt for FIFO overflow from FIFOs through INT_ENABLE_2
128+
// If we see this interrupt, we'll need to reset the FIFO
129+
//success &= (myICM.intEnableOverflowFIFO( 0x1F ) == ICM_20948_Stat_Ok); // Enable the interrupt on all FIFOs
130+
131+
// Turn off what goes into the FIFO through FIFO_EN_1, FIFO_EN_2
132+
// Stop the peripheral data from being written to the FIFO by writing zero to FIFO_EN_1
133+
success &= (myICM.write(AGB0_REG_FIFO_EN_1, &zero, 1) == ICM_20948_Stat_Ok);
134+
// Stop the accelerometer, gyro and temperature data from being written to the FIFO by writing zero to FIFO_EN_2
135+
success &= (myICM.write(AGB0_REG_FIFO_EN_2, &zero, 1) == ICM_20948_Stat_Ok);
136+
137+
// Turn off data ready interrupt through INT_ENABLE_1
138+
success &= (myICM.intEnableRawDataReady(false) == ICM_20948_Stat_Ok);
139+
140+
// Reset FIFO through FIFO_RST
141+
success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok);
142+
143+
// Set gyro sample rate divider with GYRO_SMPLRT_DIV
144+
// Set accel sample rate divider with ACCEL_SMPLRT_DIV_2
145+
ICM_20948_smplrt_t mySmplrt;
146+
mySmplrt.g = 43; // ODR is computed as follows: 1.1 kHz/(1+GYRO_SMPLRT_DIV[7:0]). 43 = 25Hz
147+
mySmplrt.a = 44; // ODR is computed as follows: 1.125 kHz/(1+ACCEL_SMPLRT_DIV[11:0]). 44 = 25Hz
148+
//myICM.setSampleRate( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), mySmplrt ); // ** Note: comment this line to leave the sample rates at the maximum **
149+
150+
// Setup DMP start address through PRGM_STRT_ADDRH/PRGM_STRT_ADDRL
151+
success &= (myICM.setDMPstartAddress() == ICM_20948_Stat_Ok); // Defaults to DMP_START_ADDRESS
152+
153+
// Now load the DMP firmware
154+
success &= (myICM.loadDMPFirmware() == ICM_20948_Stat_Ok);
155+
156+
// Write the 2 byte Firmware Start Value to ICM PRGM_STRT_ADDRH/PRGM_STRT_ADDRL
157+
success &= (myICM.setDMPstartAddress() == ICM_20948_Stat_Ok); // Defaults to DMP_START_ADDRESS
158+
159+
// Configure Accel scaling to DMP
160+
// The DMP scales accel raw data internally to align 1g as 2^25
161+
// In order to align internal accel raw data 2^25 = 1g write 0x4000000 when FSR is 4g
162+
const unsigned char accScale[4] = {0x40, 0x00, 0x00, 0x00};
163+
success &= (myICM.writeDMPmems(ACC_SCALE, 4, &accScale[0]) == ICM_20948_Stat_Ok); // Write 0x4000000 to ACC_SCALE DMP register
164+
// In order to output hardware unit data as configured FSR write 0x40000 when FSR is 4g
165+
const unsigned char accScale2[4] = {0x00, 0x04, 0x00, 0x00};
166+
success &= (myICM.writeDMPmems(ACC_SCALE2, 4, &accScale2[0]) == ICM_20948_Stat_Ok); // Write 0x40000 to ACC_SCALE2 DMP register
167+
168+
// Configure Compass mount matrix and scale to DMP
169+
// The mount matrix write to DMP register is used to align the compass axes with accel/gyro.
170+
// This mechanism is also used to convert hardware unit to uT. The value is expressed as 1uT = 2^30.
171+
// Each compass axis will be converted as below:
172+
// X = raw_x * CPASS_MTX_00 + raw_y * CPASS_MTX_01 + raw_z * CPASS_MTX_02
173+
// Y = raw_x * CPASS_MTX_10 + raw_y * CPASS_MTX_11 + raw_z * CPASS_MTX_12
174+
// Z = raw_x * CPASS_MTX_20 + raw_y * CPASS_MTX_21 + raw_z * CPASS_MTX_22
175+
// Magnetometer full scale is +/- 4900uT so _I think_ we need to multiply by 2^30 / 4900 = 0x000357FA
176+
// The magnetometer Y and Z axes are reversed compared to the accelerometer so we'll invert those
177+
const unsigned char mountMultiplierZero[4] = {0x00, 0x00, 0x00, 0x00};
178+
const unsigned char mountMultiplierPlus[4] = {0x00, 0x03, 0x57, 0xFA};
179+
const unsigned char mountMultiplierMinus[4] = {0xFF, 0xFC, 0xA8, 0x05};
180+
success &= (myICM.writeDMPmems(CPASS_MTX_00, 4, &mountMultiplierPlus[0]) == ICM_20948_Stat_Ok);
181+
success &= (myICM.writeDMPmems(CPASS_MTX_01, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
182+
success &= (myICM.writeDMPmems(CPASS_MTX_02, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
183+
success &= (myICM.writeDMPmems(CPASS_MTX_10, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
184+
success &= (myICM.writeDMPmems(CPASS_MTX_11, 4, &mountMultiplierMinus[0]) == ICM_20948_Stat_Ok);
185+
success &= (myICM.writeDMPmems(CPASS_MTX_12, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
186+
success &= (myICM.writeDMPmems(CPASS_MTX_20, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
187+
success &= (myICM.writeDMPmems(CPASS_MTX_21, 4, &mountMultiplierZero[0]) == ICM_20948_Stat_Ok);
188+
success &= (myICM.writeDMPmems(CPASS_MTX_22, 4, &mountMultiplierMinus[0]) == ICM_20948_Stat_Ok);
189+
190+
// Enable DMP interrupt
191+
// This would be the most efficient way of getting the DMP data, instead of polling the FIFO
192+
//success &= (myICM.intEnableDMP(true) == ICM_20948_Stat_Ok);
193+
194+
// DMP sensor options are defined in ICM_20948_DMP.h
195+
// INV_ICM20948_SENSOR_ACCELEROMETER
196+
// INV_ICM20948_SENSOR_GYROSCOPE
197+
// INV_ICM20948_SENSOR_RAW_ACCELEROMETER
198+
// INV_ICM20948_SENSOR_RAW_GYROSCOPE
199+
// INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED
200+
// INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED
201+
// INV_ICM20948_SENSOR_ACTIVITY_CLASSIFICATON
202+
// INV_ICM20948_SENSOR_STEP_DETECTOR
203+
// INV_ICM20948_SENSOR_STEP_COUNTER
204+
// INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR
205+
// INV_ICM20948_SENSOR_ROTATION_VECTOR
206+
// INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR
207+
// INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD
208+
// INV_ICM20948_SENSOR_WAKEUP_SIGNIFICANT_MOTION
209+
// INV_ICM20948_SENSOR_FLIP_PICKUP
210+
// INV_ICM20948_SENSOR_WAKEUP_TILT_DETECTOR
211+
// INV_ICM20948_SENSOR_GRAVITY
212+
// INV_ICM20948_SENSOR_LINEAR_ACCELERATION
213+
// INV_ICM20948_SENSOR_ORIENTATION
214+
215+
// Enable the DMP orientation sensor
216+
success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_ORIENTATION) == ICM_20948_Stat_Ok);
217+
218+
// Configuring DMP to output data at multiple ODRs:
219+
// DMP is capable of outputting multiple sensor data at different rates to FIFO.
220+
// Set the DMP Output Data Rate for Quat9 to 12Hz.
221+
success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Quat9, 12) == ICM_20948_Stat_Ok);
222+
223+
// Enable the FIFO
224+
success &= (myICM.enableFIFO() == ICM_20948_Stat_Ok);
225+
226+
// Enable the DMP
227+
success &= (myICM.enableDMP() == ICM_20948_Stat_Ok);
228+
229+
// Reset DMP
230+
success &= (myICM.resetDMP() == ICM_20948_Stat_Ok);
231+
232+
// Reset FIFO
233+
success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok);
234+
235+
// Check success
236+
if( success )
237+
SERIAL_PORT.println("DMP enabled!");
238+
else
239+
{
240+
SERIAL_PORT.println("Enable DMP failed!");
241+
}
242+
243+
}
244+
245+
void loop()
246+
{
247+
// Read any DMP data waiting in the FIFO
248+
// Note:
249+
// readDMPdataFromFIFO will return ICM_20948_Stat_FIFONoDataAvail if no data or incomplete data is available.
250+
// If data is available, readDMPdataFromFIFO will attempt to read _one_ frame of DMP data.
251+
// readDMPdataFromFIFO will return ICM_20948_Stat_Ok if a valid frame was read.
252+
// readDMPdataFromFIFO will return ICM_20948_Stat_FIFOMoreDataAvail if a valid frame was read _and_ the FIFO contains more (unread) data.
253+
icm_20948_DMP_data_t data;
254+
myICM.readDMPdataFromFIFO(&data);
255+
256+
if(( myICM.status == ICM_20948_Stat_Ok ) || ( myICM.status == ICM_20948_Stat_FIFOMoreDataAvail )) // Was valid data available?
257+
{
258+
//SERIAL_PORT.print(F("Received data! Header: 0x")); // Print the header in HEX so we can see what data is arriving in the FIFO
259+
//if ( data.header < 0x1000) SERIAL_PORT.print( "0" ); // Pad the zeros
260+
//if ( data.header < 0x100) SERIAL_PORT.print( "0" );
261+
//if ( data.header < 0x10) SERIAL_PORT.print( "0" );
262+
//SERIAL_PORT.println( data.header, HEX );
263+
264+
if ( (data.header & DMP_header_bitmap_Quat9) > 0 ) // We have asked for orientation data so we should receive Quat9
265+
{
266+
// Q0 value is computed from this equation: Q0^2 + Q1^2 + Q2^2 + Q3^2 = 1.
267+
// In case of drift, the sum will not add to 1, therefore, quaternion data need to be corrected with right bias values.
268+
// The quaternion data is scaled by 2^30.
269+
270+
//SERIAL_PORT.printf("Quat9 data is: Q1:%ld Q2:%ld Q3:%ld Accuracy:%d\r\n", data.Quat9.Data.Q1, data.Quat9.Data.Q2, data.Quat9.Data.Q3, data.Quat9.Data.Accuracy);
271+
272+
// Scale to +/- 1
273+
float q1 = ((float)data.Quat9.Data.Q1) / 1073741824.0; // Convert to float. Divide by 2^30
274+
float q2 = ((float)data.Quat9.Data.Q2) / 1073741824.0; // Convert to float. Divide by 2^30
275+
float q3 = ((float)data.Quat9.Data.Q3) / 1073741824.0; // Convert to float. Divide by 2^30
276+
277+
SERIAL_PORT.printf("Q1:%.3f Q2:%.3f Q3:%.3f\r\n", q1, q2, q3);
278+
}
279+
}
280+
281+
if ( myICM.status != ICM_20948_Stat_FIFOMoreDataAvail ) // If more data is available then we should read it right away - and not delay
282+
{
283+
delay(10);
284+
}
285+
}

0 commit comments

Comments
 (0)