-
Notifications
You must be signed in to change notification settings - Fork 72
/
ICM_20948.h
287 lines (234 loc) · 12.8 KB
/
ICM_20948.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*
A C++ interface to the ICM-20948
*/
#ifndef _ICM_20948_H_
#define _ICM_20948_H_
#include "util/ICM_20948_C.h" // The C backbone. ICM_20948_USE_DMP is defined in here.
#include "util/AK09916_REGISTERS.h"
#include "Arduino.h" // Arduino support
#include "Wire.h"
#include "SPI.h"
#define ICM_20948_ARD_UNUSED_PIN 0xFF
// Base
class ICM_20948
{
private:
Stream *_debugSerial; //The stream to send debug messages to if enabled
bool _printDebug = false; //Flag to print the serial commands we are sending to the Serial port for debug
const uint8_t MAX_MAGNETOMETER_STARTS = 10; // This replaces maxTries
protected:
ICM_20948_Device_t _device;
float getTempC(int16_t val);
float getGyrDPS(int16_t axis_val);
float getAccMG(int16_t axis_val);
float getMagUT(int16_t axis_val);
public:
ICM_20948(); // Constructor
// Enable debug messages using the chosen Serial port (Stream)
// Boards like the RedBoard Turbo use SerialUSB (not Serial).
// But other boards like the SAMD51 Thing Plus use Serial (not SerialUSB).
// These lines let the code compile cleanly on as many SAMD boards as possible.
#if defined(ARDUINO_ARCH_SAMD) // Is this a SAMD board?
#if defined(USB_VID) // Is the USB Vendor ID defined?
#if (USB_VID == 0x1B4F) // Is this a SparkFun board?
#if !defined(ARDUINO_SAMD51_THING_PLUS) & !defined(ARDUINO_SAMD51_MICROMOD) // If it is not a SAMD51 Thing Plus or SAMD51 MicroMod
void enableDebugging(Stream &debugPort = SerialUSB); //Given a port to print to, enable debug messages.
#else
void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages.
#endif
#else
void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages.
#endif
#else
void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages.
#endif
#else
void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages.
#endif
void disableDebugging(void); //Turn off debug statements
void debugPrintStatus(ICM_20948_Status_e stat);
// gfvalvo's flash string helper code: https://forum.arduino.cc/index.php?topic=533118.msg3634809#msg3634809
void debugPrint(const char *);
void debugPrint(const __FlashStringHelper *);
void debugPrintln(const char *);
void debugPrintln(const __FlashStringHelper *);
void doDebugPrint(char (*)(const char *), const char *, bool newLine = false);
void debugPrintf(int i);
void debugPrintf(float f);
ICM_20948_AGMT_t agmt; // Acceleometer, Gyroscope, Magenetometer, and Temperature data
ICM_20948_AGMT_t getAGMT(void); // Updates the agmt field in the object and also returns a copy directly
float magX(void); // micro teslas
float magY(void); // micro teslas
float magZ(void); // micro teslas
float accX(void); // milli g's
float accY(void); // milli g's
float accZ(void); // milli g's
float gyrX(void); // degrees per second
float gyrY(void); // degrees per second
float gyrZ(void); // degrees per second
float temp(void); // degrees celsius
ICM_20948_Status_e status; // Status from latest operation
const char *statusString(ICM_20948_Status_e stat = ICM_20948_Stat_NUM); // Returns a human-readable status message. Defaults to status member, but prints string for supplied status if supplied
// Device Level
ICM_20948_Status_e setBank(uint8_t bank); // Sets the bank
ICM_20948_Status_e swReset(void); // Performs a SW reset
ICM_20948_Status_e sleep(bool on = false); // Set sleep mode for the chip
ICM_20948_Status_e lowPower(bool on = true); // Set low power mode for the chip
ICM_20948_Status_e setClockSource(ICM_20948_PWR_MGMT_1_CLKSEL_e source); // Choose clock source
ICM_20948_Status_e checkID(void); // Return 'ICM_20948_Stat_Ok' if whoami matches ICM_20948_WHOAMI
bool dataReady(void); // Returns 'true' if data is ready
uint8_t getWhoAmI(void); // Return whoami in out prarmeter
bool isConnected(void); // Returns true if communications with the device are sucessful
// Internal Sensor Options
ICM_20948_Status_e setSampleMode(uint8_t sensor_id_bm, uint8_t lp_config_cycle_mode); // Use to set accel, gyro, and I2C master into cycled or continuous modes
ICM_20948_Status_e setFullScale(uint8_t sensor_id_bm, ICM_20948_fss_t fss);
ICM_20948_Status_e setDLPFcfg(uint8_t sensor_id_bm, ICM_20948_dlpcfg_t cfg);
ICM_20948_Status_e enableDLPF(uint8_t sensor_id_bm, bool enable);
ICM_20948_Status_e setSampleRate(uint8_t sensor_id_bm, ICM_20948_smplrt_t smplrt);
// Interrupts on INT and FSYNC Pins
ICM_20948_Status_e clearInterrupts(void);
ICM_20948_Status_e cfgIntActiveLow(bool active_low);
ICM_20948_Status_e cfgIntOpenDrain(bool open_drain);
ICM_20948_Status_e cfgIntLatch(bool latching); // If not latching then the interrupt is a 50 us pulse
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
ICM_20948_Status_e cfgFsyncActiveLow(bool active_low);
ICM_20948_Status_e cfgFsyncIntMode(bool interrupt_mode); // Can use FSYNC as an interrupt input that sets the I2C Master Status register's PASS_THROUGH bit
ICM_20948_Status_e intEnableI2C(bool enable);
ICM_20948_Status_e intEnableDMP(bool enable);
ICM_20948_Status_e intEnablePLL(bool enable);
ICM_20948_Status_e intEnableWOM(bool enable);
ICM_20948_Status_e intEnableWOF(bool enable);
ICM_20948_Status_e intEnableRawDataReady(bool enable);
ICM_20948_Status_e intEnableOverflowFIFO(uint8_t bm_enable);
ICM_20948_Status_e intEnableWatermarkFIFO(uint8_t bm_enable);
ICM_20948_Status_e WOMLogic(uint8_t enable, uint8_t mode);
ICM_20948_Status_e WOMThreshold(uint8_t threshold);
// Interface Options
ICM_20948_Status_e i2cMasterPassthrough(bool passthrough = true);
ICM_20948_Status_e i2cMasterEnable(bool enable = true);
ICM_20948_Status_e i2cMasterReset();
//Used for configuring peripherals 0-3
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);
ICM_20948_Status_e i2cControllerPeriph4Transaction(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len, bool Rw, bool send_reg_addr = true);
//Provided for backward-compatibility only. Please update to i2cControllerConfigurePeripheral and i2cControllerPeriph4Transaction.
//https://www.oshwa.org/2020/06/29/a-resolution-to-redefine-spi-pin-names/
ICM_20948_Status_e i2cMasterConfigureSlave(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);
ICM_20948_Status_e i2cMasterSLV4Transaction(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len, bool Rw, bool send_reg_addr = true);
//Used for configuring the Magnetometer
ICM_20948_Status_e i2cMasterSingleW(uint8_t addr, uint8_t reg, uint8_t data);
uint8_t i2cMasterSingleR(uint8_t addr, uint8_t reg);
// Default Setup
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.
// direct read/write
ICM_20948_Status_e read(uint8_t reg, uint8_t *pdata, uint32_t len);
ICM_20948_Status_e write(uint8_t reg, uint8_t *pdata, uint32_t len);
//Mag specific
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.
ICM_20948_Status_e magWhoIAm(void);
uint8_t readMag(AK09916_Reg_Addr_e reg);
ICM_20948_Status_e writeMag(AK09916_Reg_Addr_e reg, uint8_t *pdata);
ICM_20948_Status_e resetMag();
//FIFO
ICM_20948_Status_e enableFIFO(bool enable = true);
ICM_20948_Status_e resetFIFO(void);
ICM_20948_Status_e setFIFOmode(bool snapshot = false); // Default to Stream (non-Snapshot) mode
ICM_20948_Status_e getFIFOcount(uint16_t *count);
ICM_20948_Status_e readFIFO(uint8_t *data, uint8_t len = 1);
//DMP
//Gyro Bias
ICM_20948_Status_e setBiasGyroX(int32_t newValue);
ICM_20948_Status_e setBiasGyroY(int32_t newValue);
ICM_20948_Status_e setBiasGyroZ(int32_t newValue);
ICM_20948_Status_e getBiasGyroX(int32_t* bias);
ICM_20948_Status_e getBiasGyroY(int32_t* bias);
ICM_20948_Status_e getBiasGyroZ(int32_t* bias);
//Accel Bias
ICM_20948_Status_e setBiasAccelX(int32_t newValue);
ICM_20948_Status_e setBiasAccelY(int32_t newValue);
ICM_20948_Status_e setBiasAccelZ(int32_t newValue);
ICM_20948_Status_e getBiasAccelX(int32_t* bias);
ICM_20948_Status_e getBiasAccelY(int32_t* bias);
ICM_20948_Status_e getBiasAccelZ(int32_t* bias);
//CPass Bias
ICM_20948_Status_e setBiasCPassX(int32_t newValue);
ICM_20948_Status_e setBiasCPassY(int32_t newValue);
ICM_20948_Status_e setBiasCPassZ(int32_t newValue);
ICM_20948_Status_e getBiasCPassX(int32_t* bias);
ICM_20948_Status_e getBiasCPassY(int32_t* bias);
ICM_20948_Status_e getBiasCPassZ(int32_t* bias);
// Done:
// Configure DMP start address through PRGM_STRT_ADDRH/PRGM_STRT_ADDRL
// Load Firmware
// Configure Accel scaling to DMP
// Configure Compass mount matrix and scale to DMP
// Reset FIFO
// Reset DMP
// Enable DMP interrupt
// Configuring DMP to output data to FIFO: set DATA_OUT_CTL1, DATA_OUT_CTL2, DATA_INTR_CTL and MOTION_EVENT_CTL
// Configuring DMP to output data at multiple ODRs
// Configure DATA_RDY_STATUS
// Configuring Accel calibration
// Configuring Compass calibration
// Configuring Gyro gain
// Configuring Accel gain
// Configure I2C_SLV0 and I2C_SLV1 to: request mag data from the hidden reserved AK09916 registers; trigger Single Measurements
// Configure I2C Master ODR (default to 68.75Hz)
// To Do:
// Additional FIFO output control: FIFO_WATERMARK, BM_BATCH_MASK, BM_BATCH_CNTR, BM_BATCH_THLD
// Configuring DMP features: PED_STD_STEPCTR, PED_STD_TIMECTR
// Enabling Activity Recognition (BAC) feature
// Enabling Significant Motion Detect (SMD) feature
// Enabling Tilt Detector feature
// Enabling Pick Up Gesture feature
// Enabling Fsync detection feature
// Biases: add save and load methods
ICM_20948_Status_e enableDMP(bool enable = true);
ICM_20948_Status_e resetDMP(void);
ICM_20948_Status_e loadDMPFirmware(void);
ICM_20948_Status_e setDMPstartAddress(unsigned short address = DMP_START_ADDRESS);
ICM_20948_Status_e enableDMPSensor(enum inv_icm20948_sensor sensor, bool enable = true);
ICM_20948_Status_e enableDMPSensorInt(enum inv_icm20948_sensor sensor, bool enable = true);
ICM_20948_Status_e writeDMPmems(unsigned short reg, unsigned int length, const unsigned char *data);
ICM_20948_Status_e readDMPmems(unsigned short reg, unsigned int length, unsigned char *data);
ICM_20948_Status_e setDMPODRrate(enum DMP_ODR_Registers odr_reg, int interval);
ICM_20948_Status_e readDMPdataFromFIFO(icm_20948_DMP_data_t *data);
ICM_20948_Status_e setGyroSF(unsigned char div, int gyro_level);
ICM_20948_Status_e initializeDMP(void) __attribute__((weak)); // Combine all of the DMP start-up code in one place. Can be overwritten if required
};
// I2C
// Forward declarations of TwoWire and Wire for board/variant combinations that don't have a default 'SPI'
//class TwoWire; // Commented by PaulZC 21/2/8 - this was causing compilation to fail on the Arduino NANO 33 BLE
//extern TwoWire Wire; // Commented by PaulZC 21/2/8 - this was causing compilation to fail on the Arduino NANO 33 BLE
class ICM_20948_I2C : public ICM_20948
{
private:
protected:
public:
TwoWire *_i2c;
uint8_t _addr;
uint8_t _ad0;
bool _ad0val;
ICM_20948_Serif_t _serif;
ICM_20948_I2C(); // Constructor
virtual ICM_20948_Status_e begin(TwoWire &wirePort = Wire, bool ad0val = true, uint8_t ad0pin = ICM_20948_ARD_UNUSED_PIN);
};
// SPI
#define ICM_20948_SPI_DEFAULT_FREQ 4000000
#define ICM_20948_SPI_DEFAULT_ORDER MSBFIRST
#define ICM_20948_SPI_DEFAULT_MODE SPI_MODE0
// Forward declarations of SPIClass and SPI for board/variant combinations that don't have a default 'SPI'
//class SPIClass; // Commented by PaulZC 21/2/8 - this was causing compilation to fail on the Arduino NANO 33 BLE
//extern SPIClass SPI; // Commented by PaulZC 21/2/8 - this was causing compilation to fail on the Arduino NANO 33 BLE
class ICM_20948_SPI : public ICM_20948
{
private:
protected:
public:
SPIClass *_spi;
SPISettings _spisettings;
uint8_t _cs;
ICM_20948_Serif_t _serif;
ICM_20948_SPI(); // Constructor
ICM_20948_Status_e begin(uint8_t csPin, SPIClass &spiPort = SPI, uint32_t SPIFreq = ICM_20948_SPI_DEFAULT_FREQ);
};
#endif /* _ICM_20948_H_ */