Skip to content

Commit cb2bcb6

Browse files
authored
Add files via upload
1 parent 7f1f050 commit cb2bcb6

File tree

1 file changed

+337
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)