Skip to content

Commit 7f1f050

Browse files
authored
Add files via upload
1 parent 252a610 commit 7f1f050

File tree

1 file changed

+339
-1
lines changed

1 file changed

+339
-1
lines changed
Lines changed: 339 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,339 @@
1-
1+
/****************************************************************
2+
* Example3_Interrupts.ino
3+
* ICM 20948 Arduino Library Demo
4+
* Builds on Example2_Advanced.ino to set up interrupts when data is ready
5+
* Owen Lyke @ SparkFun Electronics
6+
* Original Creation Date: June 5 2019
7+
*
8+
* For this example you must connect the interrupt pin "INT" on the breakout
9+
* board to the pin specified by "INT_PIN" on your microcontroller.
10+
*
11+
* This code is beerware; if you see me (or any other SparkFun employee) at the
12+
* local, and you've found our code helpful, please buy us a round!
13+
*
14+
* Distributed as-is; no warranty is given.
15+
***************************************************************/
16+
#include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU
17+
18+
//#define USE_SPI // Uncomment this to use SPI
19+
20+
#define SERIAL_PORT Serial
21+
22+
#define INT_PIN 2 // Make sure to connect this pin on your uC to the "INT" pin on the ICM-20948 breakout
23+
//#define LED_PIN 13
24+
#define LED_PIN LED_BUILTIN
25+
#define BUFFER_SAMPLE_NUM 32
26+
27+
#define SPI_PORT SPI // Your desired SPI port. Used only when "USE_SPI" is defined
28+
#define SPI_FREQ 5000000// You can override the default SPI frequency
29+
#define CS_PIN 2 // Which pin you connect CS to. Used only when "USE_SPI" is defined
30+
31+
#define WIRE_PORT Wire // Your desired Wire port. Used when "USE_SPI" is not defined
32+
#define AD0_VAL 1 // The value of the last bit of the I2C address.
33+
// On the SparkFun 9DoF IMU breakout the default is 1, and when
34+
// the ADR jumper is closed the value becomes 0
35+
36+
#ifdef USE_SPI
37+
ICM_20948_SPI myICM; // If using SPI create an ICM_20948_SPI object
38+
#else
39+
ICM_20948_I2C myICM; // Otherwise create an ICM_20948_I2C object
40+
#endif
41+
42+
// Some vars to control or respond to interrupts
43+
volatile bool isrFired = false;
44+
volatile bool sensorSleep = false;
45+
volatile bool canToggle = false;
46+
unsigned int WOM_threshold=255;
47+
ICM_20948_ACCEL_WOM_THR_t pdata;
48+
49+
void setup() {
50+
51+
pinMode(INT_PIN, INPUT_PULLUP); // Using a pullup b/c ICM-20948 Breakout board has an onboard pullup as well and we don't want them to compete
52+
attachInterrupt(digitalPinToInterrupt(INT_PIN), icmISR, FALLING); // Set up a falling interrupt
53+
54+
pinMode(LED_PIN, OUTPUT);
55+
digitalWrite(LED_PIN, !sensorSleep);
56+
57+
SERIAL_PORT.begin(115200);
58+
while(!SERIAL_PORT){};
59+
60+
#ifdef USE_SPI
61+
SPI_PORT.begin();
62+
#else
63+
WIRE_PORT.begin();
64+
WIRE_PORT.setClock(400000);
65+
#endif
66+
67+
bool initialized = false;
68+
while( !initialized ){
69+
70+
#ifdef USE_SPI
71+
myICM.begin( CS_PIN, SPI_PORT, SPI_FREQ ); // Here we are using the user-defined SPI_FREQ as the clock speed of the SPI bus
72+
#else
73+
myICM.begin( WIRE_PORT, AD0_VAL );
74+
#endif
75+
76+
SERIAL_PORT.print( F("Initialization of the sensor returned: ") );
77+
SERIAL_PORT.println( myICM.statusString() );
78+
if( myICM.status != ICM_20948_Stat_Ok ){
79+
SERIAL_PORT.println( "Trying again..." );
80+
delay(500);
81+
}else{
82+
initialized = true;
83+
}
84+
}
85+
86+
// In this advanced example we'll cover how to do WakeOn Motion action using interrupts
87+
SERIAL_PORT.println("Device connected!");
88+
89+
// Here we are doing a SW reset to make sure the device starts in a known state
90+
myICM.swReset( );
91+
if( myICM.status != ICM_20948_Stat_Ok){
92+
SERIAL_PORT.print(F("Software Reset returned: "));
93+
SERIAL_PORT.println(myICM.statusString());
94+
}
95+
delay(250);
96+
97+
// Now wake the sensor up
98+
myICM.sleep( sensorSleep );
99+
myICM.lowPower( false );
100+
101+
// The next few configuration functions accept a bit-mask of sensors for which the settings should be applied.
102+
103+
// Set Gyro and Accelerometer to a particular sample mode
104+
// options: ICM_20948_Sample_Mode_Continuous
105+
// ICM_20948_Sample_Mode_Cycled
106+
myICM.setSampleMode( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Cycled );
107+
SERIAL_PORT.print(F("setSampleMode returned: "));
108+
SERIAL_PORT.println(myICM.statusString());
109+
110+
111+
ICM_20948_smplrt_t mySmplrt;
112+
mySmplrt.g = 54;
113+
myICM.setSampleRate( ICM_20948_Internal_Gyr, mySmplrt );
114+
SERIAL_PORT.print(F("setSampleRate returned: "));
115+
SERIAL_PORT.println(myICM.statusString());
116+
117+
// Set full scale ranges for both acc and gyr
118+
ICM_20948_fss_t myFSS; // This uses a "Full Scale Settings" structure that can contain values for all configurable sensors
119+
120+
myFSS.a = gpm2; // (ICM_20948_ACCEL_CONFIG_FS_SEL_e)
121+
// gpm2
122+
// gpm4
123+
// gpm8
124+
// gpm16
125+
126+
myFSS.g = dps250; // (ICM_20948_GYRO_CONFIG_1_FS_SEL_e)
127+
// dps250
128+
// dps500
129+
// dps1000
130+
// dps2000
131+
132+
myICM.setFullScale( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myFSS );
133+
if( myICM.status != ICM_20948_Stat_Ok){
134+
SERIAL_PORT.print(F("setFullScale returned: "));
135+
SERIAL_PORT.println(myICM.statusString());
136+
}
137+
138+
139+
// Set up Digital Low-Pass Filter configuration
140+
ICM_20948_dlpcfg_t myDLPcfg; // Similar to FSS, this uses a configuration structure for the desired sensors
141+
myDLPcfg.a = acc_d473bw_n499bw; // (ICM_20948_ACCEL_CONFIG_DLPCFG_e)
142+
// acc_d246bw_n265bw - means 3db bandwidth is 246 hz and nyquist bandwidth is 265 hz
143+
// acc_d111bw4_n136bw
144+
// acc_d50bw4_n68bw8
145+
// acc_d23bw9_n34bw4
146+
// acc_d11bw5_n17bw
147+
// acc_d5bw7_n8bw3 - means 3 db bandwidth is 5.7 hz and nyquist bandwidth is 8.3 hz
148+
// acc_d473bw_n499bw
149+
150+
myDLPcfg.g = gyr_d361bw4_n376bw5; // (ICM_20948_GYRO_CONFIG_1_DLPCFG_e)
151+
// gyr_d196bw6_n229bw8
152+
// gyr_d151bw8_n187bw6
153+
// gyr_d119bw5_n154bw3
154+
// gyr_d51bw2_n73bw3
155+
// gyr_d23bw9_n35bw9
156+
// gyr_d11bw6_n17bw8
157+
// gyr_d5bw7_n8bw9
158+
// gyr_d361bw4_n376bw5
159+
160+
myICM.setDLPFcfg( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myDLPcfg );
161+
if( myICM.status != ICM_20948_Stat_Ok){
162+
SERIAL_PORT.print(F("setDLPcfg returned: "));
163+
SERIAL_PORT.println(myICM.statusString());
164+
}
165+
166+
// Choose whether or not to use DLPF
167+
// Here we're also showing another way to access the status values, and that it is OK to supply individual sensor masks to these functions
168+
ICM_20948_Status_e accDLPEnableStat = myICM.enableDLPF( ICM_20948_Internal_Acc, true );
169+
ICM_20948_Status_e gyrDLPEnableStat = myICM.enableDLPF( ICM_20948_Internal_Gyr, true );
170+
SERIAL_PORT.print(F("Enable DLPF for Accelerometer returned: ")); SERIAL_PORT.println(myICM.statusString(accDLPEnableStat));
171+
SERIAL_PORT.print(F("Enable DLPF for Gyroscope returned: ")); SERIAL_PORT.println(myICM.statusString(gyrDLPEnableStat));
172+
173+
// Now we're going to set up interrupts. There are a lot of options, but for this test we're just configuring the interrupt pin and enabling interrupts to tell us when new data is ready
174+
/*
175+
ICM_20948_Status_e cfgIntActiveLow ( bool active_low );
176+
ICM_20948_Status_e cfgIntOpenDrain ( bool open_drain );
177+
ICM_20948_Status_e cfgIntLatch ( bool latching ); // If not latching then the interrupt is a 50 us pulse
178+
179+
ICM_20948_Status_e cfgIntAnyReadToClear ( bool enabled ); // If enabled, *ANY* read will clear the INT_STATUS register. So if you have multiple interrupt sources enabled be sure to read INT_STATUS first
180+
181+
ICM_20948_Status_e cfgFsyncActiveLow ( bool active_low );
182+
ICM_20948_Status_e cfgFsyncIntMode ( bool interrupt_mode ); // Can ue FSYNC as an interrupt input that sets the I2C Master Status register's PASS_THROUGH bit
183+
184+
ICM_20948_Status_e intEnableI2C ( bool enable );
185+
ICM_20948_Status_e intEnableDMP ( bool enable );
186+
ICM_20948_Status_e intEnablePLL ( bool enable );
187+
ICM_20948_Status_e intEnableWOM ( bool enable );
188+
ICM_20948_Status_e intEnableWOF ( bool enable );
189+
ICM_20948_Status_e intEnableRawDataReady ( bool enable );
190+
ICM_20948_Status_e intEnableOverflowFIFO ( uint8_t bm_enable );
191+
ICM_20948_Status_e intEnableWatermarkFIFO ( uint8_t bm_enable );
192+
*/
193+
myICM.cfgIntActiveLow(true); // Active low to be compatible with the breakout board's pullup resistor
194+
myICM.cfgIntOpenDrain(false); // Push-pull, though open-drain would also work thanks to the pull-up resistors on the breakout
195+
myICM.cfgIntLatch(true); // Latch the interrupt until cleared
196+
SERIAL_PORT.print(F("cfgIntLatch returned: "));
197+
SERIAL_PORT.println(myICM.statusString());
198+
199+
myICM.WOMThreshold(WOM_threshold); // set WoM threshold
200+
SERIAL_PORT.print(F("Set threshold returned: "));
201+
SERIAL_PORT.println(myICM.statusString());
202+
203+
myICM.intEnableWOM(true); // enable interrupts on WakeOnMotion
204+
SERIAL_PORT.print(F("intEnableWOM returned: "));
205+
SERIAL_PORT.println(myICM.statusString());
206+
207+
myICM.WOMThreshold(WOM_threshold); // set WoM threshold - just in case...
208+
SERIAL_PORT.print(F("Set threshold returned: "));
209+
SERIAL_PORT.println(myICM.statusString());
210+
211+
SERIAL_PORT.println();
212+
SERIAL_PORT.println(F("Configuration complete!"));
213+
}
214+
215+
void loop() {
216+
if( isrFired ){ // If our isr flag is set then clear the interrupts on the ICM
217+
isrFired = false;
218+
myICM.getAGMT(); // get the A, G, M, and T readings
219+
// printScaledAGMT( myICM.agmt); // This function takes into account the sclae settings from when the measurement was made to calculate the values with units
220+
SERIAL_PORT.println(F("Shock detected"));
221+
delay(30);
222+
myICM.clearInterrupts(); // This would be efficient... but not compatible with Uno
223+
}
224+
225+
myICM.clearInterrupts(); // clear interrupts for next time -
226+
// usually you'd do this only if an interrupt has occurred, however
227+
// on the 328p I2C usage can block interrupts. This means that sometimes
228+
// an interrupt is missed. When missed, if using an edge-based interrupt
229+
// and only clearing interrupts when one was detected there will be no more
230+
// edges to respond to, so no more interrupts will be detected. Here are
231+
// some possible solutions:
232+
// 1. use a level based interrupt
233+
// 2. use the pulse-based interrupt in ICM settings (set cfgIntLatch to false)
234+
// 3. use a microcontroller with nestable interrupts
235+
// 4. clear the interrupts often
236+
237+
}
238+
239+
void icmISR( void ){
240+
isrFired = true; // Can't use I2C within ISR on 328p, so just set a flag to know that data is available
241+
}
242+
243+
244+
// Below here are some helper functions to print the data nicely!
245+
void printPaddedInt16b( int16_t val ){
246+
if(val > 0){
247+
SERIAL_PORT.print(" ");
248+
if(val < 10000){ SERIAL_PORT.print("0"); }
249+
if(val < 1000 ){ SERIAL_PORT.print("0"); }
250+
if(val < 100 ){ SERIAL_PORT.print("0"); }
251+
if(val < 10 ){ SERIAL_PORT.print("0"); }
252+
}else{
253+
SERIAL_PORT.print("-");
254+
if(abs(val) < 10000){ SERIAL_PORT.print("0"); }
255+
if(abs(val) < 1000 ){ SERIAL_PORT.print("0"); }
256+
if(abs(val) < 100 ){ SERIAL_PORT.print("0"); }
257+
if(abs(val) < 10 ){ SERIAL_PORT.print("0"); }
258+
}
259+
SERIAL_PORT.print(abs(val));
260+
}
261+
262+
void printRawAGMT( ICM_20948_AGMT_t agmt){
263+
SERIAL_PORT.print("RAW. Acc [ ");
264+
printPaddedInt16b( agmt.acc.axes.x );
265+
SERIAL_PORT.print(", ");
266+
printPaddedInt16b( agmt.acc.axes.y );
267+
SERIAL_PORT.print(", ");
268+
printPaddedInt16b( agmt.acc.axes.z );
269+
SERIAL_PORT.print(" ], Gyr [ ");
270+
printPaddedInt16b( agmt.gyr.axes.x );
271+
SERIAL_PORT.print(", ");
272+
printPaddedInt16b( agmt.gyr.axes.y );
273+
SERIAL_PORT.print(", ");
274+
printPaddedInt16b( agmt.gyr.axes.z );
275+
SERIAL_PORT.print(" ], Mag [ ");
276+
printPaddedInt16b( agmt.mag.axes.x );
277+
SERIAL_PORT.print(", ");
278+
printPaddedInt16b( agmt.mag.axes.y );
279+
SERIAL_PORT.print(", ");
280+
printPaddedInt16b( agmt.mag.axes.z );
281+
SERIAL_PORT.print(" ], Tmp [ ");
282+
printPaddedInt16b( agmt.tmp.val );
283+
SERIAL_PORT.print(" ]");
284+
SERIAL_PORT.println();
285+
}
286+
287+
288+
void printFormattedFloat(float val, uint8_t leading, uint8_t decimals){
289+
float aval = abs(val);
290+
if(val < 0){
291+
SERIAL_PORT.print("-");
292+
}else{
293+
SERIAL_PORT.print(" ");
294+
}
295+
for( uint8_t indi = 0; indi < leading; indi++ ){
296+
uint32_t tenpow = 0;
297+
if( indi < (leading-1) ){
298+
tenpow = 1;
299+
}
300+
for(uint8_t c = 0; c < (leading-1-indi); c++){
301+
tenpow *= 10;
302+
}
303+
if( aval < tenpow){
304+
SERIAL_PORT.print("0");
305+
}else{
306+
break;
307+
}
308+
}
309+
if(val < 0){
310+
SERIAL_PORT.print(-val, decimals);
311+
}else{
312+
SERIAL_PORT.print(val, decimals);
313+
}
314+
}
315+
316+
void printScaledAGMT( ICM_20948_AGMT_t agmt){
317+
SERIAL_PORT.print("Scaled. Acc (mg) [ ");
318+
printFormattedFloat( myICM.accX(), 5, 2 );
319+
SERIAL_PORT.print(", ");
320+
printFormattedFloat( myICM.accY(), 5, 2 );
321+
SERIAL_PORT.print(", ");
322+
printFormattedFloat( myICM.accZ(), 5, 2 );
323+
SERIAL_PORT.print(" ], Gyr (DPS) [ ");
324+
printFormattedFloat( myICM.gyrX(), 5, 2 );
325+
SERIAL_PORT.print(", ");
326+
printFormattedFloat( myICM.gyrY(), 5, 2 );
327+
SERIAL_PORT.print(", ");
328+
printFormattedFloat( myICM.gyrZ(), 5, 2 );
329+
SERIAL_PORT.print(" ], Mag (uT) [ ");
330+
printFormattedFloat( myICM.magX(), 5, 2 );
331+
SERIAL_PORT.print(", ");
332+
printFormattedFloat( myICM.magY(), 5, 2 );
333+
SERIAL_PORT.print(", ");
334+
printFormattedFloat( myICM.magZ(), 5, 2 );
335+
SERIAL_PORT.print(" ], Tmp (C) [ ");
336+
printFormattedFloat( myICM.temp(), 5, 2 );
337+
SERIAL_PORT.print(" ]");
338+
SERIAL_PORT.println();
339+
}

0 commit comments

Comments
 (0)