├── .gitattributes ├── .gitignore ├── CONTRIBUTING.md ├── DMP.md ├── ISSUE_TEMPLATE.md ├── License.md ├── README.md ├── examples ├── Arduino │ ├── Example10_DMP_FastMultipleSensors │ │ └── Example10_DMP_FastMultipleSensors.ino │ ├── Example11_DMP_Bias_Save_Restore_ESP32 │ │ └── Example11_DMP_Bias_Save_Restore_ESP32.ino │ ├── Example1_Basics │ │ └── Example1_Basics.ino │ ├── Example2_Advanced │ │ └── Example2_Advanced.ino │ ├── Example3_Interrupts │ │ └── Example3_Interrupts.ino │ ├── Example4_WakeOnMotion │ │ └── Example4_WakeOnMotion.ino │ ├── Example5_DualSPITest │ │ └── Example5_DualSPITest.ino │ ├── Example6_DMP_Quat9_Orientation │ │ └── Example6_DMP_Quat9_Orientation.ino │ ├── Example7_DMP_Quat6_EulerAngles │ │ └── Example7_DMP_Quat6_EulerAngles.ino │ ├── Example8_DMP_RawAccel │ │ └── Example8_DMP_RawAccel.ino │ └── Example9_DMP_MultipleSensors │ │ └── Example9_DMP_MultipleSensors.ino └── PortableC │ └── Example999_Portable │ └── Example999_Portable.ino ├── img └── Contributing.JPG ├── keywords.txt ├── library.properties └── src ├── ICM_20948.cpp ├── ICM_20948.h └── util ├── AK09916_ENUMERATIONS.h ├── AK09916_REGISTERS.h ├── ICM20948_eMD_nucleo_1.0 └── icm20948_img.dmp3a.h_14290 ├── ICM_20948_C.c ├── ICM_20948_C.h ├── ICM_20948_DMP.h ├── ICM_20948_ENUMERATIONS.h ├── ICM_20948_REGISTERS.h ├── eMD-SmartMotion-ICM20948-1.1.0-MP └── icm20948_img.dmp3a.h_14301 └── icm20948_img.dmp3a.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # VSCode 2 | *.vscode* 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | Thank you so *much* for offering to help out. We truly appreciate it. 4 | 5 | If you'd like to contribute, start by searching through the [issues](https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary/issues) and [pull requests](https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary/pulls) to see whether someone else has raised a similar idea or question. 6 | Please check the [closed issues](https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary/issues?q=is%3Aissue+is%3Aclosed) 7 | and [closed pull requests](https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary/pulls?q=is%3Apr+is%3Aclosed) too - you may find that your issue or feature has already been discussed. 8 | 9 | If you decide to add a feature to this library, please create a PR and follow these best practices: 10 | 11 | * Change as little as possible. Do not submit a PR that changes 100 lines of whitespace. Break up into multiple PRs if necessary. 12 | * If you've added a new feature document it with a simple example sketch. This serves both as a test of your PR and as a quick way for users to quickly learn how to use your new feature. 13 | * If you add new functions also add them to _keywords.txt_ so that they are properly highlighted in Arduino. [Read more](https://www.arduino.cc/en/Hacking/libraryTutorial). 14 | * **Important:** Please submit your PR using the [release_candidate branch](https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary/tree/release_candidate). That way, we can merge and test your PR quickly without changing the _master_ branch 15 | 16 | ![Contributing.JPG](./img/Contributing.JPG) 17 | 18 | ## Style guide 19 | 20 | Please read and follow the [Arduino API style guide](https://www.arduino.cc/en/Reference/APIStyleGuide). Also read and consider the [Arduino style guide](https://www.arduino.cc/en/Reference/StyleGuide). 21 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Subject of the issue 2 | Describe your issue here. 3 | 4 | ### Your workbench 5 | * What platform are you using? 6 | * What version of the device are you using? Is there a firmware version? 7 | * How is the device wired to your platform? 8 | * How is everything being powered? 9 | * Are there any additional details that may help us help you? 10 | 11 | ### Steps to reproduce 12 | Tell us how to reproduce this issue. Please post stripped down example code demonstrating your issue to a gist. 13 | 14 | ### Expected behaviour 15 | Tell us what should happen 16 | 17 | ### Actual behaviour 18 | Tell us what happens instead 19 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | SparkFun License Information 2 | ============================ 3 | 4 | SparkFun uses two different licenses for our files — one for hardware and one for code. 5 | 6 | Hardware 7 | --------- 8 | 9 | **SparkFun hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).** 10 | 11 | Note: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-sa/4.0/legalcode). 12 | 13 | You are free to: 14 | 15 | Share — copy and redistribute the material in any medium or format 16 | Adapt — remix, transform, and build upon the material 17 | for any purpose, even commercially. 18 | The licensor cannot revoke these freedoms as long as you follow the license terms. 19 | Under the following terms: 20 | 21 | Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 22 | ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 23 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 24 | Notices: 25 | 26 | You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. 27 | No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. 28 | 29 | 30 | Code 31 | -------- 32 | 33 | **SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT).** 34 | 35 | The MIT License (MIT) 36 | 37 | Copyright (c) 2016 SparkFun Electronics 38 | 39 | Permission is hereby granted, free of charge, to any person obtaining a copy 40 | of this software and associated documentation files (the "Software"), to deal 41 | in the Software without restriction, including without limitation the rights 42 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 43 | copies of the Software, and to permit persons to whom the Software is 44 | furnished to do so, subject to the following conditions: 45 | 46 | The above copyright notice and this permission notice shall be included in all 47 | copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 50 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 51 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 52 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 53 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 54 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | SOFTWARE. 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SparkFun ICM-20948 Arduino Library 2 | 3 | This is the SparkFun library for the TDK InvenSense ICM-20948 Inertial Measurement Unit 9-Degree Of Freedom sensor as used on the [SparkFun 9DoF IMU Breakout - ICM-20948 (Qwiic)](https://www.sparkfun.com/products/15335). 4 | 5 | Version 1.2 of the library includes support for the InvenSense Digital Motion Processor (DMP™). You can find further details in [DMP.md](./DMP.md). 6 | 7 | ## Contributing 8 | 9 | If you would like to contribute to this library: please do, we truly appreciate it, but please follow [these guidelines](./CONTRIBUTING.md). Thanks! 10 | 11 | ## Repository Contents 12 | 13 | * [**/examples**](./examples) - Example sketches for the library (.ino). Run these from the Arduino IDE. 14 | * [**/src**](./src) - Source files for the library (.cpp, .h). 15 | * [**keywords.txt**](./keywords.txt) - Keywords from this library that will be highlighted in the Arduino IDE. 16 | * [**library.properties**](./library.properties) - General library properties for the Arduino package manager. 17 | * [**CONTRIBUTING.md**](./CONTRIBUTING.md) - Guidelines on how to contribute to this library. 18 | * [**DMP.md**](./DMP.md) - Information about the InvenSense Digital Motion Processor (DMP™) 19 | 20 | ## Documentation 21 | 22 | * **[Hookup Guide](https://learn.sparkfun.com/tutorials/sparkfun-9dof-imu-icm-20948-breakout-hookup-guide)** - Basic hookup guide for the SparkFun 9DoF IMU Breakout. 23 | * **[Installing an Arduino Library Guide](https://learn.sparkfun.com/tutorials/installing-an-arduino-library)** - Basic information on how to install an Arduino library. 24 | 25 | ## Products that use this Library 26 | 27 | * [SparkFun 9DoF IMU Breakout - ICM-20948 (Qwiic) - SEN-15335](https://www.sparkfun.com/products/15335) 28 | * [SparkFun OpenLog Artemis - DEV-16832](https://www.sparkfun.com/products/16832) 29 | * [SparkFun MicroMod Asset Tracker Carrier Board - DEV-17272](https://www.sparkfun.com/products/17272) 30 | 31 | ## License Information 32 | 33 | This product is _**open source**_! 34 | 35 | Please see the included [License.md](./License.md) for more information. 36 | 37 | Distributed as-is; no warranty is given. 38 | 39 | - Your friends at SparkFun. 40 | -------------------------------------------------------------------------------- /examples/Arduino/Example11_DMP_Bias_Save_Restore_ESP32/Example11_DMP_Bias_Save_Restore_ESP32.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Example11_DMP_Bias_Save_Restore_ESP32.ino 3 | * ICM 20948 Arduino Library Demo 4 | * Initialize the DMP based on the TDK InvenSense ICM20948_eMD_nucleo_1.0 example-icm20948 5 | * Periodically save the biases in ESP32 EEPROM 6 | * Restore the biases at startup - if valid 7 | * Paul Clark, November 14th, 2022 8 | * Based on original code by the SlimeVR-Tracker contributors: 9 | * https://github.com/SlimeVR/SlimeVR-Tracker-ESP/blob/83550d21ef748678704b3a11c0f7afb6f44258e4/src/sensors/icm20948sensor.cpp#L30 10 | * https://github.com/PaulZC/SlimeVR-Tracker-ESP/blob/83550d21ef748678704b3a11c0f7afb6f44258e4/src/sensors/icm20948sensor.cpp#L40-L41 11 | * 12 | * ** This example is based on InvenSense's _confidential_ Application Note "Programming Sequence for DMP Hardware Functions". 13 | * ** We are grateful to InvenSense for sharing this with us. 14 | * 15 | * ** Important note: by default the DMP functionality is disabled in the library 16 | * ** as the DMP firmware takes up 14301 Bytes of program memory. 17 | * ** To use the DMP, you will need to: 18 | * ** Edit ICM_20948_C.h 19 | * ** Uncomment line 29: #define ICM_20948_USE_DMP 20 | * ** Save changes 21 | * ** If you are using Windows, you can find ICM_20948_C.h in: 22 | * ** Documents\Arduino\libraries\SparkFun_ICM-20948_ArduinoLibrary\src\util 23 | * 24 | * Please see License.md for the license information. 25 | * 26 | * Distributed as-is; no warranty is given. 27 | ***************************************************************/ 28 | 29 | #include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU 30 | 31 | //#define USE_SPI // Uncomment this to use SPI 32 | 33 | #define SERIAL_PORT Serial 34 | 35 | #define SPI_PORT SPI // Your desired SPI port. Used only when "USE_SPI" is defined 36 | #define CS_PIN 2 // Which pin you connect CS to. Used only when "USE_SPI" is defined 37 | 38 | #define WIRE_PORT Wire // Your desired Wire port. Used when "USE_SPI" is not defined 39 | // The value of the last bit of the I2C address. 40 | // On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0 41 | #define AD0_VAL 1 42 | 43 | #ifdef USE_SPI 44 | ICM_20948_SPI myICM; // If using SPI create an ICM_20948_SPI object 45 | #else 46 | ICM_20948_I2C myICM; // Otherwise create an ICM_20948_I2C object 47 | #endif 48 | 49 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 50 | 51 | #include 52 | 53 | // Define a storage struct for the biases. Include a non-zero header and a simple checksum 54 | struct biasStore 55 | { 56 | int32_t header = 0x42; 57 | int32_t biasGyroX = 0; 58 | int32_t biasGyroY = 0; 59 | int32_t biasGyroZ = 0; 60 | int32_t biasAccelX = 0; 61 | int32_t biasAccelY = 0; 62 | int32_t biasAccelZ = 0; 63 | int32_t biasCPassX = 0; 64 | int32_t biasCPassY = 0; 65 | int32_t biasCPassZ = 0; 66 | int32_t sum = 0; 67 | }; 68 | 69 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 70 | 71 | void updateBiasStoreSum(biasStore *store) // Update the bias store checksum 72 | { 73 | int32_t sum = store->header; 74 | sum += store->biasGyroX; 75 | sum += store->biasGyroY; 76 | sum += store->biasGyroZ; 77 | sum += store->biasAccelX; 78 | sum += store->biasAccelY; 79 | sum += store->biasAccelZ; 80 | sum += store->biasCPassX; 81 | sum += store->biasCPassY; 82 | sum += store->biasCPassZ; 83 | store->sum = sum; 84 | } 85 | 86 | bool isBiasStoreValid(biasStore *store) // Returns true if the header and checksum are valid 87 | { 88 | int32_t sum = store->header; 89 | 90 | if (sum != 0x42) 91 | return false; 92 | 93 | sum += store->biasGyroX; 94 | sum += store->biasGyroY; 95 | sum += store->biasGyroZ; 96 | sum += store->biasAccelX; 97 | sum += store->biasAccelY; 98 | sum += store->biasAccelZ; 99 | sum += store->biasCPassX; 100 | sum += store->biasCPassY; 101 | sum += store->biasCPassZ; 102 | 103 | return (store->sum == sum); 104 | } 105 | 106 | void printBiases(biasStore *store) 107 | { 108 | SERIAL_PORT.print(F("Gyro X: ")); 109 | SERIAL_PORT.print(store->biasGyroX); 110 | SERIAL_PORT.print(F(" Gyro Y: ")); 111 | SERIAL_PORT.print(store->biasGyroY); 112 | SERIAL_PORT.print(F(" Gyro Z: ")); 113 | SERIAL_PORT.println(store->biasGyroZ); 114 | SERIAL_PORT.print(F("Accel X: ")); 115 | SERIAL_PORT.print(store->biasAccelX); 116 | SERIAL_PORT.print(F(" Accel Y: ")); 117 | SERIAL_PORT.print(store->biasAccelY); 118 | SERIAL_PORT.print(F(" Accel Z: ")); 119 | SERIAL_PORT.println(store->biasAccelZ); 120 | SERIAL_PORT.print(F("CPass X: ")); 121 | SERIAL_PORT.print(store->biasCPassX); 122 | SERIAL_PORT.print(F(" CPass Y: ")); 123 | SERIAL_PORT.print(store->biasCPassY); 124 | SERIAL_PORT.print(F(" CPass Z: ")); 125 | SERIAL_PORT.println(store->biasCPassZ); 126 | 127 | } 128 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 129 | 130 | void setup() 131 | { 132 | 133 | delay(1000); 134 | 135 | SERIAL_PORT.begin(115200); // Start the serial console 136 | SERIAL_PORT.println(F("ICM-20948 Example")); 137 | 138 | #ifdef USE_SPI 139 | SPI_PORT.begin(); 140 | #else 141 | WIRE_PORT.begin(); 142 | WIRE_PORT.setClock(400000); 143 | #endif 144 | 145 | //myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial 146 | 147 | bool initialized = false; 148 | while (!initialized) 149 | { 150 | 151 | // Initialize the ICM-20948 152 | // If the DMP is enabled, .begin performs a minimal startup. We need to configure the sample mode etc. manually. 153 | #ifdef USE_SPI 154 | myICM.begin(CS_PIN, SPI_PORT); 155 | #else 156 | myICM.begin(WIRE_PORT, AD0_VAL); 157 | #endif 158 | 159 | SERIAL_PORT.print(F("Initialization of the sensor returned: ")); 160 | SERIAL_PORT.println(myICM.statusString()); 161 | if (myICM.status != ICM_20948_Stat_Ok) 162 | { 163 | SERIAL_PORT.println(F("Trying again...")); 164 | delay(500); 165 | } 166 | else 167 | { 168 | initialized = true; 169 | } 170 | } 171 | 172 | SERIAL_PORT.println(F("Device connected.")); 173 | 174 | bool success = true; // Use success to show if the DMP configuration was successful 175 | 176 | // Initialize the DMP. initializeDMP is a weak function. In this example we overwrite it to change the sample rate (see below) 177 | success &= (myICM.initializeDMP() == ICM_20948_Stat_Ok); 178 | 179 | // DMP sensor options are defined in ICM_20948_DMP.h 180 | // INV_ICM20948_SENSOR_ACCELEROMETER (16-bit accel) 181 | // INV_ICM20948_SENSOR_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 182 | // INV_ICM20948_SENSOR_RAW_ACCELEROMETER (16-bit accel) 183 | // INV_ICM20948_SENSOR_RAW_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 184 | // INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED (16-bit compass) 185 | // INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED (16-bit gyro) 186 | // INV_ICM20948_SENSOR_STEP_DETECTOR (Pedometer Step Detector) 187 | // INV_ICM20948_SENSOR_STEP_COUNTER (Pedometer Step Detector) 188 | // INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR (32-bit 6-axis quaternion) 189 | // INV_ICM20948_SENSOR_ROTATION_VECTOR (32-bit 9-axis quaternion + heading accuracy) 190 | // INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR (32-bit Geomag RV + heading accuracy) 191 | // INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD (32-bit calibrated compass) 192 | // INV_ICM20948_SENSOR_GRAVITY (32-bit 6-axis quaternion) 193 | // INV_ICM20948_SENSOR_LINEAR_ACCELERATION (16-bit accel + 32-bit 6-axis quaternion) 194 | // INV_ICM20948_SENSOR_ORIENTATION (32-bit 9-axis quaternion + heading accuracy) 195 | 196 | // Enable the DMP orientation sensor 197 | success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_ORIENTATION) == ICM_20948_Stat_Ok); 198 | 199 | // Enable any additional sensors / features 200 | //success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_RAW_GYROSCOPE) == ICM_20948_Stat_Ok); 201 | //success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_RAW_ACCELEROMETER) == ICM_20948_Stat_Ok); 202 | //success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED) == ICM_20948_Stat_Ok); 203 | 204 | // Configuring DMP to output data at multiple ODRs: 205 | // DMP is capable of outputting multiple sensor data at different rates to FIFO. 206 | // Setting value can be calculated as follows: 207 | // Value = (DMP running rate / ODR ) - 1 208 | // E.g. For a 5Hz ODR rate when DMP is running at 55Hz, value = (55/5) - 1 = 10. 209 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Quat9, 0) == ICM_20948_Stat_Ok); // Set to the maximum 210 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Accel, 0) == ICM_20948_Stat_Ok); // Set to the maximum 211 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Gyro, 0) == ICM_20948_Stat_Ok); // Set to the maximum 212 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Gyro_Calibr, 0) == ICM_20948_Stat_Ok); // Set to the maximum 213 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Cpass, 0) == ICM_20948_Stat_Ok); // Set to the maximum 214 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Cpass_Calibr, 0) == ICM_20948_Stat_Ok); // Set to the maximum 215 | 216 | // Enable the FIFO 217 | success &= (myICM.enableFIFO() == ICM_20948_Stat_Ok); 218 | 219 | // Enable the DMP 220 | success &= (myICM.enableDMP() == ICM_20948_Stat_Ok); 221 | 222 | // Reset DMP 223 | success &= (myICM.resetDMP() == ICM_20948_Stat_Ok); 224 | 225 | // Reset FIFO 226 | success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok); 227 | 228 | // Check success 229 | if (success) 230 | { 231 | SERIAL_PORT.println(F("DMP enabled.")); 232 | } 233 | else 234 | { 235 | SERIAL_PORT.println(F("Enable DMP failed!")); 236 | SERIAL_PORT.println(F("Please check that you have uncommented line 29 (#define ICM_20948_USE_DMP) in ICM_20948_C.h...")); 237 | while (1) 238 | ; // Do nothing more 239 | } 240 | 241 | // Read existing biases from EEPROM 242 | if (!EEPROM.begin(128)) // Allocate 128 Bytes for EEPROM storage. ESP32 needs this. 243 | { 244 | SERIAL_PORT.println(F("EEPROM.begin failed! You will not be able to save the biases...")); 245 | } 246 | 247 | biasStore store; 248 | 249 | EEPROM.get(0, store); // Read existing EEPROM, starting at address 0 250 | if (isBiasStoreValid(&store)) 251 | { 252 | SERIAL_PORT.println(F("Bias data in EEPROM is valid. Restoring it...")); 253 | success &= (myICM.setBiasGyroX(store.biasGyroX) == ICM_20948_Stat_Ok); 254 | success &= (myICM.setBiasGyroY(store.biasGyroY) == ICM_20948_Stat_Ok); 255 | success &= (myICM.setBiasGyroZ(store.biasGyroZ) == ICM_20948_Stat_Ok); 256 | success &= (myICM.setBiasAccelX(store.biasAccelX) == ICM_20948_Stat_Ok); 257 | success &= (myICM.setBiasAccelY(store.biasAccelY) == ICM_20948_Stat_Ok); 258 | success &= (myICM.setBiasAccelZ(store.biasAccelZ) == ICM_20948_Stat_Ok); 259 | success &= (myICM.setBiasCPassX(store.biasCPassX) == ICM_20948_Stat_Ok); 260 | success &= (myICM.setBiasCPassY(store.biasCPassY) == ICM_20948_Stat_Ok); 261 | success &= (myICM.setBiasCPassZ(store.biasCPassZ) == ICM_20948_Stat_Ok); 262 | 263 | if (success) 264 | { 265 | SERIAL_PORT.println(F("Biases restored.")); 266 | printBiases(&store); 267 | } 268 | else 269 | SERIAL_PORT.println(F("Bias restore failed!")); 270 | } 271 | 272 | SERIAL_PORT.println(F("The biases will be saved in two minutes.")); 273 | SERIAL_PORT.println(F("Before then:")); 274 | SERIAL_PORT.println(F("* Rotate the sensor around all three axes")); 275 | SERIAL_PORT.println(F("* Hold the sensor stationary in all six orientations for a few seconds")); 276 | } 277 | 278 | void loop() 279 | { 280 | static unsigned long startTime = millis(); // Save the biases when the code has been running for two minutes 281 | static bool biasesStored = false; 282 | 283 | // Read any DMP data waiting in the FIFO 284 | // Note: 285 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFONoDataAvail if no data is available. 286 | // If data is available, readDMPdataFromFIFO will attempt to read _one_ frame of DMP data. 287 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOIncompleteData if a frame was present but was incomplete 288 | // readDMPdataFromFIFO will return ICM_20948_Stat_Ok if a valid frame was read. 289 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOMoreDataAvail if a valid frame was read _and_ the FIFO contains more (unread) data. 290 | icm_20948_DMP_data_t data; 291 | myICM.readDMPdataFromFIFO(&data); 292 | 293 | if ((myICM.status == ICM_20948_Stat_Ok) || (myICM.status == ICM_20948_Stat_FIFOMoreDataAvail)) // Was valid data available? 294 | { 295 | //SERIAL_PORT.print(F("Received data! Header: 0x")); // Print the header in HEX so we can see what data is arriving in the FIFO 296 | //if ( data.header < 0x1000) SERIAL_PORT.print( "0" ); // Pad the zeros 297 | //if ( data.header < 0x100) SERIAL_PORT.print( "0" ); 298 | //if ( data.header < 0x10) SERIAL_PORT.print( "0" ); 299 | //SERIAL_PORT.println( data.header, HEX ); 300 | 301 | if ((data.header & DMP_header_bitmap_Quat9) > 0) // We have asked for orientation data so we should receive Quat9 302 | { 303 | // Q0 value is computed from this equation: Q0^2 + Q1^2 + Q2^2 + Q3^2 = 1. 304 | // In case of drift, the sum will not add to 1, therefore, quaternion data need to be corrected with right bias values. 305 | // The quaternion data is scaled by 2^30. 306 | 307 | //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); 308 | 309 | // Scale to +/- 1 310 | double q1 = ((double)data.Quat9.Data.Q1) / 1073741824.0; // Convert to double. Divide by 2^30 311 | double q2 = ((double)data.Quat9.Data.Q2) / 1073741824.0; // Convert to double. Divide by 2^30 312 | double q3 = ((double)data.Quat9.Data.Q3) / 1073741824.0; // Convert to double. Divide by 2^30 313 | 314 | /* 315 | SERIAL_PORT.print(F("Q1:")); 316 | SERIAL_PORT.print(q1, 3); 317 | SERIAL_PORT.print(F(" Q2:")); 318 | SERIAL_PORT.print(q2, 3); 319 | SERIAL_PORT.print(F(" Q3:")); 320 | SERIAL_PORT.print(q3, 3); 321 | SERIAL_PORT.print(F(" Accuracy:")); 322 | SERIAL_PORT.println(data.Quat9.Data.Accuracy); 323 | */ 324 | 325 | // The ICM 20948 chip has axes y-forward, x-right and Z-up - see Figure 12: 326 | // Orientation of Axes of Sensitivity and Polarity of Rotation 327 | // in DS-000189-ICM-20948-v1.6.pdf These are the axes for gyro and accel and quat 328 | // 329 | // For conversion to roll, pitch and yaw for the equations below, the coordinate frame 330 | // must be in aircraft reference frame. 331 | // 332 | // We use the Tait Bryan angles (in terms of flight dynamics): 333 | // ref: https://en.wikipedia.org/w/index.php?title=Conversion_between_quaternions_and_Euler_angles 334 | // 335 | // Heading – ψ : rotation about the Z-axis (+/- 180 deg.) 336 | // Pitch – θ : rotation about the new Y-axis (+/- 90 deg.) 337 | // Bank – ϕ : rotation about the new X-axis (+/- 180 deg.) 338 | // 339 | // where the X-axis points forward (pin 1 on chip), Y-axis to the right and Z-axis downward. 340 | // In the conversion example above the rotation occurs in the order heading, pitch, bank. 341 | // To get the roll, pitch and yaw equations to work properly we need to exchange the axes 342 | 343 | // Note when pitch approaches +/- 90 deg. the heading and bank become less meaningfull because the 344 | // device is pointing up/down. (Gimbal lock) 345 | 346 | double q0 = sqrt(1.0 - ((q1 * q1) + (q2 * q2) + (q3 * q3))); 347 | 348 | double qw = q0; // See issue #145 - thank you @Gord1 349 | double qx = q2; 350 | double qy = q1; 351 | double qz = -q3; 352 | 353 | // roll (x-axis rotation) 354 | double t0 = +2.0 * (qw * qx + qy * qz); 355 | double t1 = +1.0 - 2.0 * (qx * qx + qy * qy); 356 | double roll = atan2(t0, t1) * 180.0 / PI; 357 | 358 | // pitch (y-axis rotation) 359 | double t2 = +2.0 * (qw * qy - qx * qz); 360 | t2 = t2 > 1.0 ? 1.0 : t2; 361 | t2 = t2 < -1.0 ? -1.0 : t2; 362 | double pitch = asin(t2) * 180.0 / PI; 363 | 364 | // yaw (z-axis rotation) 365 | double t3 = +2.0 * (qw * qz + qx * qy); 366 | double t4 = +1.0 - 2.0 * (qy * qy + qz * qz); 367 | double yaw = atan2(t3, t4) * 180.0 / PI; 368 | 369 | SERIAL_PORT.print(F("Roll: ")); 370 | SERIAL_PORT.print(roll, 1); 371 | SERIAL_PORT.print(F("\tPitch: ")); 372 | SERIAL_PORT.print(pitch, 1); 373 | SERIAL_PORT.print(F("\tYaw: ")); 374 | SERIAL_PORT.println(yaw, 1); 375 | } 376 | } 377 | 378 | if (myICM.status != ICM_20948_Stat_FIFOMoreDataAvail) // If more data is available then we should read it right away - and not delay 379 | { 380 | if (!biasesStored) // Should we store the biases? 381 | { 382 | if (millis() > (startTime + 120000)) // Is it time to store the biases? 383 | { 384 | SERIAL_PORT.println(F("\r\n\r\n\r\nSaving bias data...")); 385 | 386 | biasStore store; 387 | 388 | bool success = (myICM.getBiasGyroX(&store.biasGyroX) == ICM_20948_Stat_Ok); 389 | success &= (myICM.getBiasGyroY(&store.biasGyroY) == ICM_20948_Stat_Ok); 390 | success &= (myICM.getBiasGyroZ(&store.biasGyroZ) == ICM_20948_Stat_Ok); 391 | success &= (myICM.getBiasAccelX(&store.biasAccelX) == ICM_20948_Stat_Ok); 392 | success &= (myICM.getBiasAccelY(&store.biasAccelY) == ICM_20948_Stat_Ok); 393 | success &= (myICM.getBiasAccelZ(&store.biasAccelZ) == ICM_20948_Stat_Ok); 394 | success &= (myICM.getBiasCPassX(&store.biasCPassX) == ICM_20948_Stat_Ok); 395 | success &= (myICM.getBiasCPassY(&store.biasCPassY) == ICM_20948_Stat_Ok); 396 | success &= (myICM.getBiasCPassZ(&store.biasCPassZ) == ICM_20948_Stat_Ok); 397 | 398 | updateBiasStoreSum(&store); 399 | 400 | if (success) 401 | { 402 | biasesStored = true; // Only attempt this once 403 | 404 | EEPROM.put(0, store); // Write biases to EEPROM, starting at address 0 405 | EEPROM.commit(); // ESP32/SAMD/STM32 needs this 406 | 407 | EEPROM.get(0, store); // Read existing EEPROM, starting at address 0 408 | if (isBiasStoreValid(&store)) 409 | { 410 | SERIAL_PORT.println(F("Biases stored.")); 411 | printBiases(&store); 412 | SERIAL_PORT.println(F("\r\n\r\n\r\n")); 413 | } 414 | else 415 | SERIAL_PORT.println(F("Bias store failed!\r\n\r\n\r\n")); 416 | } 417 | else 418 | { 419 | SERIAL_PORT.println(F("Bias read failed!\r\n\r\n\r\n")); 420 | } 421 | } 422 | } 423 | 424 | delay(10); 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /examples/Arduino/Example1_Basics/Example1_Basics.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Example1_Basics.ino 3 | * ICM 20948 Arduino Library Demo 4 | * Use the default configuration to stream 9-axis IMU data 5 | * Owen Lyke @ SparkFun Electronics 6 | * Original Creation Date: April 17 2019 7 | * 8 | * Please see License.md for the license information. 9 | * 10 | * Distributed as-is; no warranty is given. 11 | ***************************************************************/ 12 | #include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU 13 | 14 | //#define USE_SPI // Uncomment this to use SPI 15 | 16 | #define SERIAL_PORT Serial 17 | 18 | #define SPI_PORT SPI // Your desired SPI port. Used only when "USE_SPI" is defined 19 | #define CS_PIN 2 // Which pin you connect CS to. Used only when "USE_SPI" is defined 20 | 21 | #define WIRE_PORT Wire // Your desired Wire port. Used when "USE_SPI" is not defined 22 | // The value of the last bit of the I2C address. 23 | // On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0 24 | #define AD0_VAL 1 25 | 26 | #ifdef USE_SPI 27 | ICM_20948_SPI myICM; // If using SPI create an ICM_20948_SPI object 28 | #else 29 | ICM_20948_I2C myICM; // Otherwise create an ICM_20948_I2C object 30 | #endif 31 | 32 | void setup() 33 | { 34 | 35 | SERIAL_PORT.begin(115200); 36 | while (!SERIAL_PORT) 37 | { 38 | }; 39 | 40 | #ifdef USE_SPI 41 | SPI_PORT.begin(); 42 | #else 43 | WIRE_PORT.begin(); 44 | WIRE_PORT.setClock(400000); 45 | #endif 46 | 47 | //myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial 48 | 49 | bool initialized = false; 50 | while (!initialized) 51 | { 52 | 53 | #ifdef USE_SPI 54 | myICM.begin(CS_PIN, SPI_PORT); 55 | #else 56 | myICM.begin(WIRE_PORT, AD0_VAL); 57 | #endif 58 | 59 | SERIAL_PORT.print(F("Initialization of the sensor returned: ")); 60 | SERIAL_PORT.println(myICM.statusString()); 61 | if (myICM.status != ICM_20948_Stat_Ok) 62 | { 63 | SERIAL_PORT.println("Trying again..."); 64 | delay(500); 65 | } 66 | else 67 | { 68 | initialized = true; 69 | } 70 | } 71 | } 72 | 73 | void loop() 74 | { 75 | 76 | if (myICM.dataReady()) 77 | { 78 | myICM.getAGMT(); // The values are only updated when you call 'getAGMT' 79 | // printRawAGMT( myICM.agmt ); // Uncomment this to see the raw values, taken directly from the agmt structure 80 | printScaledAGMT(&myICM); // This function takes into account the scale settings from when the measurement was made to calculate the values with units 81 | delay(30); 82 | } 83 | else 84 | { 85 | SERIAL_PORT.println("Waiting for data"); 86 | delay(500); 87 | } 88 | } 89 | 90 | // Below here are some helper functions to print the data nicely! 91 | 92 | void printPaddedInt16b(int16_t val) 93 | { 94 | if (val > 0) 95 | { 96 | SERIAL_PORT.print(" "); 97 | if (val < 10000) 98 | { 99 | SERIAL_PORT.print("0"); 100 | } 101 | if (val < 1000) 102 | { 103 | SERIAL_PORT.print("0"); 104 | } 105 | if (val < 100) 106 | { 107 | SERIAL_PORT.print("0"); 108 | } 109 | if (val < 10) 110 | { 111 | SERIAL_PORT.print("0"); 112 | } 113 | } 114 | else 115 | { 116 | SERIAL_PORT.print("-"); 117 | if (abs(val) < 10000) 118 | { 119 | SERIAL_PORT.print("0"); 120 | } 121 | if (abs(val) < 1000) 122 | { 123 | SERIAL_PORT.print("0"); 124 | } 125 | if (abs(val) < 100) 126 | { 127 | SERIAL_PORT.print("0"); 128 | } 129 | if (abs(val) < 10) 130 | { 131 | SERIAL_PORT.print("0"); 132 | } 133 | } 134 | SERIAL_PORT.print(abs(val)); 135 | } 136 | 137 | void printRawAGMT(ICM_20948_AGMT_t agmt) 138 | { 139 | SERIAL_PORT.print("RAW. Acc [ "); 140 | printPaddedInt16b(agmt.acc.axes.x); 141 | SERIAL_PORT.print(", "); 142 | printPaddedInt16b(agmt.acc.axes.y); 143 | SERIAL_PORT.print(", "); 144 | printPaddedInt16b(agmt.acc.axes.z); 145 | SERIAL_PORT.print(" ], Gyr [ "); 146 | printPaddedInt16b(agmt.gyr.axes.x); 147 | SERIAL_PORT.print(", "); 148 | printPaddedInt16b(agmt.gyr.axes.y); 149 | SERIAL_PORT.print(", "); 150 | printPaddedInt16b(agmt.gyr.axes.z); 151 | SERIAL_PORT.print(" ], Mag [ "); 152 | printPaddedInt16b(agmt.mag.axes.x); 153 | SERIAL_PORT.print(", "); 154 | printPaddedInt16b(agmt.mag.axes.y); 155 | SERIAL_PORT.print(", "); 156 | printPaddedInt16b(agmt.mag.axes.z); 157 | SERIAL_PORT.print(" ], Tmp [ "); 158 | printPaddedInt16b(agmt.tmp.val); 159 | SERIAL_PORT.print(" ]"); 160 | SERIAL_PORT.println(); 161 | } 162 | 163 | void printFormattedFloat(float val, uint8_t leading, uint8_t decimals) 164 | { 165 | float aval = abs(val); 166 | if (val < 0) 167 | { 168 | SERIAL_PORT.print("-"); 169 | } 170 | else 171 | { 172 | SERIAL_PORT.print(" "); 173 | } 174 | for (uint8_t indi = 0; indi < leading; indi++) 175 | { 176 | uint32_t tenpow = 0; 177 | if (indi < (leading - 1)) 178 | { 179 | tenpow = 1; 180 | } 181 | for (uint8_t c = 0; c < (leading - 1 - indi); c++) 182 | { 183 | tenpow *= 10; 184 | } 185 | if (aval < tenpow) 186 | { 187 | SERIAL_PORT.print("0"); 188 | } 189 | else 190 | { 191 | break; 192 | } 193 | } 194 | if (val < 0) 195 | { 196 | SERIAL_PORT.print(-val, decimals); 197 | } 198 | else 199 | { 200 | SERIAL_PORT.print(val, decimals); 201 | } 202 | } 203 | 204 | #ifdef USE_SPI 205 | void printScaledAGMT(ICM_20948_SPI *sensor) 206 | { 207 | #else 208 | void printScaledAGMT(ICM_20948_I2C *sensor) 209 | { 210 | #endif 211 | SERIAL_PORT.print("Scaled. Acc (mg) [ "); 212 | printFormattedFloat(sensor->accX(), 5, 2); 213 | SERIAL_PORT.print(", "); 214 | printFormattedFloat(sensor->accY(), 5, 2); 215 | SERIAL_PORT.print(", "); 216 | printFormattedFloat(sensor->accZ(), 5, 2); 217 | SERIAL_PORT.print(" ], Gyr (DPS) [ "); 218 | printFormattedFloat(sensor->gyrX(), 5, 2); 219 | SERIAL_PORT.print(", "); 220 | printFormattedFloat(sensor->gyrY(), 5, 2); 221 | SERIAL_PORT.print(", "); 222 | printFormattedFloat(sensor->gyrZ(), 5, 2); 223 | SERIAL_PORT.print(" ], Mag (uT) [ "); 224 | printFormattedFloat(sensor->magX(), 5, 2); 225 | SERIAL_PORT.print(", "); 226 | printFormattedFloat(sensor->magY(), 5, 2); 227 | SERIAL_PORT.print(", "); 228 | printFormattedFloat(sensor->magZ(), 5, 2); 229 | SERIAL_PORT.print(" ], Tmp (C) [ "); 230 | printFormattedFloat(sensor->temp(), 5, 2); 231 | SERIAL_PORT.print(" ]"); 232 | SERIAL_PORT.println(); 233 | } 234 | -------------------------------------------------------------------------------- /examples/Arduino/Example2_Advanced/Example2_Advanced.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Example2_Advanced.ino 3 | * ICM 20948 Arduino Library Demo 4 | * Shows how to use granular configuration of the ICM 20948 5 | * Owen Lyke @ SparkFun Electronics 6 | * Original Creation Date: April 17 2019 7 | * 8 | * Please see License.md for the license information. 9 | * 10 | * Distributed as-is; no warranty is given. 11 | ***************************************************************/ 12 | #include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU 13 | 14 | //#define USE_SPI // Uncomment this to use SPI 15 | 16 | #define SERIAL_PORT Serial 17 | 18 | #define SPI_PORT SPI // Your desired SPI port. Used only when "USE_SPI" is defined 19 | #define SPI_FREQ 5000000 // You can override the default SPI frequency 20 | #define CS_PIN 2 // Which pin you connect CS to. Used only when "USE_SPI" is defined 21 | 22 | #define WIRE_PORT Wire // Your desired Wire port. Used when "USE_SPI" is not defined 23 | // The value of the last bit of the I2C address. 24 | // On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0 25 | #define AD0_VAL 1 26 | 27 | #ifdef USE_SPI 28 | ICM_20948_SPI myICM; // If using SPI create an ICM_20948_SPI object 29 | #else 30 | ICM_20948_I2C myICM; // Otherwise create an ICM_20948_I2C object 31 | #endif 32 | 33 | void setup() 34 | { 35 | 36 | SERIAL_PORT.begin(115200); 37 | while (!SERIAL_PORT) 38 | { 39 | }; 40 | 41 | #ifdef USE_SPI 42 | SPI_PORT.begin(); 43 | #else 44 | WIRE_PORT.begin(); 45 | WIRE_PORT.setClock(400000); 46 | #endif 47 | 48 | //myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial 49 | 50 | bool initialized = false; 51 | while (!initialized) 52 | { 53 | 54 | #ifdef USE_SPI 55 | 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 56 | #else 57 | myICM.begin(WIRE_PORT, AD0_VAL); 58 | #endif 59 | 60 | SERIAL_PORT.print(F("Initialization of the sensor returned: ")); 61 | SERIAL_PORT.println(myICM.statusString()); 62 | if (myICM.status != ICM_20948_Stat_Ok) 63 | { 64 | SERIAL_PORT.println("Trying again..."); 65 | delay(500); 66 | } 67 | else 68 | { 69 | initialized = true; 70 | } 71 | } 72 | 73 | // In this advanced example we'll cover how to do a more fine-grained setup of your sensor 74 | SERIAL_PORT.println("Device connected!"); 75 | 76 | // Here we are doing a SW reset to make sure the device starts in a known state 77 | myICM.swReset(); 78 | if (myICM.status != ICM_20948_Stat_Ok) 79 | { 80 | SERIAL_PORT.print(F("Software Reset returned: ")); 81 | SERIAL_PORT.println(myICM.statusString()); 82 | } 83 | delay(250); 84 | 85 | // Now wake the sensor up 86 | myICM.sleep(false); 87 | myICM.lowPower(false); 88 | 89 | // The next few configuration functions accept a bit-mask of sensors for which the settings should be applied. 90 | 91 | // Set Gyro and Accelerometer to a particular sample mode 92 | // options: ICM_20948_Sample_Mode_Continuous 93 | // ICM_20948_Sample_Mode_Cycled 94 | myICM.setSampleMode((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Continuous); 95 | if (myICM.status != ICM_20948_Stat_Ok) 96 | { 97 | SERIAL_PORT.print(F("setSampleMode returned: ")); 98 | SERIAL_PORT.println(myICM.statusString()); 99 | } 100 | 101 | // Set full scale ranges for both acc and gyr 102 | ICM_20948_fss_t myFSS; // This uses a "Full Scale Settings" structure that can contain values for all configurable sensors 103 | 104 | myFSS.a = gpm2; // (ICM_20948_ACCEL_CONFIG_FS_SEL_e) 105 | // gpm2 106 | // gpm4 107 | // gpm8 108 | // gpm16 109 | 110 | myFSS.g = dps250; // (ICM_20948_GYRO_CONFIG_1_FS_SEL_e) 111 | // dps250 112 | // dps500 113 | // dps1000 114 | // dps2000 115 | 116 | myICM.setFullScale((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myFSS); 117 | if (myICM.status != ICM_20948_Stat_Ok) 118 | { 119 | SERIAL_PORT.print(F("setFullScale returned: ")); 120 | SERIAL_PORT.println(myICM.statusString()); 121 | } 122 | 123 | // Set up Digital Low-Pass Filter configuration 124 | ICM_20948_dlpcfg_t myDLPcfg; // Similar to FSS, this uses a configuration structure for the desired sensors 125 | myDLPcfg.a = acc_d473bw_n499bw; // (ICM_20948_ACCEL_CONFIG_DLPCFG_e) 126 | // acc_d246bw_n265bw - means 3db bandwidth is 246 hz and nyquist bandwidth is 265 hz 127 | // acc_d111bw4_n136bw 128 | // acc_d50bw4_n68bw8 129 | // acc_d23bw9_n34bw4 130 | // acc_d11bw5_n17bw 131 | // acc_d5bw7_n8bw3 - means 3 db bandwidth is 5.7 hz and nyquist bandwidth is 8.3 hz 132 | // acc_d473bw_n499bw 133 | 134 | myDLPcfg.g = gyr_d361bw4_n376bw5; // (ICM_20948_GYRO_CONFIG_1_DLPCFG_e) 135 | // gyr_d196bw6_n229bw8 136 | // gyr_d151bw8_n187bw6 137 | // gyr_d119bw5_n154bw3 138 | // gyr_d51bw2_n73bw3 139 | // gyr_d23bw9_n35bw9 140 | // gyr_d11bw6_n17bw8 141 | // gyr_d5bw7_n8bw9 142 | // gyr_d361bw4_n376bw5 143 | 144 | myICM.setDLPFcfg((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myDLPcfg); 145 | if (myICM.status != ICM_20948_Stat_Ok) 146 | { 147 | SERIAL_PORT.print(F("setDLPcfg returned: ")); 148 | SERIAL_PORT.println(myICM.statusString()); 149 | } 150 | 151 | // Choose whether or not to use DLPF 152 | // 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 153 | ICM_20948_Status_e accDLPEnableStat = myICM.enableDLPF(ICM_20948_Internal_Acc, false); 154 | ICM_20948_Status_e gyrDLPEnableStat = myICM.enableDLPF(ICM_20948_Internal_Gyr, false); 155 | SERIAL_PORT.print(F("Enable DLPF for Accelerometer returned: ")); 156 | SERIAL_PORT.println(myICM.statusString(accDLPEnableStat)); 157 | SERIAL_PORT.print(F("Enable DLPF for Gyroscope returned: ")); 158 | SERIAL_PORT.println(myICM.statusString(gyrDLPEnableStat)); 159 | 160 | // Choose whether or not to start the magnetometer 161 | myICM.startupMagnetometer(); 162 | if (myICM.status != ICM_20948_Stat_Ok) 163 | { 164 | SERIAL_PORT.print(F("startupMagnetometer returned: ")); 165 | SERIAL_PORT.println(myICM.statusString()); 166 | } 167 | 168 | SERIAL_PORT.println(); 169 | SERIAL_PORT.println(F("Configuration complete!")); 170 | } 171 | 172 | void loop() 173 | { 174 | 175 | if (myICM.dataReady()) 176 | { 177 | myICM.getAGMT(); // The values are only updated when you call 'getAGMT' 178 | //printRawAGMT( myICM.agmt ); // Uncomment this to see the raw values, taken directly from the agmt structure 179 | printScaledAGMT(&myICM); // This function takes into account the scale settings from when the measurement was made to calculate the values with units 180 | delay(30); 181 | } 182 | else 183 | { 184 | SERIAL_PORT.println("Waiting for data"); 185 | delay(500); 186 | } 187 | } 188 | 189 | // Below here are some helper functions to print the data nicely! 190 | 191 | void printPaddedInt16b(int16_t val) 192 | { 193 | if (val > 0) 194 | { 195 | SERIAL_PORT.print(" "); 196 | if (val < 10000) 197 | { 198 | SERIAL_PORT.print("0"); 199 | } 200 | if (val < 1000) 201 | { 202 | SERIAL_PORT.print("0"); 203 | } 204 | if (val < 100) 205 | { 206 | SERIAL_PORT.print("0"); 207 | } 208 | if (val < 10) 209 | { 210 | SERIAL_PORT.print("0"); 211 | } 212 | } 213 | else 214 | { 215 | SERIAL_PORT.print("-"); 216 | if (abs(val) < 10000) 217 | { 218 | SERIAL_PORT.print("0"); 219 | } 220 | if (abs(val) < 1000) 221 | { 222 | SERIAL_PORT.print("0"); 223 | } 224 | if (abs(val) < 100) 225 | { 226 | SERIAL_PORT.print("0"); 227 | } 228 | if (abs(val) < 10) 229 | { 230 | SERIAL_PORT.print("0"); 231 | } 232 | } 233 | SERIAL_PORT.print(abs(val)); 234 | } 235 | 236 | void printRawAGMT(ICM_20948_AGMT_t agmt) 237 | { 238 | SERIAL_PORT.print("RAW. Acc [ "); 239 | printPaddedInt16b(agmt.acc.axes.x); 240 | SERIAL_PORT.print(", "); 241 | printPaddedInt16b(agmt.acc.axes.y); 242 | SERIAL_PORT.print(", "); 243 | printPaddedInt16b(agmt.acc.axes.z); 244 | SERIAL_PORT.print(" ], Gyr [ "); 245 | printPaddedInt16b(agmt.gyr.axes.x); 246 | SERIAL_PORT.print(", "); 247 | printPaddedInt16b(agmt.gyr.axes.y); 248 | SERIAL_PORT.print(", "); 249 | printPaddedInt16b(agmt.gyr.axes.z); 250 | SERIAL_PORT.print(" ], Mag [ "); 251 | printPaddedInt16b(agmt.mag.axes.x); 252 | SERIAL_PORT.print(", "); 253 | printPaddedInt16b(agmt.mag.axes.y); 254 | SERIAL_PORT.print(", "); 255 | printPaddedInt16b(agmt.mag.axes.z); 256 | SERIAL_PORT.print(" ], Tmp [ "); 257 | printPaddedInt16b(agmt.tmp.val); 258 | SERIAL_PORT.print(" ]"); 259 | SERIAL_PORT.println(); 260 | } 261 | 262 | void printFormattedFloat(float val, uint8_t leading, uint8_t decimals) 263 | { 264 | float aval = abs(val); 265 | if (val < 0) 266 | { 267 | SERIAL_PORT.print("-"); 268 | } 269 | else 270 | { 271 | SERIAL_PORT.print(" "); 272 | } 273 | for (uint8_t indi = 0; indi < leading; indi++) 274 | { 275 | uint32_t tenpow = 0; 276 | if (indi < (leading - 1)) 277 | { 278 | tenpow = 1; 279 | } 280 | for (uint8_t c = 0; c < (leading - 1 - indi); c++) 281 | { 282 | tenpow *= 10; 283 | } 284 | if (aval < tenpow) 285 | { 286 | SERIAL_PORT.print("0"); 287 | } 288 | else 289 | { 290 | break; 291 | } 292 | } 293 | if (val < 0) 294 | { 295 | SERIAL_PORT.print(-val, decimals); 296 | } 297 | else 298 | { 299 | SERIAL_PORT.print(val, decimals); 300 | } 301 | } 302 | 303 | #ifdef USE_SPI 304 | void printScaledAGMT(ICM_20948_SPI *sensor) 305 | { 306 | #else 307 | void printScaledAGMT(ICM_20948_I2C *sensor) 308 | { 309 | #endif 310 | SERIAL_PORT.print("Scaled. Acc (mg) [ "); 311 | printFormattedFloat(sensor->accX(), 5, 2); 312 | SERIAL_PORT.print(", "); 313 | printFormattedFloat(sensor->accY(), 5, 2); 314 | SERIAL_PORT.print(", "); 315 | printFormattedFloat(sensor->accZ(), 5, 2); 316 | SERIAL_PORT.print(" ], Gyr (DPS) [ "); 317 | printFormattedFloat(sensor->gyrX(), 5, 2); 318 | SERIAL_PORT.print(", "); 319 | printFormattedFloat(sensor->gyrY(), 5, 2); 320 | SERIAL_PORT.print(", "); 321 | printFormattedFloat(sensor->gyrZ(), 5, 2); 322 | SERIAL_PORT.print(" ], Mag (uT) [ "); 323 | printFormattedFloat(sensor->magX(), 5, 2); 324 | SERIAL_PORT.print(", "); 325 | printFormattedFloat(sensor->magY(), 5, 2); 326 | SERIAL_PORT.print(", "); 327 | printFormattedFloat(sensor->magZ(), 5, 2); 328 | SERIAL_PORT.print(" ], Tmp (C) [ "); 329 | printFormattedFloat(sensor->temp(), 5, 2); 330 | SERIAL_PORT.print(" ]"); 331 | SERIAL_PORT.println(); 332 | } 333 | -------------------------------------------------------------------------------- /examples/Arduino/Example3_Interrupts/Example3_Interrupts.ino: -------------------------------------------------------------------------------- 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 | * Please see License.md for the license information. 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 | // The value of the last bit of the I2C address. 32 | // On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0 33 | #define AD0_VAL 1 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 | 46 | void setup() 47 | { 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 | }; 59 | 60 | #ifdef USE_SPI 61 | SPI_PORT.begin(); 62 | #else 63 | WIRE_PORT.begin(); 64 | WIRE_PORT.setClock(400000); 65 | #endif 66 | 67 | //myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial 68 | 69 | bool initialized = false; 70 | while (!initialized) 71 | { 72 | 73 | #ifdef USE_SPI 74 | 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 75 | #else 76 | myICM.begin(WIRE_PORT, AD0_VAL); 77 | #endif 78 | 79 | SERIAL_PORT.print(F("Initialization of the sensor returned: ")); 80 | SERIAL_PORT.println(myICM.statusString()); 81 | if (myICM.status != ICM_20948_Stat_Ok) 82 | { 83 | SERIAL_PORT.println("Trying again..."); 84 | delay(500); 85 | } 86 | else 87 | { 88 | initialized = true; 89 | } 90 | } 91 | 92 | // In this advanced example we'll cover how to do a more fine-grained setup of your sensor 93 | SERIAL_PORT.println("Device connected!"); 94 | 95 | // Here we are doing a SW reset to make sure the device starts in a known state 96 | myICM.swReset(); 97 | if (myICM.status != ICM_20948_Stat_Ok) 98 | { 99 | SERIAL_PORT.print(F("Software Reset returned: ")); 100 | SERIAL_PORT.println(myICM.statusString()); 101 | } 102 | delay(250); 103 | 104 | // Now wake the sensor up 105 | myICM.sleep(sensorSleep); 106 | myICM.lowPower(false); 107 | 108 | // The next few configuration functions accept a bit-mask of sensors for which the settings should be applied. 109 | 110 | // Set Gyro and Accelerometer to a particular sample mode 111 | // options: ICM_20948_Sample_Mode_Continuous 112 | // ICM_20948_Sample_Mode_Cycled 113 | myICM.setSampleMode((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Cycled); 114 | SERIAL_PORT.print(F("setSampleMode returned: ")); 115 | SERIAL_PORT.println(myICM.statusString()); 116 | 117 | ICM_20948_smplrt_t mySmplrt; 118 | mySmplrt.g = 54; 119 | myICM.setSampleRate(ICM_20948_Internal_Gyr, mySmplrt); 120 | SERIAL_PORT.print(F("setSampleRate returned: ")); 121 | SERIAL_PORT.println(myICM.statusString()); 122 | 123 | // Set full scale ranges for both acc and gyr 124 | ICM_20948_fss_t myFSS; // This uses a "Full Scale Settings" structure that can contain values for all configurable sensors 125 | 126 | myFSS.a = gpm2; // (ICM_20948_ACCEL_CONFIG_FS_SEL_e) 127 | // gpm2 128 | // gpm4 129 | // gpm8 130 | // gpm16 131 | 132 | myFSS.g = dps250; // (ICM_20948_GYRO_CONFIG_1_FS_SEL_e) 133 | // dps250 134 | // dps500 135 | // dps1000 136 | // dps2000 137 | 138 | myICM.setFullScale((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myFSS); 139 | if (myICM.status != ICM_20948_Stat_Ok) 140 | { 141 | SERIAL_PORT.print(F("setFullScale returned: ")); 142 | SERIAL_PORT.println(myICM.statusString()); 143 | } 144 | 145 | // Set up Digital Low-Pass Filter configuration 146 | ICM_20948_dlpcfg_t myDLPcfg; // Similar to FSS, this uses a configuration structure for the desired sensors 147 | myDLPcfg.a = acc_d473bw_n499bw; // (ICM_20948_ACCEL_CONFIG_DLPCFG_e) 148 | // acc_d246bw_n265bw - means 3db bandwidth is 246 hz and nyquist bandwidth is 265 hz 149 | // acc_d111bw4_n136bw 150 | // acc_d50bw4_n68bw8 151 | // acc_d23bw9_n34bw4 152 | // acc_d11bw5_n17bw 153 | // acc_d5bw7_n8bw3 - means 3 db bandwidth is 5.7 hz and nyquist bandwidth is 8.3 hz 154 | // acc_d473bw_n499bw 155 | 156 | myDLPcfg.g = gyr_d361bw4_n376bw5; // (ICM_20948_GYRO_CONFIG_1_DLPCFG_e) 157 | // gyr_d196bw6_n229bw8 158 | // gyr_d151bw8_n187bw6 159 | // gyr_d119bw5_n154bw3 160 | // gyr_d51bw2_n73bw3 161 | // gyr_d23bw9_n35bw9 162 | // gyr_d11bw6_n17bw8 163 | // gyr_d5bw7_n8bw9 164 | // gyr_d361bw4_n376bw5 165 | 166 | myICM.setDLPFcfg((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myDLPcfg); 167 | if (myICM.status != ICM_20948_Stat_Ok) 168 | { 169 | SERIAL_PORT.print(F("setDLPcfg returned: ")); 170 | SERIAL_PORT.println(myICM.statusString()); 171 | } 172 | 173 | // Choose whether or not to use DLPF 174 | // 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 175 | ICM_20948_Status_e accDLPEnableStat = myICM.enableDLPF(ICM_20948_Internal_Acc, true); 176 | ICM_20948_Status_e gyrDLPEnableStat = myICM.enableDLPF(ICM_20948_Internal_Gyr, true); 177 | SERIAL_PORT.print(F("Enable DLPF for Accelerometer returned: ")); 178 | SERIAL_PORT.println(myICM.statusString(accDLPEnableStat)); 179 | SERIAL_PORT.print(F("Enable DLPF for Gyroscope returned: ")); 180 | SERIAL_PORT.println(myICM.statusString(gyrDLPEnableStat)); 181 | 182 | // Choose whether or not to start the magnetometer 183 | myICM.startupMagnetometer(); 184 | if (myICM.status != ICM_20948_Stat_Ok) 185 | { 186 | SERIAL_PORT.print(F("startupMagnetometer returned: ")); 187 | SERIAL_PORT.println(myICM.statusString()); 188 | } 189 | 190 | // 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 191 | /* 192 | ICM_20948_Status_e cfgIntActiveLow ( bool active_low ); 193 | ICM_20948_Status_e cfgIntOpenDrain ( bool open_drain ); 194 | ICM_20948_Status_e cfgIntLatch ( bool latching ); // If not latching then the interrupt is a 50 us pulse 195 | 196 | 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 197 | 198 | ICM_20948_Status_e cfgFsyncActiveLow ( bool active_low ); 199 | 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 200 | 201 | ICM_20948_Status_e intEnableI2C ( bool enable ); 202 | ICM_20948_Status_e intEnableDMP ( bool enable ); 203 | ICM_20948_Status_e intEnablePLL ( bool enable ); 204 | ICM_20948_Status_e intEnableWOM ( bool enable ); 205 | ICM_20948_Status_e intEnableWOF ( bool enable ); 206 | ICM_20948_Status_e intEnableRawDataReady ( bool enable ); 207 | ICM_20948_Status_e intEnableOverflowFIFO ( uint8_t bm_enable ); 208 | ICM_20948_Status_e intEnableWatermarkFIFO ( uint8_t bm_enable ); 209 | */ 210 | myICM.cfgIntActiveLow(true); // Active low to be compatible with the breakout board's pullup resistor 211 | myICM.cfgIntOpenDrain(false); // Push-pull, though open-drain would also work thanks to the pull-up resistors on the breakout 212 | myICM.cfgIntLatch(true); // Latch the interrupt until cleared 213 | SERIAL_PORT.print(F("cfgIntLatch returned: ")); 214 | SERIAL_PORT.println(myICM.statusString()); 215 | 216 | myICM.intEnableRawDataReady(true); // enable interrupts on raw data ready 217 | SERIAL_PORT.print(F("intEnableRawDataReady returned: ")); 218 | SERIAL_PORT.println(myICM.statusString()); 219 | 220 | // // Note: weirdness with the Wake on Motion interrupt being always enabled..... 221 | // uint8_t zero_0 = 0xFF; 222 | // ICM_20948_execute_r( &myICM._device, AGB0_REG_INT_ENABLE, (uint8_t*)&zero_0, sizeof(uint8_t) ); 223 | // SERIAL_PORT.print("INT_EN was: 0x"); SERIAL_PORT.println(zero_0, HEX); 224 | // zero_0 = 0x00; 225 | // ICM_20948_execute_w( &myICM._device, AGB0_REG_INT_ENABLE, (uint8_t*)&zero_0, sizeof(uint8_t) ); 226 | 227 | SERIAL_PORT.println(); 228 | SERIAL_PORT.println(F("Configuration complete!")); 229 | } 230 | 231 | void loop() 232 | { 233 | if (isrFired) 234 | { // If our isr flag is set then clear the interrupts on the ICM 235 | isrFired = false; 236 | myICM.getAGMT(); // get the A, G, M, and T readings 237 | printScaledAGMT(&myICM); // This function takes into account the scale settings from when the measurement was made to calculate the values with units 238 | //myICM.clearInterrupts(); // This would be efficient... but not compatible with Uno 239 | } 240 | 241 | myICM.clearInterrupts(); // clear interrupts for next time - 242 | // usually you'd do this only if an interrupt has occurred, however 243 | // on the 328p I2C usage can block interrupts. This means that sometimes 244 | // an interrupt is missed. When missed, if using an edge-based interrupt 245 | // and only clearing interrupts when one was detected there will be no more 246 | // edges to respond to, so no more interrupts will be detected. Here are 247 | // some possible solutions: 248 | // 1. use a level based interrupt 249 | // 2. use the pulse-based interrupt in ICM settings (set cfgIntLatch to false) 250 | // 3. use a microcontroller with nestable interrupts 251 | // 4. clear the interrupts often 252 | 253 | if ((millis() % 1000) < 5) 254 | { // This is a method to turn the sensor on and off once per second without using delays 255 | if (canToggle) 256 | { 257 | sensorSleep = !sensorSleep; 258 | myICM.sleep(sensorSleep); 259 | digitalWrite(LED_PIN, !sensorSleep); 260 | canToggle = false; 261 | } 262 | } 263 | if ((millis() % 1000) > 500) 264 | { 265 | canToggle = true; 266 | } 267 | } 268 | 269 | void icmISR(void) 270 | { 271 | isrFired = true; // Can't use I2C within ISR on 328p, so just set a flag to know that data is available 272 | } 273 | 274 | // Below here are some helper functions to print the data nicely! 275 | void printPaddedInt16b(int16_t val) 276 | { 277 | if (val > 0) 278 | { 279 | SERIAL_PORT.print(" "); 280 | if (val < 10000) 281 | { 282 | SERIAL_PORT.print("0"); 283 | } 284 | if (val < 1000) 285 | { 286 | SERIAL_PORT.print("0"); 287 | } 288 | if (val < 100) 289 | { 290 | SERIAL_PORT.print("0"); 291 | } 292 | if (val < 10) 293 | { 294 | SERIAL_PORT.print("0"); 295 | } 296 | } 297 | else 298 | { 299 | SERIAL_PORT.print("-"); 300 | if (abs(val) < 10000) 301 | { 302 | SERIAL_PORT.print("0"); 303 | } 304 | if (abs(val) < 1000) 305 | { 306 | SERIAL_PORT.print("0"); 307 | } 308 | if (abs(val) < 100) 309 | { 310 | SERIAL_PORT.print("0"); 311 | } 312 | if (abs(val) < 10) 313 | { 314 | SERIAL_PORT.print("0"); 315 | } 316 | } 317 | SERIAL_PORT.print(abs(val)); 318 | } 319 | 320 | void printRawAGMT(ICM_20948_AGMT_t agmt) 321 | { 322 | SERIAL_PORT.print("RAW. Acc [ "); 323 | printPaddedInt16b(agmt.acc.axes.x); 324 | SERIAL_PORT.print(", "); 325 | printPaddedInt16b(agmt.acc.axes.y); 326 | SERIAL_PORT.print(", "); 327 | printPaddedInt16b(agmt.acc.axes.z); 328 | SERIAL_PORT.print(" ], Gyr [ "); 329 | printPaddedInt16b(agmt.gyr.axes.x); 330 | SERIAL_PORT.print(", "); 331 | printPaddedInt16b(agmt.gyr.axes.y); 332 | SERIAL_PORT.print(", "); 333 | printPaddedInt16b(agmt.gyr.axes.z); 334 | SERIAL_PORT.print(" ], Mag [ "); 335 | printPaddedInt16b(agmt.mag.axes.x); 336 | SERIAL_PORT.print(", "); 337 | printPaddedInt16b(agmt.mag.axes.y); 338 | SERIAL_PORT.print(", "); 339 | printPaddedInt16b(agmt.mag.axes.z); 340 | SERIAL_PORT.print(" ], Tmp [ "); 341 | printPaddedInt16b(agmt.tmp.val); 342 | SERIAL_PORT.print(" ]"); 343 | SERIAL_PORT.println(); 344 | } 345 | 346 | void printFormattedFloat(float val, uint8_t leading, uint8_t decimals) 347 | { 348 | float aval = abs(val); 349 | if (val < 0) 350 | { 351 | SERIAL_PORT.print("-"); 352 | } 353 | else 354 | { 355 | SERIAL_PORT.print(" "); 356 | } 357 | for (uint8_t indi = 0; indi < leading; indi++) 358 | { 359 | uint32_t tenpow = 0; 360 | if (indi < (leading - 1)) 361 | { 362 | tenpow = 1; 363 | } 364 | for (uint8_t c = 0; c < (leading - 1 - indi); c++) 365 | { 366 | tenpow *= 10; 367 | } 368 | if (aval < tenpow) 369 | { 370 | SERIAL_PORT.print("0"); 371 | } 372 | else 373 | { 374 | break; 375 | } 376 | } 377 | if (val < 0) 378 | { 379 | SERIAL_PORT.print(-val, decimals); 380 | } 381 | else 382 | { 383 | SERIAL_PORT.print(val, decimals); 384 | } 385 | } 386 | 387 | #ifdef USE_SPI 388 | void printScaledAGMT(ICM_20948_SPI *sensor) 389 | { 390 | #else 391 | void printScaledAGMT(ICM_20948_I2C *sensor) 392 | { 393 | #endif 394 | SERIAL_PORT.print("Scaled. Acc (mg) [ "); 395 | printFormattedFloat(sensor->accX(), 5, 2); 396 | SERIAL_PORT.print(", "); 397 | printFormattedFloat(sensor->accY(), 5, 2); 398 | SERIAL_PORT.print(", "); 399 | printFormattedFloat(sensor->accZ(), 5, 2); 400 | SERIAL_PORT.print(" ], Gyr (DPS) [ "); 401 | printFormattedFloat(sensor->gyrX(), 5, 2); 402 | SERIAL_PORT.print(", "); 403 | printFormattedFloat(sensor->gyrY(), 5, 2); 404 | SERIAL_PORT.print(", "); 405 | printFormattedFloat(sensor->gyrZ(), 5, 2); 406 | SERIAL_PORT.print(" ], Mag (uT) [ "); 407 | printFormattedFloat(sensor->magX(), 5, 2); 408 | SERIAL_PORT.print(", "); 409 | printFormattedFloat(sensor->magY(), 5, 2); 410 | SERIAL_PORT.print(", "); 411 | printFormattedFloat(sensor->magZ(), 5, 2); 412 | SERIAL_PORT.print(" ], Tmp (C) [ "); 413 | printFormattedFloat(sensor->temp(), 5, 2); 414 | SERIAL_PORT.print(" ]"); 415 | SERIAL_PORT.println(); 416 | } 417 | -------------------------------------------------------------------------------- /examples/Arduino/Example4_WakeOnMotion/Example4_WakeOnMotion.ino: -------------------------------------------------------------------------------- 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 | * Please see License.md for the license information. 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 | // The value of the last bit of the I2C address. 32 | // On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0 33 | #define AD0_VAL 1 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 long lastTriggered; 46 | 47 | // Threshold LSBit is 4mg. Range is 0mg to 1020mg. 48 | unsigned int WOM_threshold = 255; 49 | 50 | void setup() 51 | { 52 | 53 | 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 54 | attachInterrupt(digitalPinToInterrupt(INT_PIN), icmISR, FALLING); // Set up a falling interrupt 55 | 56 | pinMode(LED_PIN, OUTPUT); 57 | digitalWrite(LED_PIN, LOW); 58 | 59 | SERIAL_PORT.begin(115200); 60 | while (!SERIAL_PORT) 61 | { 62 | }; 63 | 64 | #ifdef USE_SPI 65 | SPI_PORT.begin(); 66 | #else 67 | WIRE_PORT.begin(); 68 | WIRE_PORT.setClock(400000); 69 | #endif 70 | 71 | //myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial 72 | 73 | bool initialized = false; 74 | while (!initialized) 75 | { 76 | 77 | #ifdef USE_SPI 78 | 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 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 | { 87 | SERIAL_PORT.println("Trying again..."); 88 | delay(500); 89 | } 90 | else 91 | { 92 | initialized = true; 93 | } 94 | } 95 | 96 | // In this advanced example we'll cover how to do WakeOn Motion action using interrupts 97 | SERIAL_PORT.println("Device connected!"); 98 | 99 | // Here we are doing a SW reset to make sure the device starts in a known state 100 | myICM.swReset(); 101 | if (myICM.status != ICM_20948_Stat_Ok) 102 | { 103 | SERIAL_PORT.print(F("Software Reset returned: ")); 104 | SERIAL_PORT.println(myICM.statusString()); 105 | } 106 | delay(250); 107 | 108 | // Now wake the sensor up 109 | myICM.sleep(sensorSleep); 110 | myICM.lowPower(false); 111 | 112 | // The next few configuration functions accept a bit-mask of sensors for which the settings should be applied. 113 | 114 | // Set Gyro and Accelerometer to a particular sample mode 115 | // options: ICM_20948_Sample_Mode_Continuous 116 | // ICM_20948_Sample_Mode_Cycled 117 | myICM.setSampleMode((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Cycled); 118 | SERIAL_PORT.print(F("setSampleMode returned: ")); 119 | SERIAL_PORT.println(myICM.statusString()); 120 | 121 | ICM_20948_smplrt_t mySmplrt; 122 | mySmplrt.g = 54; 123 | myICM.setSampleRate(ICM_20948_Internal_Gyr, mySmplrt); 124 | SERIAL_PORT.print(F("setSampleRate returned: ")); 125 | SERIAL_PORT.println(myICM.statusString()); 126 | 127 | // Set full scale ranges for both acc and gyr 128 | ICM_20948_fss_t myFSS; // This uses a "Full Scale Settings" structure that can contain values for all configurable sensors 129 | 130 | myFSS.a = gpm2; // (ICM_20948_ACCEL_CONFIG_FS_SEL_e) 131 | // gpm2 132 | // gpm4 133 | // gpm8 134 | // gpm16 135 | 136 | myFSS.g = dps250; // (ICM_20948_GYRO_CONFIG_1_FS_SEL_e) 137 | // dps250 138 | // dps500 139 | // dps1000 140 | // dps2000 141 | 142 | myICM.setFullScale((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myFSS); 143 | if (myICM.status != ICM_20948_Stat_Ok) 144 | { 145 | SERIAL_PORT.print(F("setFullScale returned: ")); 146 | SERIAL_PORT.println(myICM.statusString()); 147 | } 148 | 149 | // Set up Digital Low-Pass Filter configuration 150 | ICM_20948_dlpcfg_t myDLPcfg; // Similar to FSS, this uses a configuration structure for the desired sensors 151 | myDLPcfg.a = acc_d473bw_n499bw; // (ICM_20948_ACCEL_CONFIG_DLPCFG_e) 152 | // acc_d246bw_n265bw - means 3db bandwidth is 246 hz and nyquist bandwidth is 265 hz 153 | // acc_d111bw4_n136bw 154 | // acc_d50bw4_n68bw8 155 | // acc_d23bw9_n34bw4 156 | // acc_d11bw5_n17bw 157 | // acc_d5bw7_n8bw3 - means 3 db bandwidth is 5.7 hz and nyquist bandwidth is 8.3 hz 158 | // acc_d473bw_n499bw 159 | 160 | myDLPcfg.g = gyr_d361bw4_n376bw5; // (ICM_20948_GYRO_CONFIG_1_DLPCFG_e) 161 | // gyr_d196bw6_n229bw8 162 | // gyr_d151bw8_n187bw6 163 | // gyr_d119bw5_n154bw3 164 | // gyr_d51bw2_n73bw3 165 | // gyr_d23bw9_n35bw9 166 | // gyr_d11bw6_n17bw8 167 | // gyr_d5bw7_n8bw9 168 | // gyr_d361bw4_n376bw5 169 | 170 | myICM.setDLPFcfg((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myDLPcfg); 171 | if (myICM.status != ICM_20948_Stat_Ok) 172 | { 173 | SERIAL_PORT.print(F("setDLPcfg returned: ")); 174 | SERIAL_PORT.println(myICM.statusString()); 175 | } 176 | 177 | // Choose whether or not to use DLPF 178 | // 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 179 | ICM_20948_Status_e accDLPEnableStat = myICM.enableDLPF(ICM_20948_Internal_Acc, true); 180 | ICM_20948_Status_e gyrDLPEnableStat = myICM.enableDLPF(ICM_20948_Internal_Gyr, true); 181 | SERIAL_PORT.print(F("Enable DLPF for Accelerometer returned: ")); 182 | SERIAL_PORT.println(myICM.statusString(accDLPEnableStat)); 183 | SERIAL_PORT.print(F("Enable DLPF for Gyroscope returned: ")); 184 | SERIAL_PORT.println(myICM.statusString(gyrDLPEnableStat)); 185 | 186 | // Choose whether or not to start the magnetometer 187 | myICM.startupMagnetometer(); 188 | if (myICM.status != ICM_20948_Stat_Ok) 189 | { 190 | SERIAL_PORT.print(F("startupMagnetometer returned: ")); 191 | SERIAL_PORT.println(myICM.statusString()); 192 | } 193 | 194 | // 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 195 | /* 196 | ICM_20948_Status_e cfgIntActiveLow ( bool active_low ); 197 | ICM_20948_Status_e cfgIntOpenDrain ( bool open_drain ); 198 | ICM_20948_Status_e cfgIntLatch ( bool latching ); // If not latching then the interrupt is a 50 us pulse 199 | 200 | 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 201 | 202 | ICM_20948_Status_e cfgFsyncActiveLow ( bool active_low ); 203 | 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 204 | 205 | ICM_20948_Status_e intEnableI2C ( bool enable ); 206 | ICM_20948_Status_e intEnableDMP ( bool enable ); 207 | ICM_20948_Status_e intEnablePLL ( bool enable ); 208 | ICM_20948_Status_e intEnableWOM ( bool enable ); 209 | ICM_20948_Status_e intEnableWOF ( bool enable ); 210 | ICM_20948_Status_e intEnableRawDataReady ( bool enable ); 211 | ICM_20948_Status_e intEnableOverflowFIFO ( uint8_t bm_enable ); 212 | ICM_20948_Status_e intEnableWatermarkFIFO ( uint8_t bm_enable ); 213 | */ 214 | myICM.cfgIntActiveLow(true); // Active low to be compatible with the breakout board's pullup resistor 215 | myICM.cfgIntOpenDrain(false); // Push-pull, though open-drain would also work thanks to the pull-up resistors on the breakout 216 | myICM.cfgIntLatch(true); // Latch the interrupt until cleared 217 | SERIAL_PORT.print(F("cfgIntLatch returned: ")); 218 | SERIAL_PORT.println(myICM.statusString()); 219 | 220 | myICM.WOMThreshold(WOM_threshold); // set WoM threshold 221 | SERIAL_PORT.print(F("Set threshold returned: ")); 222 | SERIAL_PORT.println(myICM.statusString()); 223 | 224 | myICM.WOMLogic(true, 1); // enable WoM Logic mode 1 225 | 226 | myICM.intEnableWOM(true); // enable interrupts on WakeOnMotion 227 | SERIAL_PORT.print(F("intEnableWOM returned: ")); 228 | SERIAL_PORT.println(myICM.statusString()); 229 | 230 | myICM.WOMThreshold(WOM_threshold); // set WoM threshold - just in case... 231 | SERIAL_PORT.print(F("Set threshold returned: ")); 232 | SERIAL_PORT.println(myICM.statusString()); 233 | 234 | SERIAL_PORT.println(); 235 | SERIAL_PORT.println(F("Configuration complete!")); 236 | } 237 | 238 | void loop() 239 | { 240 | if (isrFired) 241 | { // If our isr flag is set then clear the interrupts on the ICM 242 | isrFired = false; 243 | myICM.getAGMT(); // get the A, G, M, and T readings 244 | //printScaledAGMT( &myICM ); // This function takes into account the scale settings from when the measurement was made to calculate the values with units 245 | SERIAL_PORT.println(F("Shock detected")); 246 | digitalWrite(LED_PIN, HIGH); 247 | lastTriggered = millis(); 248 | delay(30); 249 | myICM.clearInterrupts(); // This would be efficient... but not compatible with Uno 250 | } 251 | 252 | myICM.clearInterrupts(); // clear interrupts for next time - 253 | // usually you'd do this only if an interrupt has occurred, however 254 | // on the 328p I2C usage can block interrupts. This means that sometimes 255 | // an interrupt is missed. When missed, if using an edge-based interrupt 256 | // and only clearing interrupts when one was detected there will be no more 257 | // edges to respond to, so no more interrupts will be detected. Here are 258 | // some possible solutions: 259 | // 1. use a level based interrupt 260 | // 2. use the pulse-based interrupt in ICM settings (set cfgIntLatch to false) 261 | // 3. use a microcontroller with nestable interrupts 262 | // 4. clear the interrupts often 263 | 264 | if (millis() - lastTriggered > 1000) // Turn the LED off after one second 265 | digitalWrite(LED_PIN, LOW); 266 | } 267 | 268 | void icmISR(void) 269 | { 270 | isrFired = true; // Can't use I2C within ISR on 328p, so just set a flag to know that data is available 271 | } 272 | 273 | // Below here are some helper functions to print the data nicely! 274 | void printPaddedInt16b(int16_t val) 275 | { 276 | if (val > 0) 277 | { 278 | SERIAL_PORT.print(" "); 279 | if (val < 10000) 280 | { 281 | SERIAL_PORT.print("0"); 282 | } 283 | if (val < 1000) 284 | { 285 | SERIAL_PORT.print("0"); 286 | } 287 | if (val < 100) 288 | { 289 | SERIAL_PORT.print("0"); 290 | } 291 | if (val < 10) 292 | { 293 | SERIAL_PORT.print("0"); 294 | } 295 | } 296 | else 297 | { 298 | SERIAL_PORT.print("-"); 299 | if (abs(val) < 10000) 300 | { 301 | SERIAL_PORT.print("0"); 302 | } 303 | if (abs(val) < 1000) 304 | { 305 | SERIAL_PORT.print("0"); 306 | } 307 | if (abs(val) < 100) 308 | { 309 | SERIAL_PORT.print("0"); 310 | } 311 | if (abs(val) < 10) 312 | { 313 | SERIAL_PORT.print("0"); 314 | } 315 | } 316 | SERIAL_PORT.print(abs(val)); 317 | } 318 | 319 | void printRawAGMT(ICM_20948_AGMT_t agmt) 320 | { 321 | SERIAL_PORT.print("RAW. Acc [ "); 322 | printPaddedInt16b(agmt.acc.axes.x); 323 | SERIAL_PORT.print(", "); 324 | printPaddedInt16b(agmt.acc.axes.y); 325 | SERIAL_PORT.print(", "); 326 | printPaddedInt16b(agmt.acc.axes.z); 327 | SERIAL_PORT.print(" ], Gyr [ "); 328 | printPaddedInt16b(agmt.gyr.axes.x); 329 | SERIAL_PORT.print(", "); 330 | printPaddedInt16b(agmt.gyr.axes.y); 331 | SERIAL_PORT.print(", "); 332 | printPaddedInt16b(agmt.gyr.axes.z); 333 | SERIAL_PORT.print(" ], Mag [ "); 334 | printPaddedInt16b(agmt.mag.axes.x); 335 | SERIAL_PORT.print(", "); 336 | printPaddedInt16b(agmt.mag.axes.y); 337 | SERIAL_PORT.print(", "); 338 | printPaddedInt16b(agmt.mag.axes.z); 339 | SERIAL_PORT.print(" ], Tmp [ "); 340 | printPaddedInt16b(agmt.tmp.val); 341 | SERIAL_PORT.print(" ]"); 342 | SERIAL_PORT.println(); 343 | } 344 | 345 | void printFormattedFloat(float val, uint8_t leading, uint8_t decimals) 346 | { 347 | float aval = abs(val); 348 | if (val < 0) 349 | { 350 | SERIAL_PORT.print("-"); 351 | } 352 | else 353 | { 354 | SERIAL_PORT.print(" "); 355 | } 356 | for (uint8_t indi = 0; indi < leading; indi++) 357 | { 358 | uint32_t tenpow = 0; 359 | if (indi < (leading - 1)) 360 | { 361 | tenpow = 1; 362 | } 363 | for (uint8_t c = 0; c < (leading - 1 - indi); c++) 364 | { 365 | tenpow *= 10; 366 | } 367 | if (aval < tenpow) 368 | { 369 | SERIAL_PORT.print("0"); 370 | } 371 | else 372 | { 373 | break; 374 | } 375 | } 376 | if (val < 0) 377 | { 378 | SERIAL_PORT.print(-val, decimals); 379 | } 380 | else 381 | { 382 | SERIAL_PORT.print(val, decimals); 383 | } 384 | } 385 | 386 | #ifdef USE_SPI 387 | void printScaledAGMT(ICM_20948_SPI *sensor) 388 | { 389 | #else 390 | void printScaledAGMT(ICM_20948_I2C *sensor) 391 | { 392 | #endif 393 | SERIAL_PORT.print("Scaled. Acc (mg) [ "); 394 | printFormattedFloat(sensor->accX(), 5, 2); 395 | SERIAL_PORT.print(", "); 396 | printFormattedFloat(sensor->accY(), 5, 2); 397 | SERIAL_PORT.print(", "); 398 | printFormattedFloat(sensor->accZ(), 5, 2); 399 | SERIAL_PORT.print(" ], Gyr (DPS) [ "); 400 | printFormattedFloat(sensor->gyrX(), 5, 2); 401 | SERIAL_PORT.print(", "); 402 | printFormattedFloat(sensor->gyrY(), 5, 2); 403 | SERIAL_PORT.print(", "); 404 | printFormattedFloat(sensor->gyrZ(), 5, 2); 405 | SERIAL_PORT.print(" ], Mag (uT) [ "); 406 | printFormattedFloat(sensor->magX(), 5, 2); 407 | SERIAL_PORT.print(", "); 408 | printFormattedFloat(sensor->magY(), 5, 2); 409 | SERIAL_PORT.print(", "); 410 | printFormattedFloat(sensor->magZ(), 5, 2); 411 | SERIAL_PORT.print(" ], Tmp (C) [ "); 412 | printFormattedFloat(sensor->temp(), 5, 2); 413 | SERIAL_PORT.print(" ]"); 414 | SERIAL_PORT.println(); 415 | } 416 | -------------------------------------------------------------------------------- /examples/Arduino/Example5_DualSPITest/Example5_DualSPITest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Example5_DualSPITest.ino 3 | * ICM 20948 Arduino Library Demo 4 | * Use the default configuration to stream 9-axis IMU data on two IMUs over SPI 5 | * Owen Lyke @ SparkFun Electronics 6 | * Original Creation Date: April 17 2019 7 | * 8 | * Please see License.md for the license information. 9 | * 10 | * Distributed as-is; no warranty is given. 11 | ***************************************************************/ 12 | #include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU 13 | 14 | #define SERIAL_PORT Serial 15 | 16 | #define SPI_PORT SPI // Your desired SPI port. 17 | #define CS_PIN_1 2 // Which pin you connect CS to for Sensor 1 18 | #define CS_PIN_2 4 // Which pin you connect CS to for Sensor 2 19 | 20 | ICM_20948_SPI myICM1; // Create an ICM_20948_SPI object 21 | ICM_20948_SPI myICM2; // Create an ICM_20948_SPI object 22 | 23 | void setup() 24 | { 25 | 26 | SERIAL_PORT.begin(115200); 27 | while (!SERIAL_PORT) 28 | { 29 | }; 30 | 31 | SPI_PORT.begin(); 32 | 33 | myICM1.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial 34 | myICM2.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial 35 | 36 | bool initialized = false; 37 | while (!initialized) 38 | { 39 | 40 | myICM1.begin(CS_PIN_1, SPI_PORT); 41 | 42 | SERIAL_PORT.print(F("Initialization of sensor 1 returned: ")); 43 | SERIAL_PORT.println(myICM1.statusString()); 44 | if (myICM1.status != ICM_20948_Stat_Ok) 45 | { 46 | SERIAL_PORT.println(F("Trying again...")); 47 | delay(500); 48 | } 49 | else 50 | { 51 | initialized = true; 52 | } 53 | } 54 | 55 | initialized = false; 56 | while (!initialized) 57 | { 58 | 59 | myICM2.begin(CS_PIN_2, SPI_PORT); 60 | 61 | SERIAL_PORT.print(F("Initialization of sensor 2 returned: ")); 62 | SERIAL_PORT.println(myICM1.statusString()); 63 | if (myICM2.status != ICM_20948_Stat_Ok) 64 | { 65 | SERIAL_PORT.println(F("Trying again...")); 66 | delay(500); 67 | } 68 | else 69 | { 70 | initialized = true; 71 | } 72 | } 73 | } 74 | 75 | bool dataSeen = false; 76 | 77 | void loop() 78 | { 79 | 80 | if (myICM1.dataReady()) 81 | { 82 | myICM1.getAGMT(); // The values are only updated when you call 'getAGMT' 83 | SERIAL_PORT.print("Sensor 1: "); 84 | //printRawAGMT( myICM1.agmt); 85 | printScaledAGMT(&myICM1); 86 | dataSeen = true; 87 | } 88 | if (myICM2.dataReady()) 89 | { 90 | myICM2.getAGMT(); // The values are only updated when you call 'getAGMT' 91 | SERIAL_PORT.print("Sensor 2: "); 92 | //printRawAGMT( myICM2.agmt ); 93 | printScaledAGMT(&myICM2); 94 | dataSeen = true; 95 | } 96 | if (dataSeen) 97 | { 98 | delay(30); 99 | dataSeen = false; 100 | } 101 | else 102 | { 103 | SERIAL_PORT.println("Waiting for data"); 104 | delay(500); 105 | } 106 | } 107 | 108 | // Below here are some helper functions to print the data nicely! 109 | 110 | void printPaddedInt16b(int16_t val) 111 | { 112 | if (val > 0) 113 | { 114 | SERIAL_PORT.print(" "); 115 | if (val < 10000) 116 | { 117 | SERIAL_PORT.print("0"); 118 | } 119 | if (val < 1000) 120 | { 121 | SERIAL_PORT.print("0"); 122 | } 123 | if (val < 100) 124 | { 125 | SERIAL_PORT.print("0"); 126 | } 127 | if (val < 10) 128 | { 129 | SERIAL_PORT.print("0"); 130 | } 131 | } 132 | else 133 | { 134 | SERIAL_PORT.print("-"); 135 | if (abs(val) < 10000) 136 | { 137 | SERIAL_PORT.print("0"); 138 | } 139 | if (abs(val) < 1000) 140 | { 141 | SERIAL_PORT.print("0"); 142 | } 143 | if (abs(val) < 100) 144 | { 145 | SERIAL_PORT.print("0"); 146 | } 147 | if (abs(val) < 10) 148 | { 149 | SERIAL_PORT.print("0"); 150 | } 151 | } 152 | SERIAL_PORT.print(abs(val)); 153 | } 154 | 155 | void printRawAGMT(ICM_20948_AGMT_t agmt) 156 | { 157 | SERIAL_PORT.print("RAW. Acc [ "); 158 | printPaddedInt16b(agmt.acc.axes.x); 159 | SERIAL_PORT.print(", "); 160 | printPaddedInt16b(agmt.acc.axes.y); 161 | SERIAL_PORT.print(", "); 162 | printPaddedInt16b(agmt.acc.axes.z); 163 | SERIAL_PORT.print(" ], Gyr [ "); 164 | printPaddedInt16b(agmt.gyr.axes.x); 165 | SERIAL_PORT.print(", "); 166 | printPaddedInt16b(agmt.gyr.axes.y); 167 | SERIAL_PORT.print(", "); 168 | printPaddedInt16b(agmt.gyr.axes.z); 169 | SERIAL_PORT.print(" ], Mag [ "); 170 | printPaddedInt16b(agmt.mag.axes.x); 171 | SERIAL_PORT.print(", "); 172 | printPaddedInt16b(agmt.mag.axes.y); 173 | SERIAL_PORT.print(", "); 174 | printPaddedInt16b(agmt.mag.axes.z); 175 | SERIAL_PORT.print(" ], Tmp [ "); 176 | printPaddedInt16b(agmt.tmp.val); 177 | SERIAL_PORT.print(" ]"); 178 | SERIAL_PORT.println(); 179 | } 180 | 181 | void printFormattedFloat(float val, uint8_t leading, uint8_t decimals) 182 | { 183 | float aval = abs(val); 184 | if (val < 0) 185 | { 186 | SERIAL_PORT.print("-"); 187 | } 188 | else 189 | { 190 | SERIAL_PORT.print(" "); 191 | } 192 | for (uint8_t indi = 0; indi < leading; indi++) 193 | { 194 | uint32_t tenpow = 0; 195 | if (indi < (leading - 1)) 196 | { 197 | tenpow = 1; 198 | } 199 | for (uint8_t c = 0; c < (leading - 1 - indi); c++) 200 | { 201 | tenpow *= 10; 202 | } 203 | if (aval < tenpow) 204 | { 205 | SERIAL_PORT.print("0"); 206 | } 207 | else 208 | { 209 | break; 210 | } 211 | } 212 | if (val < 0) 213 | { 214 | SERIAL_PORT.print(-val, decimals); 215 | } 216 | else 217 | { 218 | SERIAL_PORT.print(val, decimals); 219 | } 220 | } 221 | 222 | void printScaledAGMT(ICM_20948_SPI *sensor) 223 | { 224 | SERIAL_PORT.print("Scaled. Acc (mg) [ "); 225 | printFormattedFloat(sensor->accX(), 5, 2); 226 | SERIAL_PORT.print(", "); 227 | printFormattedFloat(sensor->accY(), 5, 2); 228 | SERIAL_PORT.print(", "); 229 | printFormattedFloat(sensor->accZ(), 5, 2); 230 | SERIAL_PORT.print(" ], Gyr (DPS) [ "); 231 | printFormattedFloat(sensor->gyrX(), 5, 2); 232 | SERIAL_PORT.print(", "); 233 | printFormattedFloat(sensor->gyrY(), 5, 2); 234 | SERIAL_PORT.print(", "); 235 | printFormattedFloat(sensor->gyrZ(), 5, 2); 236 | SERIAL_PORT.print(" ], Mag (uT) [ "); 237 | printFormattedFloat(sensor->magX(), 5, 2); 238 | SERIAL_PORT.print(", "); 239 | printFormattedFloat(sensor->magY(), 5, 2); 240 | SERIAL_PORT.print(", "); 241 | printFormattedFloat(sensor->magZ(), 5, 2); 242 | SERIAL_PORT.print(" ], Tmp (C) [ "); 243 | printFormattedFloat(sensor->temp(), 5, 2); 244 | SERIAL_PORT.print(" ]"); 245 | SERIAL_PORT.println(); 246 | } 247 | -------------------------------------------------------------------------------- /examples/Arduino/Example6_DMP_Quat9_Orientation/Example6_DMP_Quat9_Orientation.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Example6_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, April 25th, 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 14301 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 | //#define QUAT_ANIMATION // Uncomment this line to output data in the correct format for ZaneL's Node.js Quaternion animation tool: https://github.com/ZaneL/quaternion_sensor_3d_nodejs 28 | 29 | #include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU 30 | 31 | //#define USE_SPI // Uncomment this to use SPI 32 | 33 | #define SERIAL_PORT Serial 34 | 35 | #define SPI_PORT SPI // Your desired SPI port. Used only when "USE_SPI" is defined 36 | #define CS_PIN 2 // Which pin you connect CS to. Used only when "USE_SPI" is defined 37 | 38 | #define WIRE_PORT Wire // Your desired Wire port. Used when "USE_SPI" is not defined 39 | // The value of the last bit of the I2C address. 40 | // On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0 41 | #define AD0_VAL 1 42 | 43 | #ifdef USE_SPI 44 | ICM_20948_SPI myICM; // If using SPI create an ICM_20948_SPI object 45 | #else 46 | ICM_20948_I2C myICM; // Otherwise create an ICM_20948_I2C object 47 | #endif 48 | 49 | void setup() 50 | { 51 | 52 | SERIAL_PORT.begin(115200); // Start the serial console 53 | #ifndef QUAT_ANIMATION 54 | SERIAL_PORT.println(F("ICM-20948 Example")); 55 | #endif 56 | 57 | delay(100); 58 | 59 | #ifndef QUAT_ANIMATION 60 | while (SERIAL_PORT.available()) // Make sure the serial RX buffer is empty 61 | SERIAL_PORT.read(); 62 | 63 | SERIAL_PORT.println(F("Press any key to continue...")); 64 | 65 | while (!SERIAL_PORT.available()) // Wait for the user to press a key (send any serial character) 66 | ; 67 | #endif 68 | 69 | #ifdef USE_SPI 70 | SPI_PORT.begin(); 71 | #else 72 | WIRE_PORT.begin(); 73 | WIRE_PORT.setClock(400000); 74 | #endif 75 | 76 | #ifndef QUAT_ANIMATION 77 | //myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial 78 | #endif 79 | 80 | bool initialized = false; 81 | while (!initialized) 82 | { 83 | 84 | // Initialize the ICM-20948 85 | // If the DMP is enabled, .begin performs a minimal startup. We need to configure the sample mode etc. manually. 86 | #ifdef USE_SPI 87 | myICM.begin(CS_PIN, SPI_PORT); 88 | #else 89 | myICM.begin(WIRE_PORT, AD0_VAL); 90 | #endif 91 | 92 | #ifndef QUAT_ANIMATION 93 | SERIAL_PORT.print(F("Initialization of the sensor returned: ")); 94 | SERIAL_PORT.println(myICM.statusString()); 95 | #endif 96 | if (myICM.status != ICM_20948_Stat_Ok) 97 | { 98 | #ifndef QUAT_ANIMATION 99 | SERIAL_PORT.println(F("Trying again...")); 100 | #endif 101 | delay(500); 102 | } 103 | else 104 | { 105 | initialized = true; 106 | } 107 | } 108 | 109 | #ifndef QUAT_ANIMATION 110 | SERIAL_PORT.println(F("Device connected!")); 111 | #endif 112 | 113 | bool success = true; // Use success to show if the DMP configuration was successful 114 | 115 | // Initialize the DMP. initializeDMP is a weak function. You can overwrite it if you want to e.g. to change the sample rate 116 | success &= (myICM.initializeDMP() == ICM_20948_Stat_Ok); 117 | 118 | // DMP sensor options are defined in ICM_20948_DMP.h 119 | // INV_ICM20948_SENSOR_ACCELEROMETER (16-bit accel) 120 | // INV_ICM20948_SENSOR_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 121 | // INV_ICM20948_SENSOR_RAW_ACCELEROMETER (16-bit accel) 122 | // INV_ICM20948_SENSOR_RAW_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 123 | // INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED (16-bit compass) 124 | // INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED (16-bit gyro) 125 | // INV_ICM20948_SENSOR_STEP_DETECTOR (Pedometer Step Detector) 126 | // INV_ICM20948_SENSOR_STEP_COUNTER (Pedometer Step Detector) 127 | // INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR (32-bit 6-axis quaternion) 128 | // INV_ICM20948_SENSOR_ROTATION_VECTOR (32-bit 9-axis quaternion + heading accuracy) 129 | // INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR (32-bit Geomag RV + heading accuracy) 130 | // INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD (32-bit calibrated compass) 131 | // INV_ICM20948_SENSOR_GRAVITY (32-bit 6-axis quaternion) 132 | // INV_ICM20948_SENSOR_LINEAR_ACCELERATION (16-bit accel + 32-bit 6-axis quaternion) 133 | // INV_ICM20948_SENSOR_ORIENTATION (32-bit 9-axis quaternion + heading accuracy) 134 | 135 | // Enable the DMP orientation sensor 136 | success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_ORIENTATION) == ICM_20948_Stat_Ok); 137 | 138 | // Enable any additional sensors / features 139 | //success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_RAW_GYROSCOPE) == ICM_20948_Stat_Ok); 140 | //success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_RAW_ACCELEROMETER) == ICM_20948_Stat_Ok); 141 | //success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED) == ICM_20948_Stat_Ok); 142 | 143 | // Configuring DMP to output data at multiple ODRs: 144 | // DMP is capable of outputting multiple sensor data at different rates to FIFO. 145 | // Setting value can be calculated as follows: 146 | // Value = (DMP running rate / ODR ) - 1 147 | // E.g. For a 5Hz ODR rate when DMP is running at 55Hz, value = (55/5) - 1 = 10. 148 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Quat9, 0) == ICM_20948_Stat_Ok); // Set to the maximum 149 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Accel, 0) == ICM_20948_Stat_Ok); // Set to the maximum 150 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Gyro, 0) == ICM_20948_Stat_Ok); // Set to the maximum 151 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Gyro_Calibr, 0) == ICM_20948_Stat_Ok); // Set to the maximum 152 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Cpass, 0) == ICM_20948_Stat_Ok); // Set to the maximum 153 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Cpass_Calibr, 0) == ICM_20948_Stat_Ok); // Set to the maximum 154 | 155 | // Enable the FIFO 156 | success &= (myICM.enableFIFO() == ICM_20948_Stat_Ok); 157 | 158 | // Enable the DMP 159 | success &= (myICM.enableDMP() == ICM_20948_Stat_Ok); 160 | 161 | // Reset DMP 162 | success &= (myICM.resetDMP() == ICM_20948_Stat_Ok); 163 | 164 | // Reset FIFO 165 | success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok); 166 | 167 | // Check success 168 | if (success) 169 | { 170 | #ifndef QUAT_ANIMATION 171 | SERIAL_PORT.println(F("DMP enabled!")); 172 | #endif 173 | } 174 | else 175 | { 176 | SERIAL_PORT.println(F("Enable DMP failed!")); 177 | SERIAL_PORT.println(F("Please check that you have uncommented line 29 (#define ICM_20948_USE_DMP) in ICM_20948_C.h...")); 178 | while (1) 179 | ; // Do nothing more 180 | } 181 | } 182 | 183 | void loop() 184 | { 185 | // Read any DMP data waiting in the FIFO 186 | // Note: 187 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFONoDataAvail if no data is available. 188 | // If data is available, readDMPdataFromFIFO will attempt to read _one_ frame of DMP data. 189 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOIncompleteData if a frame was present but was incomplete 190 | // readDMPdataFromFIFO will return ICM_20948_Stat_Ok if a valid frame was read. 191 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOMoreDataAvail if a valid frame was read _and_ the FIFO contains more (unread) data. 192 | icm_20948_DMP_data_t data; 193 | myICM.readDMPdataFromFIFO(&data); 194 | 195 | if ((myICM.status == ICM_20948_Stat_Ok) || (myICM.status == ICM_20948_Stat_FIFOMoreDataAvail)) // Was valid data available? 196 | { 197 | //SERIAL_PORT.print(F("Received data! Header: 0x")); // Print the header in HEX so we can see what data is arriving in the FIFO 198 | //if ( data.header < 0x1000) SERIAL_PORT.print( "0" ); // Pad the zeros 199 | //if ( data.header < 0x100) SERIAL_PORT.print( "0" ); 200 | //if ( data.header < 0x10) SERIAL_PORT.print( "0" ); 201 | //SERIAL_PORT.println( data.header, HEX ); 202 | 203 | if ((data.header & DMP_header_bitmap_Quat9) > 0) // We have asked for orientation data so we should receive Quat9 204 | { 205 | // Q0 value is computed from this equation: Q0^2 + Q1^2 + Q2^2 + Q3^2 = 1. 206 | // In case of drift, the sum will not add to 1, therefore, quaternion data need to be corrected with right bias values. 207 | // The quaternion data is scaled by 2^30. 208 | 209 | //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); 210 | 211 | // Scale to +/- 1 212 | double q1 = ((double)data.Quat9.Data.Q1) / 1073741824.0; // Convert to double. Divide by 2^30 213 | double q2 = ((double)data.Quat9.Data.Q2) / 1073741824.0; // Convert to double. Divide by 2^30 214 | double q3 = ((double)data.Quat9.Data.Q3) / 1073741824.0; // Convert to double. Divide by 2^30 215 | double q0 = sqrt(1.0 - ((q1 * q1) + (q2 * q2) + (q3 * q3))); 216 | 217 | #ifndef QUAT_ANIMATION 218 | SERIAL_PORT.print(F("Q1:")); 219 | SERIAL_PORT.print(q1, 3); 220 | SERIAL_PORT.print(F(" Q2:")); 221 | SERIAL_PORT.print(q2, 3); 222 | SERIAL_PORT.print(F(" Q3:")); 223 | SERIAL_PORT.print(q3, 3); 224 | SERIAL_PORT.print(F(" Accuracy:")); 225 | SERIAL_PORT.println(data.Quat9.Data.Accuracy); 226 | #else 227 | // Output the Quaternion data in the format expected by ZaneL's Node.js Quaternion animation tool 228 | SERIAL_PORT.print(F("{\"quat_w\":")); 229 | SERIAL_PORT.print(q0, 3); 230 | SERIAL_PORT.print(F(", \"quat_x\":")); 231 | SERIAL_PORT.print(q1, 3); 232 | SERIAL_PORT.print(F(", \"quat_y\":")); 233 | SERIAL_PORT.print(q2, 3); 234 | SERIAL_PORT.print(F(", \"quat_z\":")); 235 | SERIAL_PORT.print(q3, 3); 236 | SERIAL_PORT.println(F("}")); 237 | #endif 238 | } 239 | } 240 | 241 | if (myICM.status != ICM_20948_Stat_FIFOMoreDataAvail) // If more data is available then we should read it right away - and not delay 242 | { 243 | delay(10); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /examples/Arduino/Example7_DMP_Quat6_EulerAngles/Example7_DMP_Quat6_EulerAngles.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Example7_DMP_Quat6_EulerAngles.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, April 25th, 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 14301 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 | //#define QUAT_ANIMATION // Uncomment this line to output data in the correct format for ZaneL's Node.js Quaternion animation tool: https://github.com/ZaneL/quaternion_sensor_3d_nodejs 28 | 29 | #include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU 30 | 31 | //#define USE_SPI // Uncomment this to use SPI 32 | 33 | #define SERIAL_PORT Serial 34 | 35 | #define SPI_PORT SPI // Your desired SPI port. Used only when "USE_SPI" is defined 36 | #define CS_PIN 2 // Which pin you connect CS to. Used only when "USE_SPI" is defined 37 | 38 | #define WIRE_PORT Wire // Your desired Wire port. Used when "USE_SPI" is not defined 39 | // The value of the last bit of the I2C address. 40 | // On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0 41 | #define AD0_VAL 1 42 | 43 | #ifdef USE_SPI 44 | ICM_20948_SPI myICM; // If using SPI create an ICM_20948_SPI object 45 | #else 46 | ICM_20948_I2C myICM; // Otherwise create an ICM_20948_I2C object 47 | #endif 48 | 49 | void setup() 50 | { 51 | 52 | SERIAL_PORT.begin(115200); // Start the serial console 53 | #ifndef QUAT_ANIMATION 54 | SERIAL_PORT.println(F("ICM-20948 Example")); 55 | #endif 56 | 57 | delay(100); 58 | 59 | #ifndef QUAT_ANIMATION 60 | while (SERIAL_PORT.available()) // Make sure the serial RX buffer is empty 61 | SERIAL_PORT.read(); 62 | 63 | SERIAL_PORT.println(F("Press any key to continue...")); 64 | 65 | while (!SERIAL_PORT.available()) // Wait for the user to press a key (send any serial character) 66 | ; 67 | #endif 68 | 69 | #ifdef USE_SPI 70 | SPI_PORT.begin(); 71 | #else 72 | WIRE_PORT.begin(); 73 | WIRE_PORT.setClock(400000); 74 | #endif 75 | 76 | #ifndef QUAT_ANIMATION 77 | //myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial 78 | #endif 79 | 80 | bool initialized = false; 81 | while (!initialized) 82 | { 83 | 84 | // Initialize the ICM-20948 85 | // If the DMP is enabled, .begin performs a minimal startup. We need to configure the sample mode etc. manually. 86 | #ifdef USE_SPI 87 | myICM.begin(CS_PIN, SPI_PORT); 88 | #else 89 | myICM.begin(WIRE_PORT, AD0_VAL); 90 | #endif 91 | 92 | #ifndef QUAT_ANIMATION 93 | SERIAL_PORT.print(F("Initialization of the sensor returned: ")); 94 | SERIAL_PORT.println(myICM.statusString()); 95 | #endif 96 | if (myICM.status != ICM_20948_Stat_Ok) 97 | { 98 | #ifndef QUAT_ANIMATION 99 | SERIAL_PORT.println(F("Trying again...")); 100 | #endif 101 | delay(500); 102 | } 103 | else 104 | { 105 | initialized = true; 106 | } 107 | } 108 | 109 | #ifndef QUAT_ANIMATION 110 | SERIAL_PORT.println(F("Device connected!")); 111 | #endif 112 | 113 | bool success = true; // Use success to show if the DMP configuration was successful 114 | 115 | // Initialize the DMP. initializeDMP is a weak function. You can overwrite it if you want to e.g. to change the sample rate 116 | success &= (myICM.initializeDMP() == ICM_20948_Stat_Ok); 117 | 118 | // DMP sensor options are defined in ICM_20948_DMP.h 119 | // INV_ICM20948_SENSOR_ACCELEROMETER (16-bit accel) 120 | // INV_ICM20948_SENSOR_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 121 | // INV_ICM20948_SENSOR_RAW_ACCELEROMETER (16-bit accel) 122 | // INV_ICM20948_SENSOR_RAW_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 123 | // INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED (16-bit compass) 124 | // INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED (16-bit gyro) 125 | // INV_ICM20948_SENSOR_STEP_DETECTOR (Pedometer Step Detector) 126 | // INV_ICM20948_SENSOR_STEP_COUNTER (Pedometer Step Detector) 127 | // INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR (32-bit 6-axis quaternion) 128 | // INV_ICM20948_SENSOR_ROTATION_VECTOR (32-bit 9-axis quaternion + heading accuracy) 129 | // INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR (32-bit Geomag RV + heading accuracy) 130 | // INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD (32-bit calibrated compass) 131 | // INV_ICM20948_SENSOR_GRAVITY (32-bit 6-axis quaternion) 132 | // INV_ICM20948_SENSOR_LINEAR_ACCELERATION (16-bit accel + 32-bit 6-axis quaternion) 133 | // INV_ICM20948_SENSOR_ORIENTATION (32-bit 9-axis quaternion + heading accuracy) 134 | 135 | // Enable the DMP Game Rotation Vector sensor 136 | success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR) == ICM_20948_Stat_Ok); 137 | 138 | // Enable any additional sensors / features 139 | //success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_RAW_GYROSCOPE) == ICM_20948_Stat_Ok); 140 | //success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_RAW_ACCELEROMETER) == ICM_20948_Stat_Ok); 141 | //success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED) == ICM_20948_Stat_Ok); 142 | 143 | // Configuring DMP to output data at multiple ODRs: 144 | // DMP is capable of outputting multiple sensor data at different rates to FIFO. 145 | // Setting value can be calculated as follows: 146 | // Value = (DMP running rate / ODR ) - 1 147 | // E.g. For a 5Hz ODR rate when DMP is running at 55Hz, value = (55/5) - 1 = 10. 148 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Quat6, 0) == ICM_20948_Stat_Ok); // Set to the maximum 149 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Accel, 0) == ICM_20948_Stat_Ok); // Set to the maximum 150 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Gyro, 0) == ICM_20948_Stat_Ok); // Set to the maximum 151 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Gyro_Calibr, 0) == ICM_20948_Stat_Ok); // Set to the maximum 152 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Cpass, 0) == ICM_20948_Stat_Ok); // Set to the maximum 153 | //success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Cpass_Calibr, 0) == ICM_20948_Stat_Ok); // Set to the maximum 154 | 155 | // Enable the FIFO 156 | success &= (myICM.enableFIFO() == ICM_20948_Stat_Ok); 157 | 158 | // Enable the DMP 159 | success &= (myICM.enableDMP() == ICM_20948_Stat_Ok); 160 | 161 | // Reset DMP 162 | success &= (myICM.resetDMP() == ICM_20948_Stat_Ok); 163 | 164 | // Reset FIFO 165 | success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok); 166 | 167 | // Check success 168 | if (success) 169 | { 170 | #ifndef QUAT_ANIMATION 171 | SERIAL_PORT.println(F("DMP enabled!")); 172 | #endif 173 | } 174 | else 175 | { 176 | SERIAL_PORT.println(F("Enable DMP failed!")); 177 | SERIAL_PORT.println(F("Please check that you have uncommented line 29 (#define ICM_20948_USE_DMP) in ICM_20948_C.h...")); 178 | while (1) 179 | ; // Do nothing more 180 | } 181 | } 182 | 183 | void loop() 184 | { 185 | // Read any DMP data waiting in the FIFO 186 | // Note: 187 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFONoDataAvail if no data is available. 188 | // If data is available, readDMPdataFromFIFO will attempt to read _one_ frame of DMP data. 189 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOIncompleteData if a frame was present but was incomplete 190 | // readDMPdataFromFIFO will return ICM_20948_Stat_Ok if a valid frame was read. 191 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOMoreDataAvail if a valid frame was read _and_ the FIFO contains more (unread) data. 192 | icm_20948_DMP_data_t data; 193 | myICM.readDMPdataFromFIFO(&data); 194 | 195 | if ((myICM.status == ICM_20948_Stat_Ok) || (myICM.status == ICM_20948_Stat_FIFOMoreDataAvail)) // Was valid data available? 196 | { 197 | //SERIAL_PORT.print(F("Received data! Header: 0x")); // Print the header in HEX so we can see what data is arriving in the FIFO 198 | //if ( data.header < 0x1000) SERIAL_PORT.print( "0" ); // Pad the zeros 199 | //if ( data.header < 0x100) SERIAL_PORT.print( "0" ); 200 | //if ( data.header < 0x10) SERIAL_PORT.print( "0" ); 201 | //SERIAL_PORT.println( data.header, HEX ); 202 | 203 | if ((data.header & DMP_header_bitmap_Quat6) > 0) // We have asked for GRV data so we should receive Quat6 204 | { 205 | // Q0 value is computed from this equation: Q0^2 + Q1^2 + Q2^2 + Q3^2 = 1. 206 | // In case of drift, the sum will not add to 1, therefore, quaternion data need to be corrected with right bias values. 207 | // The quaternion data is scaled by 2^30. 208 | 209 | //SERIAL_PORT.printf("Quat6 data is: Q1:%ld Q2:%ld Q3:%ld\r\n", data.Quat6.Data.Q1, data.Quat6.Data.Q2, data.Quat6.Data.Q3); 210 | 211 | // Scale to +/- 1 212 | double q1 = ((double)data.Quat6.Data.Q1) / 1073741824.0; // Convert to double. Divide by 2^30 213 | double q2 = ((double)data.Quat6.Data.Q2) / 1073741824.0; // Convert to double. Divide by 2^30 214 | double q3 = ((double)data.Quat6.Data.Q3) / 1073741824.0; // Convert to double. Divide by 2^30 215 | 216 | /* 217 | SERIAL_PORT.print(F("Q1:")); 218 | SERIAL_PORT.print(q1, 3); 219 | SERIAL_PORT.print(F(" Q2:")); 220 | SERIAL_PORT.print(q2, 3); 221 | SERIAL_PORT.print(F(" Q3:")); 222 | SERIAL_PORT.println(q3, 3); 223 | */ 224 | 225 | // The ICM 20948 chip has axes y-forward, x-right and Z-up - see Figure 12: 226 | // Orientation of Axes of Sensitivity and Polarity of Rotation 227 | // in DS-000189-ICM-20948-v1.6.pdf These are the axes for gyro and accel and quat 228 | // 229 | // For conversion to roll, pitch and yaw for the equations below, the coordinate frame 230 | // must be in aircraft reference frame. 231 | // 232 | // We use the Tait Bryan angles (in terms of flight dynamics): 233 | // ref: https://en.wikipedia.org/w/index.php?title=Conversion_between_quaternions_and_Euler_angles 234 | // 235 | // Heading – ψ : rotation about the Z-axis (+/- 180 deg.) 236 | // Pitch – θ : rotation about the new Y-axis (+/- 90 deg.) 237 | // Bank – ϕ : rotation about the new X-axis (+/- 180 deg.) 238 | // 239 | // where the X-axis points forward (pin 1 on chip), Y-axis to the right and Z-axis downward. 240 | // In the conversion example above the rotation occurs in the order heading, pitch, bank. 241 | // To get the roll, pitch and yaw equations to work properly we need to exchange the axes 242 | 243 | // Note when pitch approaches +/- 90 deg. the heading and bank become less meaningfull because the 244 | // device is pointing up/down. (Gimbal lock) 245 | 246 | double q0 = sqrt(1.0 - ((q1 * q1) + (q2 * q2) + (q3 * q3))); 247 | 248 | double qw = q0; // See issue #145 - thank you @Gord1 249 | double qx = q2; 250 | double qy = q1; 251 | double qz = -q3; 252 | 253 | // roll (x-axis rotation) 254 | double t0 = +2.0 * (qw * qx + qy * qz); 255 | double t1 = +1.0 - 2.0 * (qx * qx + qy * qy); 256 | double roll = atan2(t0, t1) * 180.0 / PI; 257 | 258 | // pitch (y-axis rotation) 259 | double t2 = +2.0 * (qw * qy - qx * qz); 260 | t2 = t2 > 1.0 ? 1.0 : t2; 261 | t2 = t2 < -1.0 ? -1.0 : t2; 262 | double pitch = asin(t2) * 180.0 / PI; 263 | 264 | // yaw (z-axis rotation) 265 | double t3 = +2.0 * (qw * qz + qx * qy); 266 | double t4 = +1.0 - 2.0 * (qy * qy + qz * qz); 267 | double yaw = atan2(t3, t4) * 180.0 / PI; 268 | 269 | #ifndef QUAT_ANIMATION 270 | SERIAL_PORT.print(F("Roll:")); 271 | SERIAL_PORT.print(roll, 1); 272 | SERIAL_PORT.print(F(" Pitch:")); 273 | SERIAL_PORT.print(pitch, 1); 274 | SERIAL_PORT.print(F(" Yaw:")); 275 | SERIAL_PORT.println(yaw, 1); 276 | #else 277 | // Output the Quaternion data in the format expected by ZaneL's Node.js Quaternion animation tool 278 | SERIAL_PORT.print(F("{\"quat_w\":")); 279 | SERIAL_PORT.print(q0, 3); 280 | SERIAL_PORT.print(F(", \"quat_x\":")); 281 | SERIAL_PORT.print(q1, 3); 282 | SERIAL_PORT.print(F(", \"quat_y\":")); 283 | SERIAL_PORT.print(q2, 3); 284 | SERIAL_PORT.print(F(", \"quat_z\":")); 285 | SERIAL_PORT.print(q3, 3); 286 | SERIAL_PORT.println(F("}")); 287 | #endif 288 | } 289 | } 290 | 291 | if (myICM.status != ICM_20948_Stat_FIFOMoreDataAvail) // If more data is available then we should read it right away - and not delay 292 | { 293 | delay(10); 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /examples/Arduino/Example8_DMP_RawAccel/Example8_DMP_RawAccel.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Example8_DMP_RawAccel.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, April 25th, 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 14301 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 | // The value of the last bit of the I2C address. 38 | // On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0 39 | #define AD0_VAL 1 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 | void setup() 48 | { 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 | 76 | // Initialize the ICM-20948 77 | // If the DMP is enabled, .begin performs a minimal startup. We need to configure the sample mode etc. manually. 78 | #ifdef USE_SPI 79 | myICM.begin(CS_PIN, SPI_PORT); 80 | #else 81 | myICM.begin(WIRE_PORT, AD0_VAL); 82 | #endif 83 | 84 | SERIAL_PORT.print(F("Initialization of the sensor returned: ")); 85 | SERIAL_PORT.println(myICM.statusString()); 86 | if (myICM.status != ICM_20948_Stat_Ok) 87 | { 88 | SERIAL_PORT.println(F("Trying again...")); 89 | delay(500); 90 | } 91 | else 92 | { 93 | initialized = true; 94 | } 95 | } 96 | 97 | SERIAL_PORT.println(F("Device connected!")); 98 | 99 | bool success = true; // Use success to show if the DMP configuration was successful 100 | 101 | // Initialize the DMP. initializeDMP is a weak function. You can overwrite it if you want to e.g. to change the sample rate 102 | success &= (myICM.initializeDMP() == ICM_20948_Stat_Ok); 103 | 104 | // DMP sensor options are defined in ICM_20948_DMP.h 105 | // INV_ICM20948_SENSOR_ACCELEROMETER (16-bit accel) 106 | // INV_ICM20948_SENSOR_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 107 | // INV_ICM20948_SENSOR_RAW_ACCELEROMETER (16-bit accel) 108 | // INV_ICM20948_SENSOR_RAW_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 109 | // INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED (16-bit compass) 110 | // INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED (16-bit gyro) 111 | // INV_ICM20948_SENSOR_STEP_DETECTOR (Pedometer Step Detector) 112 | // INV_ICM20948_SENSOR_STEP_COUNTER (Pedometer Step Detector) 113 | // INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR (32-bit 6-axis quaternion) 114 | // INV_ICM20948_SENSOR_ROTATION_VECTOR (32-bit 9-axis quaternion + heading accuracy) 115 | // INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR (32-bit Geomag RV + heading accuracy) 116 | // INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD (32-bit calibrated compass) 117 | // INV_ICM20948_SENSOR_GRAVITY (32-bit 6-axis quaternion) 118 | // INV_ICM20948_SENSOR_LINEAR_ACCELERATION (16-bit accel + 32-bit 6-axis quaternion) 119 | // INV_ICM20948_SENSOR_ORIENTATION (32-bit 9-axis quaternion + heading accuracy) 120 | 121 | // Enable the DMP accelerometer 122 | success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_ACCELEROMETER) == ICM_20948_Stat_Ok); 123 | 124 | // Configuring DMP to output data at multiple ODRs: 125 | // DMP is capable of outputting multiple sensor data at different rates to FIFO. 126 | // Setting value can be calculated as follows: 127 | // Value = (DMP running rate / ODR ) - 1 128 | // E.g. For a 5Hz ODR rate when DMP is running at 55Hz, value = (55/5) - 1 = 10. 129 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Accel, 0) == ICM_20948_Stat_Ok); // Set to the maximum 130 | 131 | // Enable the FIFO 132 | success &= (myICM.enableFIFO() == ICM_20948_Stat_Ok); 133 | 134 | // Enable the DMP 135 | success &= (myICM.enableDMP() == ICM_20948_Stat_Ok); 136 | 137 | // Reset DMP 138 | success &= (myICM.resetDMP() == ICM_20948_Stat_Ok); 139 | 140 | // Reset FIFO 141 | success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok); 142 | 143 | // Check success 144 | if (success) 145 | { 146 | SERIAL_PORT.println(F("DMP enabled!")); 147 | } 148 | else 149 | { 150 | SERIAL_PORT.println(F("Enable DMP failed!")); 151 | SERIAL_PORT.println(F("Please check that you have uncommented line 29 (#define ICM_20948_USE_DMP) in ICM_20948_C.h...")); 152 | while (1) 153 | ; // Do nothing more 154 | } 155 | } 156 | 157 | void loop() 158 | { 159 | // Read any DMP data waiting in the FIFO 160 | // Note: 161 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFONoDataAvail if no data is available. 162 | // If data is available, readDMPdataFromFIFO will attempt to read _one_ frame of DMP data. 163 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOIncompleteData if a frame was present but was incomplete 164 | // readDMPdataFromFIFO will return ICM_20948_Stat_Ok if a valid frame was read. 165 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOMoreDataAvail if a valid frame was read _and_ the FIFO contains more (unread) data. 166 | icm_20948_DMP_data_t data; 167 | myICM.readDMPdataFromFIFO(&data); 168 | 169 | if ((myICM.status == ICM_20948_Stat_Ok) || (myICM.status == ICM_20948_Stat_FIFOMoreDataAvail)) // Was valid data available? 170 | { 171 | //SERIAL_PORT.print(F("Received data! Header: 0x")); // Print the header in HEX so we can see what data is arriving in the FIFO 172 | //if ( data.header < 0x1000) SERIAL_PORT.print( "0" ); // Pad the zeros 173 | //if ( data.header < 0x100) SERIAL_PORT.print( "0" ); 174 | //if ( data.header < 0x10) SERIAL_PORT.print( "0" ); 175 | //SERIAL_PORT.println( data.header, HEX ); 176 | 177 | if ((data.header & DMP_header_bitmap_Accel) > 0) // Check the packet contains Accel data 178 | { 179 | float acc_x = (float)data.Raw_Accel.Data.X; // Extract the raw accelerometer data 180 | float acc_y = (float)data.Raw_Accel.Data.Y; 181 | float acc_z = (float)data.Raw_Accel.Data.Z; 182 | 183 | SERIAL_PORT.print(F("X:")); 184 | SERIAL_PORT.print(acc_x); 185 | SERIAL_PORT.print(F(" Y:")); 186 | SERIAL_PORT.print(acc_y); 187 | SERIAL_PORT.print(F(" Z:")); 188 | SERIAL_PORT.println(acc_z); 189 | } 190 | } 191 | 192 | if (myICM.status != ICM_20948_Stat_FIFOMoreDataAvail) // If more data is available then we should read it right away - and not delay 193 | { 194 | delay(10); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /examples/Arduino/Example9_DMP_MultipleSensors/Example9_DMP_MultipleSensors.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Example9_DMP_MultipleSensors.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, April 25th, 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 14301 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 | // The value of the last bit of the I2C address. 38 | // On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0 39 | #define AD0_VAL 1 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 | void setup() 48 | { 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 | 76 | // Initialize the ICM-20948 77 | // If the DMP is enabled, .begin performs a minimal startup. We need to configure the sample mode etc. manually. 78 | #ifdef USE_SPI 79 | myICM.begin(CS_PIN, SPI_PORT); 80 | #else 81 | myICM.begin(WIRE_PORT, AD0_VAL); 82 | #endif 83 | 84 | SERIAL_PORT.print(F("Initialization of the sensor returned: ")); 85 | SERIAL_PORT.println(myICM.statusString()); 86 | if (myICM.status != ICM_20948_Stat_Ok) 87 | { 88 | SERIAL_PORT.println(F("Trying again...")); 89 | delay(500); 90 | } 91 | else 92 | { 93 | initialized = true; 94 | } 95 | } 96 | 97 | SERIAL_PORT.println(F("Device connected!")); 98 | 99 | bool success = true; // Use success to show if the DMP configuration was successful 100 | 101 | // Initialize the DMP. initializeDMP is a weak function. You can overwrite it if you want to e.g. to change the sample rate 102 | success &= (myICM.initializeDMP() == ICM_20948_Stat_Ok); 103 | 104 | // DMP sensor options are defined in ICM_20948_DMP.h 105 | // INV_ICM20948_SENSOR_ACCELEROMETER (16-bit accel) 106 | // INV_ICM20948_SENSOR_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 107 | // INV_ICM20948_SENSOR_RAW_ACCELEROMETER (16-bit accel) 108 | // INV_ICM20948_SENSOR_RAW_GYROSCOPE (16-bit gyro + 32-bit calibrated gyro) 109 | // INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED (16-bit compass) 110 | // INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED (16-bit gyro) 111 | // INV_ICM20948_SENSOR_STEP_DETECTOR (Pedometer Step Detector) 112 | // INV_ICM20948_SENSOR_STEP_COUNTER (Pedometer Step Detector) 113 | // INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR (32-bit 6-axis quaternion) 114 | // INV_ICM20948_SENSOR_ROTATION_VECTOR (32-bit 9-axis quaternion + heading accuracy) 115 | // INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR (32-bit Geomag RV + heading accuracy) 116 | // INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD (32-bit calibrated compass) 117 | // INV_ICM20948_SENSOR_GRAVITY (32-bit 6-axis quaternion) 118 | // INV_ICM20948_SENSOR_LINEAR_ACCELERATION (16-bit accel + 32-bit 6-axis quaternion) 119 | // INV_ICM20948_SENSOR_ORIENTATION (32-bit 9-axis quaternion + heading accuracy) 120 | 121 | // Enable the DMP Game Rotation Vector sensor (Quat6) 122 | success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR) == ICM_20948_Stat_Ok); 123 | 124 | // Enable additional sensors / features 125 | success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_RAW_GYROSCOPE) == ICM_20948_Stat_Ok); 126 | success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_RAW_ACCELEROMETER) == ICM_20948_Stat_Ok); 127 | success &= (myICM.enableDMPSensor(INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED) == ICM_20948_Stat_Ok); 128 | 129 | // Configuring DMP to output data at multiple ODRs: 130 | // DMP is capable of outputting multiple sensor data at different rates to FIFO. 131 | // Setting value can be calculated as follows: 132 | // Value = (DMP running rate / ODR ) - 1 133 | // E.g. For a 5Hz ODR rate when DMP is running at 55Hz, value = (55/5) - 1 = 10. 134 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Quat6, 10) == ICM_20948_Stat_Ok); // Set to 5Hz 135 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Accel, 54) == ICM_20948_Stat_Ok); // Set to 1Hz 136 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Gyro, 54) == ICM_20948_Stat_Ok); // Set to 1Hz 137 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Gyro_Calibr, 54) == ICM_20948_Stat_Ok); // Set to 1Hz 138 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Cpass, 54) == ICM_20948_Stat_Ok); // Set to 1Hz 139 | success &= (myICM.setDMPODRrate(DMP_ODR_Reg_Cpass_Calibr, 54) == ICM_20948_Stat_Ok); // Set to 1Hz 140 | 141 | // Enable the FIFO 142 | success &= (myICM.enableFIFO() == ICM_20948_Stat_Ok); 143 | 144 | // Enable the DMP 145 | success &= (myICM.enableDMP() == ICM_20948_Stat_Ok); 146 | 147 | // Reset DMP 148 | success &= (myICM.resetDMP() == ICM_20948_Stat_Ok); 149 | 150 | // Reset FIFO 151 | success &= (myICM.resetFIFO() == ICM_20948_Stat_Ok); 152 | 153 | // Check success 154 | if (success) 155 | { 156 | SERIAL_PORT.println(F("DMP enabled!")); 157 | } 158 | else 159 | { 160 | SERIAL_PORT.println(F("Enable DMP failed!")); 161 | SERIAL_PORT.println(F("Please check that you have uncommented line 29 (#define ICM_20948_USE_DMP) in ICM_20948_C.h...")); 162 | while (1) 163 | ; // Do nothing more 164 | } 165 | } 166 | 167 | void loop() 168 | { 169 | // Read any DMP data waiting in the FIFO 170 | // Note: 171 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFONoDataAvail if no data is available. 172 | // If data is available, readDMPdataFromFIFO will attempt to read _one_ frame of DMP data. 173 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOIncompleteData if a frame was present but was incomplete 174 | // readDMPdataFromFIFO will return ICM_20948_Stat_Ok if a valid frame was read. 175 | // readDMPdataFromFIFO will return ICM_20948_Stat_FIFOMoreDataAvail if a valid frame was read _and_ the FIFO contains more (unread) data. 176 | icm_20948_DMP_data_t data; 177 | myICM.readDMPdataFromFIFO(&data); 178 | 179 | if ((myICM.status == ICM_20948_Stat_Ok) || (myICM.status == ICM_20948_Stat_FIFOMoreDataAvail)) // Was valid data available? 180 | { 181 | //SERIAL_PORT.print(F("Received data! Header: 0x")); // Print the header in HEX so we can see what data is arriving in the FIFO 182 | //if ( data.header < 0x1000) SERIAL_PORT.print( "0" ); // Pad the zeros 183 | //if ( data.header < 0x100) SERIAL_PORT.print( "0" ); 184 | //if ( data.header < 0x10) SERIAL_PORT.print( "0" ); 185 | //SERIAL_PORT.println( data.header, HEX ); 186 | 187 | if ((data.header & DMP_header_bitmap_Quat6) > 0) // Check for orientation data (Quat9) 188 | { 189 | // Q0 value is computed from this equation: Q0^2 + Q1^2 + Q2^2 + Q3^2 = 1. 190 | // In case of drift, the sum will not add to 1, therefore, quaternion data need to be corrected with right bias values. 191 | // The quaternion data is scaled by 2^30. 192 | 193 | //SERIAL_PORT.printf("Quat6 data is: Q1:%ld Q2:%ld Q3:%ld\r\n", data.Quat6.Data.Q1, data.Quat6.Data.Q2, data.Quat6.Data.Q3); 194 | 195 | // Scale to +/- 1 196 | double q1 = ((double)data.Quat6.Data.Q1) / 1073741824.0; // Convert to double. Divide by 2^30 197 | double q2 = ((double)data.Quat6.Data.Q2) / 1073741824.0; // Convert to double. Divide by 2^30 198 | double q3 = ((double)data.Quat6.Data.Q3) / 1073741824.0; // Convert to double. Divide by 2^30 199 | 200 | SERIAL_PORT.print(F("Q1:")); 201 | SERIAL_PORT.print(q1, 3); 202 | SERIAL_PORT.print(F(" Q2:")); 203 | SERIAL_PORT.print(q2, 3); 204 | SERIAL_PORT.print(F(" Q3:")); 205 | SERIAL_PORT.println(q3, 3); 206 | } 207 | 208 | if ((data.header & DMP_header_bitmap_Accel) > 0) // Check for Accel 209 | { 210 | float acc_x = (float)data.Raw_Accel.Data.X; // Extract the raw accelerometer data 211 | float acc_y = (float)data.Raw_Accel.Data.Y; 212 | float acc_z = (float)data.Raw_Accel.Data.Z; 213 | 214 | SERIAL_PORT.print(F("Accel: X:")); 215 | SERIAL_PORT.print(acc_x); 216 | SERIAL_PORT.print(F(" Y:")); 217 | SERIAL_PORT.print(acc_y); 218 | SERIAL_PORT.print(F(" Z:")); 219 | SERIAL_PORT.println(acc_z); 220 | } 221 | 222 | if ((data.header & DMP_header_bitmap_Gyro) > 0) // Check for Gyro 223 | { 224 | float x = (float)data.Raw_Gyro.Data.X; // Extract the raw gyro data 225 | float y = (float)data.Raw_Gyro.Data.Y; 226 | float z = (float)data.Raw_Gyro.Data.Z; 227 | 228 | SERIAL_PORT.print(F("Gyro: X:")); 229 | SERIAL_PORT.print(x); 230 | SERIAL_PORT.print(F(" Y:")); 231 | SERIAL_PORT.print(y); 232 | SERIAL_PORT.print(F(" Z:")); 233 | SERIAL_PORT.println(z); 234 | } 235 | 236 | if ((data.header & DMP_header_bitmap_Compass) > 0) // Check for Compass 237 | { 238 | float x = (float)data.Compass.Data.X; // Extract the compass data 239 | float y = (float)data.Compass.Data.Y; 240 | float z = (float)data.Compass.Data.Z; 241 | 242 | SERIAL_PORT.print(F("Compass: X:")); 243 | SERIAL_PORT.print(x); 244 | SERIAL_PORT.print(F(" Y:")); 245 | SERIAL_PORT.print(y); 246 | SERIAL_PORT.print(F(" Z:")); 247 | SERIAL_PORT.println(z); 248 | } 249 | } 250 | 251 | if (myICM.status != ICM_20948_Stat_FIFOMoreDataAvail) // If more data is available then we should read it right away - and not delay 252 | { 253 | delay(10); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /examples/PortableC/Example999_Portable/Example999_Portable.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Example999_Portable.ino 3 | * ICM 20948 Arduino Library Demo 4 | * Uses underlying portable C skeleton with user-defined read/write functions 5 | * Owen Lyke @ SparkFun Electronics 6 | * Original Creation Date: April 17 2019 7 | * 8 | * Please see License.md for the license information. 9 | * 10 | * Distributed as-is; no warranty is given. 11 | ***************************************************************/ 12 | #include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU 13 | 14 | #define SERIAL_PORT Serial 15 | 16 | //#define USE_SPI // uncomment to use SPI instead 17 | 18 | #ifdef USE_SPI 19 | #define SPI_PORT SPI 20 | #define CS_PIN 2 21 | #define SPI_CLK 1000000 22 | SPISettings mySettings(SPI_CLK, MSBFIRST, SPI_MODE3); 23 | #else 24 | #define WIRE_PORT Wire 25 | #define I2C_ADDR ICM_20948_I2C_ADDR_AD1 26 | #endif 27 | 28 | // These are the interface functions that you would define for your system. They can use either I2C or SPI, 29 | // or really **any** protocol as long as it successfully reads / writes the desired data into the ICM in the end 30 | #ifdef USE_SPI 31 | ICM_20948_Status_e my_write_spi(uint8_t reg, uint8_t *data, uint32_t len, void *user); 32 | ICM_20948_Status_e my_read_spi(uint8_t reg, uint8_t *buff, uint32_t len, void *user); 33 | #else 34 | ICM_20948_Status_e my_write_i2c(uint8_t reg, uint8_t *data, uint32_t len, void *user); 35 | ICM_20948_Status_e my_read_i2c(uint8_t reg, uint8_t *buff, uint32_t len, void *user); 36 | #endif 37 | 38 | // You declare a "Serial Interface" (serif) type and give it the pointers to your interface functions 39 | #ifdef USE_SPI 40 | const ICM_20948_Serif_t mySerif = { 41 | my_write_spi, // write 42 | my_read_spi, // read 43 | NULL, // this pointer is passed into your functions when they are called. 44 | }; 45 | #else 46 | const ICM_20948_Serif_t mySerif = { 47 | my_write_i2c, // write 48 | my_read_i2c, // read 49 | NULL, 50 | }; 51 | #endif 52 | 53 | // Now declare the structure that represents the ICM. 54 | ICM_20948_Device_t myICM; 55 | 56 | void setup() 57 | { 58 | 59 | // Perform platform initialization 60 | Serial.begin(115200); 61 | 62 | #ifdef USE_SPI 63 | SPI_PORT.begin(); 64 | pinMode(CS_PIN, OUTPUT); 65 | digitalWrite(CS_PIN, HIGH); 66 | // Aha! The SPI initialization monster bytes again! Let's send one byte out to 'set' the pins into a good starting state 67 | SPI_PORT.beginTransaction(mySettings); 68 | SPI_PORT.transfer(0x00); 69 | SPI_PORT.endTransaction(); 70 | #else 71 | WIRE_PORT.begin(); 72 | WIRE_PORT.setClock(400000); 73 | #endif 74 | 75 | // Initialize myICM 76 | ICM_20948_init_struct(&myICM); 77 | 78 | // Link the serif 79 | ICM_20948_link_serif(&myICM, &mySerif); 80 | 81 | while (ICM_20948_check_id(&myICM) != ICM_20948_Stat_Ok) 82 | { 83 | Serial.println("whoami does not match. Halting..."); 84 | delay(1000); 85 | } 86 | 87 | ICM_20948_Status_e stat = ICM_20948_Stat_Err; 88 | uint8_t whoami = 0x00; 89 | while ((stat != ICM_20948_Stat_Ok) || (whoami != ICM_20948_WHOAMI)) 90 | { 91 | whoami = 0x00; 92 | stat = ICM_20948_get_who_am_i(&myICM, &whoami); 93 | Serial.print("whoami does not match (0x"); 94 | Serial.print(whoami, HEX); 95 | Serial.print("). Halting..."); 96 | Serial.println(); 97 | delay(1000); 98 | } 99 | 100 | // Here we are doing a SW reset to make sure the device starts in a known state 101 | ICM_20948_sw_reset(&myICM); 102 | delay(250); 103 | 104 | // Set Gyro and Accelerometer to a particular sample mode 105 | ICM_20948_set_sample_mode(&myICM, (ICM_20948_InternalSensorID_bm)(ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Continuous); // optiona: ICM_20948_Sample_Mode_Continuous. ICM_20948_Sample_Mode_Cycled 106 | 107 | // Set full scale ranges for both acc and gyr 108 | ICM_20948_fss_t myfss; 109 | myfss.a = gpm2; // (ICM_20948_ACCEL_CONFIG_FS_SEL_e) 110 | myfss.g = dps250; // (ICM_20948_GYRO_CONFIG_1_FS_SEL_e) 111 | ICM_20948_set_full_scale(&myICM, (ICM_20948_InternalSensorID_bm)(ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myfss); 112 | 113 | // Set up DLPF configuration 114 | ICM_20948_dlpcfg_t myDLPcfg; 115 | myDLPcfg.a = acc_d473bw_n499bw; 116 | myDLPcfg.g = gyr_d361bw4_n376bw5; 117 | ICM_20948_set_dlpf_cfg(&myICM, (ICM_20948_InternalSensorID_bm)(ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myDLPcfg); 118 | 119 | // Choose whether or not to use DLPF 120 | ICM_20948_enable_dlpf(&myICM, ICM_20948_Internal_Acc, false); 121 | ICM_20948_enable_dlpf(&myICM, ICM_20948_Internal_Gyr, false); 122 | 123 | // Now wake the sensor up 124 | ICM_20948_sleep(&myICM, false); 125 | ICM_20948_low_power(&myICM, false); 126 | } 127 | 128 | void loop() 129 | { 130 | delay(1000); 131 | 132 | ICM_20948_AGMT_t agmt = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0}}; 133 | if (ICM_20948_get_agmt(&myICM, &agmt) == ICM_20948_Stat_Ok) 134 | { 135 | printRawAGMT(agmt); 136 | } 137 | else 138 | { 139 | Serial.println("Uh oh"); 140 | } 141 | } 142 | 143 | /////////////////////////////////////////////////////////////// 144 | /* Here's where you actually define your interface functions */ 145 | /////////////////////////////////////////////////////////////// 146 | 147 | #ifdef USE_SPI 148 | ICM_20948_Status_e my_write_spi(uint8_t reg, uint8_t *data, uint32_t len, void *user) 149 | { 150 | SPI_PORT.beginTransaction(mySettings); 151 | digitalWrite(CS_PIN, LOW); 152 | delayMicroseconds(5); 153 | SPI_PORT.transfer(((reg & 0x7F) | 0x00)); 154 | for (uint32_t indi = 0; indi < len; indi++) 155 | { 156 | SPI_PORT.transfer(*(data + indi)); 157 | } 158 | delayMicroseconds(5); 159 | digitalWrite(CS_PIN, HIGH); 160 | SPI_PORT.endTransaction(); 161 | 162 | return ICM_20948_Stat_Ok; 163 | } 164 | 165 | ICM_20948_Status_e my_read_spi(uint8_t reg, uint8_t *buff, uint32_t len, void *user) 166 | { 167 | SPI_PORT.beginTransaction(mySettings); 168 | digitalWrite(CS_PIN, LOW); 169 | delayMicroseconds(5); 170 | SPI_PORT.transfer(((reg & 0x7F) | 0x80)); 171 | for (uint32_t indi = 0; indi < len; indi++) 172 | { 173 | *(buff + indi) = SPI_PORT.transfer(0x00); 174 | } 175 | delayMicroseconds(5); 176 | digitalWrite(CS_PIN, HIGH); 177 | SPI_PORT.endTransaction(); 178 | 179 | return ICM_20948_Stat_Ok; 180 | } 181 | 182 | #else 183 | 184 | ICM_20948_Status_e my_write_i2c(uint8_t reg, uint8_t *data, uint32_t len, void *user) 185 | { 186 | WIRE_PORT.beginTransmission(I2C_ADDR); 187 | WIRE_PORT.write(reg); 188 | WIRE_PORT.write(data, len); 189 | WIRE_PORT.endTransmission(); 190 | 191 | return ICM_20948_Stat_Ok; 192 | } 193 | 194 | ICM_20948_Status_e my_read_i2c(uint8_t reg, uint8_t *buff, uint32_t len, void *user) 195 | { 196 | WIRE_PORT.beginTransmission(I2C_ADDR); 197 | WIRE_PORT.write(reg); 198 | WIRE_PORT.endTransmission(false); // Send repeated start 199 | 200 | uint32_t num_received = WIRE_PORT.requestFrom(I2C_ADDR, len); 201 | if (num_received == len) 202 | { 203 | for (uint32_t i = 0; i < len; i++) 204 | { 205 | buff[i] = WIRE_PORT.read(); 206 | } 207 | } 208 | 209 | return ICM_20948_Stat_Ok; 210 | } 211 | 212 | #endif 213 | 214 | // Some helper functions 215 | 216 | void printPaddedInt16b(int16_t val) 217 | { 218 | if (val > 0) 219 | { 220 | SERIAL_PORT.print(" "); 221 | if (val < 10000) 222 | { 223 | SERIAL_PORT.print("0"); 224 | } 225 | if (val < 1000) 226 | { 227 | SERIAL_PORT.print("0"); 228 | } 229 | if (val < 100) 230 | { 231 | SERIAL_PORT.print("0"); 232 | } 233 | if (val < 10) 234 | { 235 | SERIAL_PORT.print("0"); 236 | } 237 | } 238 | else 239 | { 240 | SERIAL_PORT.print("-"); 241 | if (abs(val) < 10000) 242 | { 243 | SERIAL_PORT.print("0"); 244 | } 245 | if (abs(val) < 1000) 246 | { 247 | SERIAL_PORT.print("0"); 248 | } 249 | if (abs(val) < 100) 250 | { 251 | SERIAL_PORT.print("0"); 252 | } 253 | if (abs(val) < 10) 254 | { 255 | SERIAL_PORT.print("0"); 256 | } 257 | } 258 | SERIAL_PORT.print(abs(val)); 259 | } 260 | 261 | void printRawAGMT(ICM_20948_AGMT_t agmt) 262 | { 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 | float getAccMG(int16_t raw, uint8_t fss) 288 | { 289 | switch (fss) 290 | { 291 | case 0: 292 | return (((float)raw) / 16.384); 293 | break; 294 | case 1: 295 | return (((float)raw) / 8.192); 296 | break; 297 | case 2: 298 | return (((float)raw) / 4.096); 299 | break; 300 | case 3: 301 | return (((float)raw) / 2.048); 302 | break; 303 | default: 304 | return 0; 305 | break; 306 | } 307 | } 308 | 309 | float getGyrDPS(int16_t raw, uint8_t fss) 310 | { 311 | switch (fss) 312 | { 313 | case 0: 314 | return (((float)raw) / 131); 315 | break; 316 | case 1: 317 | return (((float)raw) / 65.5); 318 | break; 319 | case 2: 320 | return (((float)raw) / 32.8); 321 | break; 322 | case 3: 323 | return (((float)raw) / 16.4); 324 | break; 325 | default: 326 | return 0; 327 | break; 328 | } 329 | } 330 | 331 | float getMagUT(int16_t raw) 332 | { 333 | return (((float)raw) * 0.15); 334 | } 335 | 336 | float getTmpC(int16_t raw) 337 | { 338 | return (((float)raw) / 333.87); 339 | } 340 | -------------------------------------------------------------------------------- /img/Contributing.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary/32cf0614bccf0d8cff32fbc4260097ac10e5b714/img/Contributing.JPG -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ICM_20948_I2C KEYWORD1 10 | ICM_20948_SPI KEYWORD1 11 | ICM_20948_Status_e KEYWORD1 12 | ICM_20948_InternalSensorID_bm KEYWORD1 13 | icm_20948_DMP_data_t KEYWORD1 14 | 15 | ####################################### 16 | # Methods and Functions (KEYWORD2) 17 | ####################################### 18 | 19 | ICM_20948 KEYWORD2 20 | enableDebugging KEYWORD2 21 | disableDebugging KEYWORD2 22 | debugPrintStatus KEYWORD2 23 | debugPrint KEYWORD2 24 | debugPrintln KEYWORD2 25 | doDebugPrint KEYWORD2 26 | debugPrintf KEYWORD2 27 | getAGMT KEYWORD2 28 | magX KEYWORD2 29 | magY KEYWORD2 30 | magZ KEYWORD2 31 | accX KEYWORD2 32 | accY KEYWORD2 33 | accZ KEYWORD2 34 | gyrX KEYWORD2 35 | gyrY KEYWORD2 36 | gyrZ KEYWORD2 37 | temp KEYWORD2 38 | statusString KEYWORD2 39 | setBank KEYWORD2 40 | swReset KEYWORD2 41 | sleep KEYWORD2 42 | lowPower KEYWORD2 43 | setClockSource KEYWORD2 44 | checkID KEYWORD2 45 | dataReady KEYWORD2 46 | getWhoAmI KEYWORD2 47 | isConnected KEYWORD2 48 | setSampleMode KEYWORD2 49 | setFullScale KEYWORD2 50 | setDLPFcfg KEYWORD2 51 | enableDLPF KEYWORD2 52 | setSampleRate KEYWORD2 53 | clearInterrupts 54 | cfgIntActiveLow KEYWORD2 55 | cfgIntOpenDrain KEYWORD2 56 | cfgIntLatch KEYWORD2 57 | cfgIntAnyReadToClear KEYWORD2 58 | cfgFsyncActiveLow KEYWORD2 59 | cfgFsyncIntMode KEYWORD2 60 | intEnableI2C KEYWORD2 61 | intEnableDMP KEYWORD2 62 | intEnablePLL KEYWORD2 63 | intEnableWOM KEYWORD2 64 | intEnableWOF KEYWORD2 65 | intEnableRawDataReady KEYWORD2 66 | intEnableOverflowFIFO KEYWORD2 67 | intEnableWatermarkFIFO KEYWORD2 68 | WOMLogic KEYWORD2 69 | WOMThreshold KEYWORD2 70 | i2cMasterPassthrough KEYWORD2 71 | i2cMasterEnable KEYWORD2 72 | i2cControllerConfigurePeripheral KEYWORD2 73 | i2cControllerPeriph4Transaction KEYWORD2 74 | i2cMasterSingleW KEYWORD2 75 | i2cMasterSingleR KEYWORD2 76 | startupDefault KEYWORD2 77 | read KEYWORD2 78 | write KEYWORD2 79 | startupMagnetometer KEYWORD2 80 | magWhoIAm KEYWORD2 81 | readMag KEYWORD2 82 | writeMag KEYWORD2 83 | resetMag KEYWORD2 84 | enableFIFO KEYWORD2 85 | resetFIFO KEYWORD2 86 | setFIFOmode KEYWORD2 87 | getFIFOcount KEYWORD2 88 | readFIFO KEYWORD2 89 | enableDMP KEYWORD2 90 | resetDMP KEYWORD2 91 | loadDMPFirmware KEYWORD2 92 | setDMPstartAddress KEYWORD2 93 | enableDMPSensor KEYWORD2 94 | enableDMPSensorInt KEYWORD2 95 | writeDMPmems KEYWORD2 96 | readDMPmems KEYWORD2 97 | setDMPODRrate KEYWORD2 98 | readDMPdataFromFIFO KEYWORD2 99 | setGyroSF KEYWORD2 100 | initializeDMP KEYWORD2 101 | begin KEYWORD2 102 | setBiasGyroX KEYWORD2 103 | setBiasGyroY KEYWORD2 104 | setBiasGyroZ KEYWORD2 105 | setBiasAccelX KEYWORD2 106 | setBiasAccelY KEYWORD2 107 | setBiasAccelZ KEYWORD2 108 | setBiasCPassX KEYWORD2 109 | setBiasCPassY KEYWORD2 110 | setBiasCPassZ KEYWORD2 111 | getBiasGyroX KEYWORD2 112 | getBiasGyroY KEYWORD2 113 | getBiasGyroZ KEYWORD2 114 | getBiasAccelX KEYWORD2 115 | getBiasAccelY KEYWORD2 116 | getBiasAccelZ KEYWORD2 117 | getBiasCPassX KEYWORD2 118 | getBiasCPassY KEYWORD2 119 | getBiasCPassZ KEYWORD2 120 | 121 | ####################################### 122 | # Constants (LITERAL1) 123 | ####################################### 124 | 125 | _ICM_20948_H_ LITERAL1 126 | ICM_20948_ARD_UNUSED_PIN LITERAL1 127 | ICM_20948_SPI_DEFAULT_FREQ LITERAL1 128 | ICM_20948_SPI_DEFAULT_ORDER LITERAL1 129 | ICM_20948_SPI_DEFAULT_MODE LITERAL1 130 | ICM_20948_I2C_ADDR_AD0 LITERAL1 131 | ICM_20948_I2C_ADDR_AD1 LITERAL1 132 | ICM_20948_WHOAMI LITERAL1 133 | MAG_AK09916_I2C_ADDR LITERAL1 134 | MAG_AK09916_WHO_AM_I LITERAL1 135 | MAG_REG_WHO_AM_I LITERAL1 136 | ICM_20948_Stat_Ok LITERAL1 137 | ICM_20948_Stat_Err LITERAL1 138 | ICM_20948_Stat_NotImpl LITERAL1 139 | ICM_20948_Stat_ParamErr LITERAL1 140 | ICM_20948_Stat_WrongID LITERAL1 141 | ICM_20948_Stat_InvalSensor LITERAL1 142 | ICM_20948_Stat_NoData LITERAL1 143 | ICM_20948_Stat_SensorNotSupported LITERAL1 144 | ICM_20948_Stat_DMPNotSupported LITERAL1 145 | ICM_20948_Stat_DMPVerifyFail LITERAL1 146 | ICM_20948_Stat_FIFONoDataAvail LITERAL1 147 | ICM_20948_Stat_FIFOIncompleteData LITERAL1 148 | ICM_20948_Stat_FIFOMoreDataAvail LITERAL1 149 | ICM_20948_Stat_UnrecognisedDMPHeader LITERAL1 150 | ICM_20948_Stat_UnrecognisedDMPHeader2 LITERAL1 151 | ICM_20948_Stat_InvalDMPRegister LITERAL1 152 | ICM_20948_Stat_NUM LITERAL1 153 | ICM_20948_Stat_Unknown LITERAL1 154 | ICM_20948_Internal_Acc LITERAL1 155 | ICM_20948_Internal_Gyr LITERAL1 156 | ICM_20948_Internal_Mag LITERAL1 157 | ICM_20948_Internal_Tmp LITERAL1 158 | ICM_20948_Internal_Mst LITERAL1 159 | DMP_ODR_Reg_Accel LITERAL1 160 | DMP_ODR_Reg_Gyro LITERAL1 161 | DMP_ODR_Reg_Cpass LITERAL1 162 | DMP_ODR_Reg_ALS LITERAL1 163 | DMP_ODR_Reg_Quat6 LITERAL1 164 | DMP_ODR_Reg_Quat9 LITERAL1 165 | DMP_ODR_Reg_PQuat6 LITERAL1 166 | DMP_ODR_Reg_Geomag LITERAL1 167 | DMP_ODR_Reg_Pressure LITERAL1 168 | DMP_ODR_Reg_Gyro_Calibr LITERAL1 169 | DMP_ODR_Reg_Cpass_Calibr LITERAL1 170 | INV_ICM20948_SENSOR_ACCELEROMETER LITERAL1 171 | INV_ICM20948_SENSOR_GYROSCOPE LITERAL1 172 | INV_ICM20948_SENSOR_RAW_ACCELEROMETER LITERAL1 173 | INV_ICM20948_SENSOR_RAW_GYROSCOPE LITERAL1 174 | INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED LITERAL1 175 | INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED LITERAL1 176 | INV_ICM20948_SENSOR_ACTIVITY_CLASSIFICATON LITERAL1 177 | INV_ICM20948_SENSOR_STEP_DETECTOR LITERAL1 178 | INV_ICM20948_SENSOR_STEP_COUNTER LITERAL1 179 | INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR LITERAL1 180 | INV_ICM20948_SENSOR_ROTATION_VECTOR LITERAL1 181 | INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR LITERAL1 182 | INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD LITERAL1 183 | INV_ICM20948_SENSOR_WAKEUP_SIGNIFICANT_MOTION LITERAL1 184 | INV_ICM20948_SENSOR_FLIP_PICKUP LITERAL1 185 | INV_ICM20948_SENSOR_WAKEUP_TILT_DETECTOR LITERAL1 186 | INV_ICM20948_SENSOR_GRAVITY LITERAL1 187 | INV_ICM20948_SENSOR_LINEAR_ACCELERATION LITERAL1 188 | INV_ICM20948_SENSOR_ORIENTATION LITERAL1 189 | INV_ICM20948_SENSOR_B2S LITERAL1 190 | DMP_header_bitmap_Header2 LITERAL1 191 | DMP_header_bitmap_Step_Detector LITERAL1 192 | DMP_header_bitmap_Compass_Calibr LITERAL1 193 | DMP_header_bitmap_Gyro_Calibr LITERAL1 194 | DMP_header_bitmap_Pressure LITERAL1 195 | DMP_header_bitmap_Geomag LITERAL1 196 | DMP_header_bitmap_PQuat6 LITERAL1 197 | DMP_header_bitmap_Quat9 LITERAL1 198 | DMP_header_bitmap_Quat6 LITERAL1 199 | DMP_header_bitmap_ALS LITERAL1 200 | DMP_header_bitmap_Compass LITERAL1 201 | DMP_header_bitmap_Gyro LITERAL1 202 | DMP_header_bitmap_Accel LITERAL1 203 | DMP_header2_bitmap_Secondary_On_Off LITERAL1 204 | DMP_header2_bitmap_Activity_Recog LITERAL1 205 | DMP_header2_bitmap_Pickup LITERAL1 206 | DMP_header2_bitmap_Fsync LITERAL1 207 | DMP_header2_bitmap_Compass_Accuracy LITERAL1 208 | DMP_header2_bitmap_Gyro_Accuracy LITERAL1 209 | DMP_header2_bitmap_Accel_Accuracy LITERAL1 210 | DMP_Data_ready_Gyro LITERAL1 211 | DMP_Data_ready_Accel LITERAL1 212 | DMP_Data_ready_Secondary_Compass LITERAL1 213 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library 2 | version=1.3.2 3 | author=SparkFun Electronics 4 | maintainer=SparkFun Electronics 5 | sentence=Use the low-power high-resolution ICM 20948 9 DoF IMU from Invensense with I2C or SPI. Version 1.2 of the library includes support for the InvenSense Digital Motion Processor (DMP™). 6 | paragraph=The SparkFun 9DoF IMU Breakout uses the Invensense ICM-20948 -- a system-in-package featuring acceleration full-scales of ±2 / ±4 / ±8 / ±16 (g), rotational full-scales of ±250 / ±500 / ±1000 / ±2000 (°/sec) and a magnetic field full scale of ±4800 µT. The ICM-20948 can be accessed via either I2C (400 kHz) or SPI (7 MHz) 7 | category=Sensors 8 | url=https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/ICM_20948.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A C++ interface to the ICM-20948 4 | 5 | */ 6 | 7 | #ifndef _ICM_20948_H_ 8 | #define _ICM_20948_H_ 9 | 10 | #include "util/ICM_20948_C.h" // The C backbone. ICM_20948_USE_DMP is defined in here. 11 | #include "util/AK09916_REGISTERS.h" 12 | 13 | #include "Arduino.h" // Arduino support 14 | #include "Wire.h" 15 | #include "SPI.h" 16 | 17 | #define ICM_20948_ARD_UNUSED_PIN 0xFF 18 | 19 | // Base 20 | class ICM_20948 21 | { 22 | private: 23 | Stream *_debugSerial; //The stream to send debug messages to if enabled 24 | bool _printDebug = false; //Flag to print the serial commands we are sending to the Serial port for debug 25 | 26 | const uint8_t MAX_MAGNETOMETER_STARTS = 10; // This replaces maxTries 27 | 28 | protected: 29 | ICM_20948_Device_t _device; 30 | 31 | float getTempC(int16_t val); 32 | float getGyrDPS(int16_t axis_val); 33 | float getAccMG(int16_t axis_val); 34 | float getMagUT(int16_t axis_val); 35 | 36 | public: 37 | ICM_20948(); // Constructor 38 | 39 | // Enable debug messages using the chosen Serial port (Stream) 40 | // Boards like the RedBoard Turbo use SerialUSB (not Serial). 41 | // But other boards like the SAMD51 Thing Plus use Serial (not SerialUSB). 42 | // These lines let the code compile cleanly on as many SAMD boards as possible. 43 | #if defined(ARDUINO_ARCH_SAMD) // Is this a SAMD board? 44 | #if defined(USB_VID) // Is the USB Vendor ID defined? 45 | #if (USB_VID == 0x1B4F) // Is this a SparkFun board? 46 | #if !defined(ARDUINO_SAMD51_THING_PLUS) & !defined(ARDUINO_SAMD51_MICROMOD) // If it is not a SAMD51 Thing Plus or SAMD51 MicroMod 47 | void enableDebugging(Stream &debugPort = SerialUSB); //Given a port to print to, enable debug messages. 48 | #else 49 | void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages. 50 | #endif 51 | #else 52 | void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages. 53 | #endif 54 | #else 55 | void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages. 56 | #endif 57 | #else 58 | void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages. 59 | #endif 60 | 61 | void disableDebugging(void); //Turn off debug statements 62 | 63 | void debugPrintStatus(ICM_20948_Status_e stat); 64 | 65 | // gfvalvo's flash string helper code: https://forum.arduino.cc/index.php?topic=533118.msg3634809#msg3634809 66 | void debugPrint(const char *); 67 | void debugPrint(const __FlashStringHelper *); 68 | void debugPrintln(const char *); 69 | void debugPrintln(const __FlashStringHelper *); 70 | void doDebugPrint(char (*)(const char *), const char *, bool newLine = false); 71 | 72 | void debugPrintf(int i); 73 | void debugPrintf(float f); 74 | 75 | ICM_20948_AGMT_t agmt; // Acceleometer, Gyroscope, Magenetometer, and Temperature data 76 | ICM_20948_AGMT_t getAGMT(void); // Updates the agmt field in the object and also returns a copy directly 77 | 78 | float magX(void); // micro teslas 79 | float magY(void); // micro teslas 80 | float magZ(void); // micro teslas 81 | 82 | float accX(void); // milli g's 83 | float accY(void); // milli g's 84 | float accZ(void); // milli g's 85 | 86 | float gyrX(void); // degrees per second 87 | float gyrY(void); // degrees per second 88 | float gyrZ(void); // degrees per second 89 | 90 | float temp(void); // degrees celsius 91 | 92 | ICM_20948_Status_e status; // Status from latest operation 93 | 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 94 | 95 | // Device Level 96 | ICM_20948_Status_e setBank(uint8_t bank); // Sets the bank 97 | ICM_20948_Status_e swReset(void); // Performs a SW reset 98 | ICM_20948_Status_e sleep(bool on = false); // Set sleep mode for the chip 99 | ICM_20948_Status_e lowPower(bool on = true); // Set low power mode for the chip 100 | ICM_20948_Status_e setClockSource(ICM_20948_PWR_MGMT_1_CLKSEL_e source); // Choose clock source 101 | ICM_20948_Status_e checkID(void); // Return 'ICM_20948_Stat_Ok' if whoami matches ICM_20948_WHOAMI 102 | 103 | bool dataReady(void); // Returns 'true' if data is ready 104 | uint8_t getWhoAmI(void); // Return whoami in out prarmeter 105 | bool isConnected(void); // Returns true if communications with the device are sucessful 106 | 107 | // Internal Sensor Options 108 | 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 109 | ICM_20948_Status_e setFullScale(uint8_t sensor_id_bm, ICM_20948_fss_t fss); 110 | ICM_20948_Status_e setDLPFcfg(uint8_t sensor_id_bm, ICM_20948_dlpcfg_t cfg); 111 | ICM_20948_Status_e enableDLPF(uint8_t sensor_id_bm, bool enable); 112 | ICM_20948_Status_e setSampleRate(uint8_t sensor_id_bm, ICM_20948_smplrt_t smplrt); 113 | 114 | // Interrupts on INT and FSYNC Pins 115 | ICM_20948_Status_e clearInterrupts(void); 116 | 117 | ICM_20948_Status_e cfgIntActiveLow(bool active_low); 118 | ICM_20948_Status_e cfgIntOpenDrain(bool open_drain); 119 | ICM_20948_Status_e cfgIntLatch(bool latching); // If not latching then the interrupt is a 50 us pulse 120 | 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 121 | ICM_20948_Status_e cfgFsyncActiveLow(bool active_low); 122 | 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 123 | 124 | ICM_20948_Status_e intEnableI2C(bool enable); 125 | ICM_20948_Status_e intEnableDMP(bool enable); 126 | ICM_20948_Status_e intEnablePLL(bool enable); 127 | ICM_20948_Status_e intEnableWOM(bool enable); 128 | ICM_20948_Status_e intEnableWOF(bool enable); 129 | ICM_20948_Status_e intEnableRawDataReady(bool enable); 130 | ICM_20948_Status_e intEnableOverflowFIFO(uint8_t bm_enable); 131 | ICM_20948_Status_e intEnableWatermarkFIFO(uint8_t bm_enable); 132 | 133 | ICM_20948_Status_e WOMLogic(uint8_t enable, uint8_t mode); 134 | ICM_20948_Status_e WOMThreshold(uint8_t threshold); 135 | 136 | // Interface Options 137 | ICM_20948_Status_e i2cMasterPassthrough(bool passthrough = true); 138 | ICM_20948_Status_e i2cMasterEnable(bool enable = true); 139 | ICM_20948_Status_e i2cMasterReset(); 140 | 141 | //Used for configuring peripherals 0-3 142 | 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); 143 | ICM_20948_Status_e i2cControllerPeriph4Transaction(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len, bool Rw, bool send_reg_addr = true); 144 | 145 | //Provided for backward-compatibility only. Please update to i2cControllerConfigurePeripheral and i2cControllerPeriph4Transaction. 146 | //https://www.oshwa.org/2020/06/29/a-resolution-to-redefine-spi-pin-names/ 147 | 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); 148 | ICM_20948_Status_e i2cMasterSLV4Transaction(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len, bool Rw, bool send_reg_addr = true); 149 | 150 | //Used for configuring the Magnetometer 151 | ICM_20948_Status_e i2cMasterSingleW(uint8_t addr, uint8_t reg, uint8_t data); 152 | uint8_t i2cMasterSingleR(uint8_t addr, uint8_t reg); 153 | 154 | // Default Setup 155 | 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. 156 | 157 | // direct read/write 158 | ICM_20948_Status_e read(uint8_t reg, uint8_t *pdata, uint32_t len); 159 | ICM_20948_Status_e write(uint8_t reg, uint8_t *pdata, uint32_t len); 160 | 161 | //Mag specific 162 | 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. 163 | ICM_20948_Status_e magWhoIAm(void); 164 | uint8_t readMag(AK09916_Reg_Addr_e reg); 165 | ICM_20948_Status_e writeMag(AK09916_Reg_Addr_e reg, uint8_t *pdata); 166 | ICM_20948_Status_e resetMag(); 167 | 168 | //FIFO 169 | ICM_20948_Status_e enableFIFO(bool enable = true); 170 | ICM_20948_Status_e resetFIFO(void); 171 | ICM_20948_Status_e setFIFOmode(bool snapshot = false); // Default to Stream (non-Snapshot) mode 172 | ICM_20948_Status_e getFIFOcount(uint16_t *count); 173 | ICM_20948_Status_e readFIFO(uint8_t *data, uint8_t len = 1); 174 | 175 | //DMP 176 | 177 | //Gyro Bias 178 | ICM_20948_Status_e setBiasGyroX(int32_t newValue); 179 | ICM_20948_Status_e setBiasGyroY(int32_t newValue); 180 | ICM_20948_Status_e setBiasGyroZ(int32_t newValue); 181 | ICM_20948_Status_e getBiasGyroX(int32_t* bias); 182 | ICM_20948_Status_e getBiasGyroY(int32_t* bias); 183 | ICM_20948_Status_e getBiasGyroZ(int32_t* bias); 184 | //Accel Bias 185 | ICM_20948_Status_e setBiasAccelX(int32_t newValue); 186 | ICM_20948_Status_e setBiasAccelY(int32_t newValue); 187 | ICM_20948_Status_e setBiasAccelZ(int32_t newValue); 188 | ICM_20948_Status_e getBiasAccelX(int32_t* bias); 189 | ICM_20948_Status_e getBiasAccelY(int32_t* bias); 190 | ICM_20948_Status_e getBiasAccelZ(int32_t* bias); 191 | //CPass Bias 192 | ICM_20948_Status_e setBiasCPassX(int32_t newValue); 193 | ICM_20948_Status_e setBiasCPassY(int32_t newValue); 194 | ICM_20948_Status_e setBiasCPassZ(int32_t newValue); 195 | ICM_20948_Status_e getBiasCPassX(int32_t* bias); 196 | ICM_20948_Status_e getBiasCPassY(int32_t* bias); 197 | ICM_20948_Status_e getBiasCPassZ(int32_t* bias); 198 | 199 | // Done: 200 | // Configure DMP start address through PRGM_STRT_ADDRH/PRGM_STRT_ADDRL 201 | // Load Firmware 202 | // Configure Accel scaling to DMP 203 | // Configure Compass mount matrix and scale to DMP 204 | // Reset FIFO 205 | // Reset DMP 206 | // Enable DMP interrupt 207 | // Configuring DMP to output data to FIFO: set DATA_OUT_CTL1, DATA_OUT_CTL2, DATA_INTR_CTL and MOTION_EVENT_CTL 208 | // Configuring DMP to output data at multiple ODRs 209 | // Configure DATA_RDY_STATUS 210 | // Configuring Accel calibration 211 | // Configuring Compass calibration 212 | // Configuring Gyro gain 213 | // Configuring Accel gain 214 | // Configure I2C_SLV0 and I2C_SLV1 to: request mag data from the hidden reserved AK09916 registers; trigger Single Measurements 215 | // Configure I2C Master ODR (default to 68.75Hz) 216 | 217 | // To Do: 218 | // Additional FIFO output control: FIFO_WATERMARK, BM_BATCH_MASK, BM_BATCH_CNTR, BM_BATCH_THLD 219 | // Configuring DMP features: PED_STD_STEPCTR, PED_STD_TIMECTR 220 | // Enabling Activity Recognition (BAC) feature 221 | // Enabling Significant Motion Detect (SMD) feature 222 | // Enabling Tilt Detector feature 223 | // Enabling Pick Up Gesture feature 224 | // Enabling Fsync detection feature 225 | // Biases: add save and load methods 226 | 227 | ICM_20948_Status_e enableDMP(bool enable = true); 228 | ICM_20948_Status_e resetDMP(void); 229 | ICM_20948_Status_e loadDMPFirmware(void); 230 | ICM_20948_Status_e setDMPstartAddress(unsigned short address = DMP_START_ADDRESS); 231 | ICM_20948_Status_e enableDMPSensor(enum inv_icm20948_sensor sensor, bool enable = true); 232 | ICM_20948_Status_e enableDMPSensorInt(enum inv_icm20948_sensor sensor, bool enable = true); 233 | ICM_20948_Status_e writeDMPmems(unsigned short reg, unsigned int length, const unsigned char *data); 234 | ICM_20948_Status_e readDMPmems(unsigned short reg, unsigned int length, unsigned char *data); 235 | ICM_20948_Status_e setDMPODRrate(enum DMP_ODR_Registers odr_reg, int interval); 236 | ICM_20948_Status_e readDMPdataFromFIFO(icm_20948_DMP_data_t *data); 237 | ICM_20948_Status_e setGyroSF(unsigned char div, int gyro_level); 238 | ICM_20948_Status_e initializeDMP(void) __attribute__((weak)); // Combine all of the DMP start-up code in one place. Can be overwritten if required 239 | }; 240 | 241 | // I2C 242 | 243 | // Forward declarations of TwoWire and Wire for board/variant combinations that don't have a default 'SPI' 244 | //class TwoWire; // Commented by PaulZC 21/2/8 - this was causing compilation to fail on the Arduino NANO 33 BLE 245 | //extern TwoWire Wire; // Commented by PaulZC 21/2/8 - this was causing compilation to fail on the Arduino NANO 33 BLE 246 | 247 | class ICM_20948_I2C : public ICM_20948 248 | { 249 | private: 250 | protected: 251 | public: 252 | TwoWire *_i2c; 253 | uint8_t _addr; 254 | uint8_t _ad0; 255 | bool _ad0val; 256 | ICM_20948_Serif_t _serif; 257 | 258 | ICM_20948_I2C(); // Constructor 259 | 260 | virtual ICM_20948_Status_e begin(TwoWire &wirePort = Wire, bool ad0val = true, uint8_t ad0pin = ICM_20948_ARD_UNUSED_PIN); 261 | }; 262 | 263 | // SPI 264 | #define ICM_20948_SPI_DEFAULT_FREQ 4000000 265 | #define ICM_20948_SPI_DEFAULT_ORDER MSBFIRST 266 | #define ICM_20948_SPI_DEFAULT_MODE SPI_MODE0 267 | 268 | // Forward declarations of SPIClass and SPI for board/variant combinations that don't have a default 'SPI' 269 | //class SPIClass; // Commented by PaulZC 21/2/8 - this was causing compilation to fail on the Arduino NANO 33 BLE 270 | //extern SPIClass SPI; // Commented by PaulZC 21/2/8 - this was causing compilation to fail on the Arduino NANO 33 BLE 271 | 272 | class ICM_20948_SPI : public ICM_20948 273 | { 274 | private: 275 | protected: 276 | public: 277 | SPIClass *_spi; 278 | SPISettings _spisettings; 279 | uint8_t _cs; 280 | ICM_20948_Serif_t _serif; 281 | 282 | ICM_20948_SPI(); // Constructor 283 | 284 | ICM_20948_Status_e begin(uint8_t csPin, SPIClass &spiPort = SPI, uint32_t SPIFreq = ICM_20948_SPI_DEFAULT_FREQ); 285 | }; 286 | 287 | #endif /* _ICM_20948_H_ */ 288 | -------------------------------------------------------------------------------- /src/util/AK09916_ENUMERATIONS.h: -------------------------------------------------------------------------------- 1 | #ifndef _AK09916_ENUMERATIONS_H_ 2 | #define _AK09916_ENUMERATIONS_H_ 3 | 4 | typedef enum 5 | { 6 | AK09916_mode_power_down = 0x00, 7 | AK09916_mode_single = (0x01 << 0), 8 | AK09916_mode_cont_10hz = (0x01 << 1), 9 | AK09916_mode_cont_20hz = (0x02 << 1), 10 | AK09916_mode_cont_50hz = (0x03 << 1), 11 | AK09916_mode_cont_100hz = (0x04 << 1), 12 | AK09916_mode_self_test = (0x01 << 4), 13 | } AK09916_mode_e; 14 | 15 | #endif // _AK09916_ENUMERATIONS_H_ -------------------------------------------------------------------------------- /src/util/AK09916_REGISTERS.h: -------------------------------------------------------------------------------- 1 | #ifndef _AK09916_REGISTERS_H_ 2 | #define _AK09916_REGISTERS_H_ 3 | 4 | #include 5 | 6 | typedef enum 7 | { 8 | AK09916_REG_WIA1 = 0x00, 9 | AK09916_REG_WIA2, 10 | AK09916_REG_RSV1, 11 | AK09916_REG_RSV2, // Reserved register. We start reading here when using the DMP. Secret sauce... 12 | // discontinuity - containing another nine reserved registers? Secret sauce... 13 | AK09916_REG_ST1 = 0x10, 14 | AK09916_REG_HXL, 15 | AK09916_REG_HXH, 16 | AK09916_REG_HYL, 17 | AK09916_REG_HYH, 18 | AK09916_REG_HZL, 19 | AK09916_REG_HZH, 20 | // discontinuity 21 | AK09916_REG_ST2 = 0x18, 22 | // discontinuity 23 | AK09916_REG_CNTL2 = 0x31, 24 | AK09916_REG_CNTL3, 25 | } AK09916_Reg_Addr_e; 26 | 27 | typedef struct 28 | { 29 | uint8_t WIA1; 30 | } AK09916_WIA1_Reg_t; 31 | 32 | typedef struct 33 | { 34 | uint8_t WIA2; 35 | } AK09916_WIA2_Reg_t; 36 | 37 | typedef struct 38 | { 39 | uint8_t DRDY : 1; 40 | uint8_t DOR : 1; 41 | uint8_t reserved_0 : 6; 42 | } AK09916_ST1_Reg_t; 43 | 44 | // typedef struct{ 45 | 46 | // }AK09916_HXL_Reg_t; 47 | 48 | // typedef struct{ 49 | 50 | // }AK09916_HXH_Reg_t; 51 | // typedef struct{ 52 | 53 | // }AK09916_HYL_Reg_t; 54 | // typedef struct{ 55 | 56 | // }AK09916_HYH_Reg_t; 57 | // typedef struct{ 58 | 59 | // }AK09916_HZL_Reg_t; 60 | // typedef struct{ 61 | 62 | // }AK09916_HZH_Reg_t; 63 | 64 | typedef struct 65 | { 66 | uint8_t reserved_0 : 3; 67 | uint8_t HOFL : 1; 68 | uint8_t reserved_1 : 4; 69 | } AK09916_ST2_Reg_t; 70 | 71 | typedef struct 72 | { 73 | uint8_t MODE : 5; 74 | uint8_t reserved_0 : 3; 75 | } AK09916_CNTL2_Reg_t; 76 | 77 | typedef struct 78 | { 79 | uint8_t SRST : 1; 80 | uint8_t reserved_0 : 7; 81 | } AK09916_CNTL3_Reg_t; 82 | 83 | #endif // _AK09916_REGISTERS_H_ 84 | -------------------------------------------------------------------------------- /src/util/ICM_20948_C.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is a C-compatible interface to the features presented by the ICM 20948 9-axis device 4 | The imementation of the interface is flexible 5 | 6 | */ 7 | 8 | #ifndef _ICM_20948_C_H_ 9 | #define _ICM_20948_C_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ICM_20948_REGISTERS.h" 16 | #include "ICM_20948_ENUMERATIONS.h" // This is to give users access to usable value definiitons 17 | #include "AK09916_ENUMERATIONS.h" 18 | #include "ICM_20948_DMP.h" 19 | 20 | #ifdef __cplusplus 21 | extern "C" 22 | { 23 | #endif /* __cplusplus */ 24 | 25 | // Portduino target uses system memcmp call, which conflicts with this 26 | #ifdef PORTDUINO 27 | #include 28 | #else 29 | extern int memcmp(const void *, const void *, size_t); // Avoid compiler warnings 30 | #endif 31 | 32 | // Define if the DMP will be supported 33 | // Note: you must have 14290/14301 Bytes of program memory available to store the DMP firmware! 34 | //#define ICM_20948_USE_DMP // Uncomment this line to enable DMP support. You can of course use ICM_20948_USE_DMP as a compiler flag too 35 | 36 | // There are two versions of the InvenSense DMP firmware for the ICM20948 - with slightly different sizes 37 | #define DMP_CODE_SIZE 14301 /* eMD-SmartMotion-ICM20948-1.1.0-MP */ 38 | //#define DMP_CODE_SIZE 14290 /* ICM20948_eMD_nucleo_1.0 */ 39 | 40 | 41 | #define ICM_20948_I2C_ADDR_AD0 0x68 // Or 0x69 when AD0 is high 42 | #define ICM_20948_I2C_ADDR_AD1 0x69 // 43 | #define ICM_20948_WHOAMI 0xEA 44 | 45 | #define MAG_AK09916_I2C_ADDR 0x0C 46 | #define MAG_AK09916_WHO_AM_I 0x4809 47 | #define MAG_REG_WHO_AM_I 0x00 48 | 49 | /** @brief Max size that can be read across I2C or SPI data lines */ 50 | #define INV_MAX_SERIAL_READ 16 51 | /** @brief Max size that can be written across I2C or SPI data lines */ 52 | #define INV_MAX_SERIAL_WRITE 16 53 | 54 | typedef enum 55 | { 56 | ICM_20948_Stat_Ok = 0x00, // The only return code that means all is well 57 | ICM_20948_Stat_Err, // A general error 58 | ICM_20948_Stat_NotImpl, // Returned by virtual functions that are not implemented 59 | ICM_20948_Stat_ParamErr, 60 | ICM_20948_Stat_WrongID, 61 | ICM_20948_Stat_InvalSensor, // Tried to apply a function to a sensor that does not support it (e.g. DLPF to the temperature sensor) 62 | ICM_20948_Stat_NoData, 63 | ICM_20948_Stat_SensorNotSupported, 64 | ICM_20948_Stat_DMPNotSupported, // DMP not supported (no #define ICM_20948_USE_DMP) 65 | ICM_20948_Stat_DMPVerifyFail, // DMP was written but did not verify correctly 66 | ICM_20948_Stat_FIFONoDataAvail, // FIFO contains no data 67 | ICM_20948_Stat_FIFOIncompleteData, // FIFO contained incomplete data 68 | ICM_20948_Stat_FIFOMoreDataAvail, // FIFO contains more data 69 | ICM_20948_Stat_UnrecognisedDMPHeader, 70 | ICM_20948_Stat_UnrecognisedDMPHeader2, 71 | ICM_20948_Stat_InvalDMPRegister, // Invalid DMP Register 72 | 73 | ICM_20948_Stat_NUM, 74 | ICM_20948_Stat_Unknown, 75 | } ICM_20948_Status_e; 76 | 77 | typedef enum 78 | { 79 | ICM_20948_Internal_Acc = (1 << 0), 80 | ICM_20948_Internal_Gyr = (1 << 1), 81 | ICM_20948_Internal_Mag = (1 << 2), 82 | ICM_20948_Internal_Tmp = (1 << 3), 83 | ICM_20948_Internal_Mst = (1 << 4), // I2C Master Ineternal 84 | } ICM_20948_InternalSensorID_bm; // A bitmask of internal sensor IDs 85 | 86 | typedef union 87 | { 88 | int16_t i16bit[3]; 89 | uint8_t u8bit[6]; 90 | } ICM_20948_axis3bit16_t; 91 | 92 | typedef union 93 | { 94 | int16_t i16bit; 95 | uint8_t u8bit[2]; 96 | } ICM_20948_axis1bit16_t; 97 | 98 | typedef struct 99 | { 100 | uint8_t a : 2; 101 | uint8_t g : 2; 102 | uint8_t reserved_0 : 4; 103 | } ICM_20948_fss_t; // Holds full-scale settings to be able to extract measurements with units 104 | 105 | typedef struct 106 | { 107 | uint8_t a; 108 | uint8_t g; 109 | } ICM_20948_dlpcfg_t; // Holds digital low pass filter settings. Members are type ICM_20948_ACCEL_CONFIG_DLPCFG_e 110 | 111 | typedef struct 112 | { 113 | uint16_t a; 114 | uint8_t g; 115 | } ICM_20948_smplrt_t; 116 | 117 | typedef struct 118 | { 119 | uint8_t I2C_MST_INT_EN : 1; 120 | uint8_t DMP_INT1_EN : 1; 121 | uint8_t PLL_RDY_EN : 1; 122 | uint8_t WOM_INT_EN : 1; 123 | uint8_t REG_WOF_EN : 1; 124 | uint8_t RAW_DATA_0_RDY_EN : 1; 125 | uint8_t FIFO_OVERFLOW_EN_4 : 1; 126 | uint8_t FIFO_OVERFLOW_EN_3 : 1; 127 | uint8_t FIFO_OVERFLOW_EN_2 : 1; 128 | uint8_t FIFO_OVERFLOW_EN_1 : 1; 129 | uint8_t FIFO_OVERFLOW_EN_0 : 1; 130 | uint8_t FIFO_WM_EN_4 : 1; 131 | uint8_t FIFO_WM_EN_3 : 1; 132 | uint8_t FIFO_WM_EN_2 : 1; 133 | uint8_t FIFO_WM_EN_1 : 1; 134 | uint8_t FIFO_WM_EN_0 : 1; 135 | } ICM_20948_INT_enable_t; 136 | 137 | typedef union 138 | { 139 | ICM_20948_axis3bit16_t raw; 140 | struct 141 | { 142 | int16_t x; 143 | int16_t y; 144 | int16_t z; 145 | } axes; 146 | } ICM_20948_axis3named_t; 147 | 148 | typedef struct 149 | { 150 | ICM_20948_axis3named_t acc; 151 | ICM_20948_axis3named_t gyr; 152 | ICM_20948_axis3named_t mag; 153 | union 154 | { 155 | ICM_20948_axis1bit16_t raw; 156 | int16_t val; 157 | } tmp; 158 | ICM_20948_fss_t fss; // Full-scale range settings for this measurement 159 | uint8_t magStat1; 160 | uint8_t magStat2; 161 | } ICM_20948_AGMT_t; 162 | 163 | typedef struct 164 | { 165 | ICM_20948_Status_e (*write)(uint8_t regaddr, uint8_t *pdata, uint32_t len, void *user); 166 | ICM_20948_Status_e (*read)(uint8_t regaddr, uint8_t *pdata, uint32_t len, void *user); 167 | // void (*delay)(uint32_t ms); 168 | void *user; 169 | } ICM_20948_Serif_t; // This is the vtable of serial interface functions 170 | extern const ICM_20948_Serif_t NullSerif; // Here is a default for initialization (NULL) 171 | 172 | typedef struct 173 | { 174 | const ICM_20948_Serif_t *_serif; // Pointer to the assigned Serif (Serial Interface) vtable 175 | bool _dmp_firmware_available; // Indicates if the DMP firmware has been included. It 176 | bool _firmware_loaded; // Indicates if DMP has been loaded 177 | uint8_t _last_bank; // Keep track of which bank was selected last - to avoid unnecessary writes 178 | uint8_t _last_mems_bank; // Keep track of which bank was selected last - to avoid unnecessary writes 179 | int32_t _gyroSF; // Use this to record the GyroSF, calculated by inv_icm20948_set_gyro_sf 180 | int8_t _gyroSFpll; 181 | uint32_t _enabled_Android_0; // Keep track of which Android sensors are enabled: 0-31 182 | uint32_t _enabled_Android_1; // Keep track of which Android sensors are enabled: 32- 183 | uint32_t _enabled_Android_intr_0; // Keep track of which Android sensor interrupts are enabled: 0-31 184 | uint32_t _enabled_Android_intr_1; // Keep track of which Android sensor interrupts are enabled: 32- 185 | uint16_t _dataOutCtl1; // Diagnostics: record the setting of DATA_OUT_CTL1 186 | uint16_t _dataOutCtl2; // Diagnostics: record the setting of DATA_OUT_CTL2 187 | uint16_t _dataRdyStatus; // Diagnostics: record the setting of DATA_RDY_STATUS 188 | uint16_t _motionEventCtl; // Diagnostics: record the setting of MOTION_EVENT_CTL 189 | uint16_t _dataIntrCtl; // Diagnostics: record the setting of DATA_INTR_CTL 190 | } ICM_20948_Device_t; // Definition of device struct type 191 | 192 | ICM_20948_Status_e ICM_20948_init_struct(ICM_20948_Device_t *pdev); // Initialize ICM_20948_Device_t 193 | 194 | // ICM_20948_Status_e ICM_20948_Startup( ICM_20948_Device_t* pdev ); // For the time being this performs a standardized startup routine 195 | 196 | ICM_20948_Status_e ICM_20948_link_serif(ICM_20948_Device_t *pdev, const ICM_20948_Serif_t *s); // Links a SERIF structure to the device 197 | 198 | // use the device's serif to perform a read or write 199 | ICM_20948_Status_e ICM_20948_execute_r(ICM_20948_Device_t *pdev, uint8_t regaddr, uint8_t *pdata, uint32_t len); // Executes a R or W witht he serif vt as long as the pointers are not null 200 | ICM_20948_Status_e ICM_20948_execute_w(ICM_20948_Device_t *pdev, uint8_t regaddr, uint8_t *pdata, uint32_t len); 201 | 202 | // Single-shot I2C on Master IF 203 | ICM_20948_Status_e ICM_20948_i2c_controller_periph4_txn(ICM_20948_Device_t *pdev, uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len, bool Rw, bool send_reg_addr); 204 | ICM_20948_Status_e ICM_20948_i2c_master_single_w(ICM_20948_Device_t *pdev, uint8_t addr, uint8_t reg, uint8_t *data); 205 | ICM_20948_Status_e ICM_20948_i2c_master_single_r(ICM_20948_Device_t *pdev, uint8_t addr, uint8_t reg, uint8_t *data); 206 | 207 | // Device Level 208 | ICM_20948_Status_e ICM_20948_set_bank(ICM_20948_Device_t *pdev, uint8_t bank); // Sets the bank 209 | ICM_20948_Status_e ICM_20948_sw_reset(ICM_20948_Device_t *pdev); // Performs a SW reset 210 | ICM_20948_Status_e ICM_20948_sleep(ICM_20948_Device_t *pdev, bool on); // Set sleep mode for the chip 211 | ICM_20948_Status_e ICM_20948_low_power(ICM_20948_Device_t *pdev, bool on); // Set low power mode for the chip 212 | ICM_20948_Status_e ICM_20948_set_clock_source(ICM_20948_Device_t *pdev, ICM_20948_PWR_MGMT_1_CLKSEL_e source); // Choose clock source 213 | ICM_20948_Status_e ICM_20948_get_who_am_i(ICM_20948_Device_t *pdev, uint8_t *whoami); // Return whoami in out prarmeter 214 | ICM_20948_Status_e ICM_20948_check_id(ICM_20948_Device_t *pdev); // Return 'ICM_20948_Stat_Ok' if whoami matches ICM_20948_WHOAMI 215 | ICM_20948_Status_e ICM_20948_data_ready(ICM_20948_Device_t *pdev); // Returns 'Ok' if data is ready 216 | 217 | // Interrupt Configuration 218 | ICM_20948_Status_e ICM_20948_int_pin_cfg(ICM_20948_Device_t *pdev, ICM_20948_INT_PIN_CFG_t *write, ICM_20948_INT_PIN_CFG_t *read); // Set the INT pin configuration 219 | ICM_20948_Status_e ICM_20948_int_enable(ICM_20948_Device_t *pdev, ICM_20948_INT_enable_t *write, ICM_20948_INT_enable_t *read); // Write and or read the interrupt enable information. If non-null the write operation occurs before the read, so as to verify that the write was successful 220 | 221 | // WoM Enable Logic configuration 222 | ICM_20948_Status_e ICM_20948_wom_logic(ICM_20948_Device_t *pdev, ICM_20948_ACCEL_INTEL_CTRL_t *write, ICM_20948_ACCEL_INTEL_CTRL_t *read); //Enable or disable WoM Logic 223 | 224 | // WoM Threshold Level Configuration 225 | ICM_20948_Status_e ICM_20948_wom_threshold(ICM_20948_Device_t *pdev, ICM_20948_ACCEL_WOM_THR_t *write, ICM_20948_ACCEL_WOM_THR_t *read); // Write and or read the Wake on Motion threshold. If non-null the write operation occurs before the read, so as to verify that the write was successful 226 | 227 | // Internal Sensor Options 228 | ICM_20948_Status_e ICM_20948_set_sample_mode(ICM_20948_Device_t *pdev, ICM_20948_InternalSensorID_bm sensors, ICM_20948_LP_CONFIG_CYCLE_e mode); // Use to set accel, gyro, and I2C master into cycled or continuous modes 229 | ICM_20948_Status_e ICM_20948_set_full_scale(ICM_20948_Device_t *pdev, ICM_20948_InternalSensorID_bm sensors, ICM_20948_fss_t fss); 230 | ICM_20948_Status_e ICM_20948_set_dlpf_cfg(ICM_20948_Device_t *pdev, ICM_20948_InternalSensorID_bm sensors, ICM_20948_dlpcfg_t cfg); 231 | ICM_20948_Status_e ICM_20948_enable_dlpf(ICM_20948_Device_t *pdev, ICM_20948_InternalSensorID_bm sensors, bool enable); 232 | ICM_20948_Status_e ICM_20948_set_sample_rate(ICM_20948_Device_t *pdev, ICM_20948_InternalSensorID_bm sensors, ICM_20948_smplrt_t smplrt); 233 | 234 | // Interface Things 235 | ICM_20948_Status_e ICM_20948_i2c_master_passthrough(ICM_20948_Device_t *pdev, bool passthrough); 236 | ICM_20948_Status_e ICM_20948_i2c_master_enable(ICM_20948_Device_t *pdev, bool enable); 237 | ICM_20948_Status_e ICM_20948_i2c_master_reset(ICM_20948_Device_t *pdev); 238 | ICM_20948_Status_e ICM_20948_i2c_controller_configure_peripheral(ICM_20948_Device_t *pdev, uint8_t peripheral, uint8_t addr, uint8_t reg, uint8_t len, bool Rw, bool enable, bool data_only, bool grp, bool swap, uint8_t dataOut); 239 | 240 | // Higher Level 241 | ICM_20948_Status_e ICM_20948_get_agmt(ICM_20948_Device_t *pdev, ICM_20948_AGMT_t *p); 242 | 243 | // FIFO 244 | 245 | ICM_20948_Status_e ICM_20948_enable_FIFO(ICM_20948_Device_t *pdev, bool enable); 246 | ICM_20948_Status_e ICM_20948_reset_FIFO(ICM_20948_Device_t *pdev); 247 | ICM_20948_Status_e ICM_20948_set_FIFO_mode(ICM_20948_Device_t *pdev, bool snapshot); 248 | ICM_20948_Status_e ICM_20948_get_FIFO_count(ICM_20948_Device_t *pdev, uint16_t *count); 249 | ICM_20948_Status_e ICM_20948_read_FIFO(ICM_20948_Device_t *pdev, uint8_t *data, uint8_t len); 250 | 251 | // DMP 252 | 253 | ICM_20948_Status_e ICM_20948_enable_DMP(ICM_20948_Device_t *pdev, bool enable); 254 | ICM_20948_Status_e ICM_20948_reset_DMP(ICM_20948_Device_t *pdev); 255 | ICM_20948_Status_e ICM_20948_firmware_load(ICM_20948_Device_t *pdev); 256 | ICM_20948_Status_e ICM_20948_set_dmp_start_address(ICM_20948_Device_t *pdev, unsigned short address); 257 | 258 | /** @brief Loads the DMP firmware from SRAM 259 | * @param[in] data pointer where the image 260 | * @param[in] size size if the image 261 | * @param[in] load_addr address to loading the image 262 | * @return 0 in case of success, -1 for any error 263 | */ 264 | ICM_20948_Status_e inv_icm20948_firmware_load(ICM_20948_Device_t *pdev, const unsigned char *data, unsigned short size, unsigned short load_addr); 265 | /** 266 | * @brief Write data to a register in DMP memory 267 | * @param[in] DMP memory address 268 | * @param[in] number of byte to be written 269 | * @param[out] output data from the register 270 | * @return 0 if successful. 271 | */ 272 | ICM_20948_Status_e inv_icm20948_write_mems(ICM_20948_Device_t *pdev, unsigned short reg, unsigned int length, const unsigned char *data); 273 | /** 274 | * @brief Read data from a register in DMP memory 275 | * @param[in] DMP memory address 276 | * @param[in] number of byte to be read 277 | * @param[in] input data from the register 278 | * @return 0 if successful. 279 | */ 280 | ICM_20948_Status_e inv_icm20948_read_mems(ICM_20948_Device_t *pdev, unsigned short reg, unsigned int length, unsigned char *data); 281 | 282 | ICM_20948_Status_e inv_icm20948_set_dmp_sensor_period(ICM_20948_Device_t *pdev, enum DMP_ODR_Registers odr_reg, uint16_t interval); 283 | ICM_20948_Status_e inv_icm20948_enable_dmp_sensor(ICM_20948_Device_t *pdev, enum inv_icm20948_sensor sensor, int state); // State is actually boolean 284 | ICM_20948_Status_e inv_icm20948_enable_dmp_sensor_int(ICM_20948_Device_t *pdev, enum inv_icm20948_sensor sensor, int state); // State is actually boolean 285 | uint8_t sensor_type_2_android_sensor(enum inv_icm20948_sensor sensor); 286 | enum inv_icm20948_sensor inv_icm20948_sensor_android_2_sensor_type(int sensor); 287 | 288 | ICM_20948_Status_e inv_icm20948_read_dmp_data(ICM_20948_Device_t *pdev, icm_20948_DMP_data_t *data); 289 | ICM_20948_Status_e inv_icm20948_set_gyro_sf(ICM_20948_Device_t *pdev, unsigned char div, int gyro_level); 290 | 291 | // ToDo: 292 | 293 | /* 294 | Want to access magnetometer throught the I2C master interface... 295 | 296 | // If using the I2C master to read from the magnetometer 297 | // Enable the I2C master to talk to the magnetometer through the ICM 20948 298 | myICM.i2cMasterEnable( true ); 299 | SERIAL_PORT.print(F("Enabling the I2C master returned ")); SERIAL_PORT.println(myICM.statusString()); 300 | myICM.i2cControllerConfigurePeripheral ( 0, MAG_AK09916_I2C_ADDR, REG_ST1, 9, true, true, false, false, false ); 301 | SERIAL_PORT.print(F("Configuring the magnetometer peripheral returned ")); SERIAL_PORT.println(myICM.statusString()); 302 | 303 | // Operate the I2C master in duty-cycled mode 304 | myICM.setSampleMode( (ICM_20948_Internal_Mst | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Cycled ); // options: ICM_20948_Sample_Mode_Continuous or ICM_20948_Sample_Mode_Cycled 305 | */ 306 | 307 | #ifdef __cplusplus 308 | } 309 | #endif /* __cplusplus */ 310 | 311 | #endif /* _ICM_20948_C_H_ */ 312 | -------------------------------------------------------------------------------- /src/util/ICM_20948_ENUMERATIONS.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file contains a useful c translation of the datasheet register map values 4 | 5 | */ 6 | 7 | #ifndef _ICM_20948_ENUMERATIONS_H_ 8 | #define _ICM_20948_ENUMERATIONS_H_ 9 | 10 | #ifdef __cplusplus 11 | extern "C" 12 | { 13 | #endif /* __cplusplus */ 14 | 15 | // // Generalized 16 | // REG_BANK_SEL = 0x7F, 17 | 18 | // // Gyroscope and Accelerometer 19 | // // User Bank 0 20 | // AGB0_REG_WHO_AM_I = 0x00, 21 | // // Break 22 | // AGB0_REG_USER_CTRL = 0x03, 23 | // // Break 24 | // AGB0_REG_LP_CONFIG = 0x05, 25 | 26 | typedef enum 27 | { 28 | ICM_20948_Sample_Mode_Continuous = 0x00, 29 | ICM_20948_Sample_Mode_Cycled, 30 | } ICM_20948_LP_CONFIG_CYCLE_e; 31 | 32 | // AGB0_REG_PWR_MGMT_1, 33 | 34 | typedef enum 35 | { 36 | ICM_20948_Clock_Internal_20MHz = 0x00, 37 | ICM_20948_Clock_Auto, 38 | ICM_20948_Clock_TimingReset = 0x07 39 | } ICM_20948_PWR_MGMT_1_CLKSEL_e; 40 | 41 | // AGB0_REG_PWR_MGMT_2, 42 | // // Break 43 | // AGB0_REG_INT_PIN_CONFIG = 0x0F, 44 | // AGB0_REG_INT_ENABLE, 45 | // AGB0_REG_INT_ENABLE_1, 46 | // AGB0_REG_INT_ENABLE_2, 47 | // AGB0_REG_INT_ENABLE_3, 48 | // // Break 49 | // AGB0_REG_I2C_MST_STATUS = 0x17, 50 | // // Break 51 | // AGB0_REG_INT_STATUS = 0x19, 52 | // AGB0_REG_INT_STATUS_1, 53 | // AGB0_REG_INT_STATUS_2, 54 | // AGB0_REG_INT_STATUS_3, 55 | // // Break 56 | // AGB0_REG_DELAY_TIMEH = 0x28, 57 | // AGB0_REG_DELAY_TIMEL, 58 | // // Break 59 | // AGB0_REG_ACCEL_XOUT_H = 0x2D, 60 | // AGB0_REG_ACCEL_XOUT_L, 61 | // AGB0_REG_ACCEL_YOUT_H, 62 | // AGB0_REG_ACCEL_YOUT_L, 63 | // AGB0_REG_ACCEL_ZOUT_H, 64 | // AGB0_REG_ACCEL_ZOUT_L, 65 | // AGB0_REG_GYRO_XOUT_H, 66 | // AGB0_REG_GYRO_XOUT_L, 67 | // AGB0_REG_GYRO_YOUT_H, 68 | // AGB0_REG_GYRO_YOUT_L, 69 | // AGB0_REG_GYRO_ZOUT_H, 70 | // AGB0_REG_GYRO_ZOUT_L, 71 | // AGB0_REG_TEMP_OUT_H, 72 | // AGB0_REG_TEMP_OUT_L, 73 | // AGB0_REG_EXT_PERIPH_SENS_DATA_00, 74 | // AGB0_REG_EXT_PERIPH_SENS_DATA_01, 75 | // AGB0_REG_EXT_PERIPH_SENS_DATA_02, 76 | // AGB0_REG_EXT_PERIPH_SENS_DATA_03, 77 | // AGB0_REG_EXT_PERIPH_SENS_DATA_04, 78 | // AGB0_REG_EXT_PERIPH_SENS_DATA_05, 79 | // AGB0_REG_EXT_PERIPH_SENS_DATA_06, 80 | // AGB0_REG_EXT_PERIPH_SENS_DATA_07, 81 | // AGB0_REG_EXT_PERIPH_SENS_DATA_08, 82 | // AGB0_REG_EXT_PERIPH_SENS_DATA_09, 83 | // AGB0_REG_EXT_PERIPH_SENS_DATA_10, 84 | // AGB0_REG_EXT_PERIPH_SENS_DATA_11, 85 | // AGB0_REG_EXT_PERIPH_SENS_DATA_12, 86 | // AGB0_REG_EXT_PERIPH_SENS_DATA_13, 87 | // AGB0_REG_EXT_PERIPH_SENS_DATA_14, 88 | // AGB0_REG_EXT_PERIPH_SENS_DATA_15, 89 | // AGB0_REG_EXT_PERIPH_SENS_DATA_16, 90 | // AGB0_REG_EXT_PERIPH_SENS_DATA_17, 91 | // AGB0_REG_EXT_PERIPH_SENS_DATA_18, 92 | // AGB0_REG_EXT_PERIPH_SENS_DATA_19, 93 | // AGB0_REG_EXT_PERIPH_SENS_DATA_20, 94 | // AGB0_REG_EXT_PERIPH_SENS_DATA_21, 95 | // AGB0_REG_EXT_PERIPH_SENS_DATA_22, 96 | // AGB0_REG_EXT_PERIPH_SENS_DATA_23, 97 | // // Break 98 | // AGB0_REG_FIFO_EN_1 = 0x66, 99 | // AGB0_REG_FIFO_EN_2, 100 | // AGB0_REG_FIFO_MODE, 101 | // // Break 102 | // AGB0_REG_FIFO_COUNT_H = 0x70, 103 | // AGB0_REG_FIFO_COUNT_L, 104 | // AGB0_REG_FIFO_R_W, 105 | // // Break 106 | // AGB0_REG_DATA_RDY_STATUS = 0x74, 107 | // // Break 108 | // AGB0_REG_FIFO_CFG = 0x76, 109 | // // Break 110 | // AGB0_REG_MEM_START_ADDR = 0x7C, // Hmm, Invensense thought they were sneaky not listing these locations on the datasheet... 111 | // AGB0_REG_MEM_R_W = 0x7D, // These three locations seem to be able to access some memory within the device 112 | // AGB0_REG_MEM_BANK_SEL = 0x7E, // And that location is also where the DMP image gets loaded 113 | // AGB0_REG_REG_BANK_SEL = 0x7F, 114 | 115 | // // Bank 1 116 | // AGB1_REG_SELF_TEST_X_GYRO = 0x02, 117 | // AGB1_REG_SELF_TEST_Y_GYRO, 118 | // AGB1_REG_SELF_TEST_Z_GYRO, 119 | // // Break 120 | // AGB1_REG_SELF_TEST_X_ACCEL = 0x0E, 121 | // AGB1_REG_SELF_TEST_Y_ACCEL, 122 | // AGB1_REG_SELF_TEST_Z_ACCEL, 123 | // // Break 124 | // AGB1_REG_XA_OFFS_H = 0x14, 125 | // AGB1_REG_XA_OFFS_L, 126 | // // Break 127 | // AGB1_REG_YA_OFFS_H = 0x17, 128 | // AGB1_REG_YA_OFFS_L, 129 | // // Break 130 | // AGB1_REG_ZA_OFFS_H = 0x1A, 131 | // AGB1_REG_ZA_OFFS_L, 132 | // // Break 133 | // AGB1_REG_TIMEBASE_CORRECTION_PLL = 0x28, 134 | // // Break 135 | // AGB1_REG_REG_BANK_SEL = 0x7F, 136 | 137 | // // Bank 2 138 | // AGB2_REG_GYRO_SMPLRT_DIV = 0x00, 139 | 140 | /* 141 | Gyro sample rate divider. Divides the internal sample rate to generate the sample 142 | rate that controls sensor data output rate, FIFO sample rate, and DMP sequence rate. 143 | NOTE: This register is only effective when FCHOICE = 1’b1 (FCHOICE_B register bit is 1’b0), and 144 | (0 < DLPF_CFG < 7). 145 | ODR is computed as follows: 146 | 1.1 kHz/(1+GYRO_SMPLRT_DIV[7:0]) 147 | */ 148 | 149 | // AGB2_REG_GYRO_CONFIG_1, 150 | 151 | typedef enum 152 | { // Full scale range options in degrees per second 153 | dps250 = 0x00, 154 | dps500, 155 | dps1000, 156 | dps2000, 157 | } ICM_20948_GYRO_CONFIG_1_FS_SEL_e; 158 | 159 | typedef enum 160 | { // Format is dAbwB_nXbwY - A is integer part of 3db BW, B is fraction. X is integer part of nyquist bandwidth, Y is fraction 161 | gyr_d196bw6_n229bw8 = 0x00, 162 | gyr_d151bw8_n187bw6, 163 | gyr_d119bw5_n154bw3, 164 | gyr_d51bw2_n73bw3, 165 | gyr_d23bw9_n35bw9, 166 | gyr_d11bw6_n17bw8, 167 | gyr_d5bw7_n8bw9, 168 | gyr_d361bw4_n376bw5, 169 | } ICM_20948_GYRO_CONFIG_1_DLPCFG_e; 170 | 171 | // AGB2_REG_GYRO_CONFIG_2, 172 | // AGB2_REG_XG_OFFS_USRH, 173 | // AGB2_REG_XG_OFFS_USRL, 174 | // AGB2_REG_YG_OFFS_USRH, 175 | // AGB2_REG_YG_OFFS_USRL, 176 | // AGB2_REG_ZG_OFFS_USRH, 177 | // AGB2_REG_ZG_OFFS_USRL, 178 | // AGB2_REG_ODR_ALIGN_EN, 179 | // // Break 180 | // AGB2_REG_ACCEL_SMPLRT_DIV_1 = 0x10, 181 | // AGB2_REG_ACCEL_SMPLRT_DIV_2, 182 | // AGB2_REG_ACCEL_INTEL_CTRL, 183 | // AGB2_REG_ACCEL_WOM_THR, 184 | // AGB2_REG_ACCEL_CONFIG, 185 | 186 | typedef enum 187 | { 188 | gpm2 = 0x00, 189 | gpm4, 190 | gpm8, 191 | gpm16, 192 | } ICM_20948_ACCEL_CONFIG_FS_SEL_e; 193 | 194 | typedef enum 195 | { // Format is dAbwB_nXbwZ - A is integer part of 3db BW, B is fraction. X is integer part of nyquist bandwidth, Y is fraction 196 | acc_d246bw_n265bw = 0x00, 197 | acc_d246bw_n265bw_1, 198 | acc_d111bw4_n136bw, 199 | acc_d50bw4_n68bw8, 200 | acc_d23bw9_n34bw4, 201 | acc_d11bw5_n17bw, 202 | acc_d5bw7_n8bw3, 203 | acc_d473bw_n499bw, 204 | } ICM_20948_ACCEL_CONFIG_DLPCFG_e; 205 | 206 | // AGB2_REG_ACCEL_CONFIG_2, 207 | // // Break 208 | // AGB2_REG_FSYNC_CONFIG = 0x52, 209 | // AGB2_REG_TEMP_CONFIG, 210 | // AGB2_REG_MOD_CTRL_USR, 211 | // // Break 212 | // AGB2_REG_REG_BANK_SEL = 0x7F, 213 | 214 | // // Bank 3 215 | // AGB3_REG_I2C_MST_ODR_CONFIG = 0x00, 216 | // AGB3_REG_I2C_MST_CTRL, 217 | // AGB3_REG_I2C_MST_DELAY_CTRL, 218 | // AGB3_REG_I2C_PERIPH0_ADDR, 219 | // AGB3_REG_I2C_PERIPH0_REG, 220 | // AGB3_REG_I2C_PERIPH0_CTRL, 221 | // AGB3_REG_I2C_PERIPH0_DO, 222 | // AGB3_REG_I2C_PERIPH1_ADDR, 223 | // AGB3_REG_I2C_PERIPH1_REG, 224 | // AGB3_REG_I2C_PERIPH1_CTRL, 225 | // AGB3_REG_I2C_PERIPH1_DO, 226 | // AGB3_REG_I2C_PERIPH2_ADDR, 227 | // AGB3_REG_I2C_PERIPH2_REG, 228 | // AGB3_REG_I2C_PERIPH2_CTRL, 229 | // AGB3_REG_I2C_PERIPH2_DO, 230 | // AGB3_REG_I2C_PERIPH3_ADDR, 231 | // AGB3_REG_I2C_PERIPH3_REG, 232 | // AGB3_REG_I2C_PERIPH3_CTRL, 233 | // AGB3_REG_I2C_PERIPH3_DO, 234 | // AGB3_REG_I2C_PERIPH4_ADDR, 235 | // AGB3_REG_I2C_PERIPH4_REG, 236 | // AGB3_REG_I2C_PERIPH4_CTRL, 237 | // AGB3_REG_I2C_PERIPH4_DO, 238 | // AGB3_REG_I2C_PERIPH4_DI, 239 | // // Break 240 | // AGB3_REG_REG_BANK_SEL = 0x7F, 241 | 242 | // // Magnetometer 243 | // M_REG_WIA2 = 0x01, 244 | // // Break 245 | // M_REG_ST1 = 0x10, 246 | // M_REG_HXL, 247 | // M_REG_HXH, 248 | // M_REG_HYL, 249 | // M_REG_HYH, 250 | // M_REG_HZL, 251 | // M_REG_HZH, 252 | // M_REG_ST2, 253 | // // Break 254 | // M_REG_CNTL2 = 0x31, 255 | // M_REG_CNTL3, 256 | // M_REG_TS1, 257 | // M_REG_TS2, 258 | 259 | #ifdef __cplusplus 260 | } 261 | #endif /* __cplusplus */ 262 | 263 | #endif /* _ICM_20948_ENUMERATIONS_H_ */ 264 | -------------------------------------------------------------------------------- /src/util/ICM_20948_REGISTERS.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file contains a useful c translation of the datasheet register map 4 | 5 | */ 6 | 7 | #ifndef _ICM_20948_REGISTERS_H_ 8 | #define _ICM_20948_REGISTERS_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" 14 | { 15 | #endif /* __cplusplus */ 16 | 17 | typedef enum 18 | { 19 | // Generalized 20 | REG_BANK_SEL = 0x7F, 21 | 22 | // Gyroscope and Accelerometer 23 | // User Bank 0 24 | AGB0_REG_WHO_AM_I = 0x00, 25 | AGB0_REG_LPF, 26 | // Break 27 | AGB0_REG_USER_CTRL = 0x03, 28 | // Break 29 | AGB0_REG_LP_CONFIG = 0x05, 30 | AGB0_REG_PWR_MGMT_1, 31 | AGB0_REG_PWR_MGMT_2, 32 | // Break 33 | AGB0_REG_INT_PIN_CONFIG = 0x0F, 34 | AGB0_REG_INT_ENABLE, 35 | AGB0_REG_INT_ENABLE_1, 36 | AGB0_REG_INT_ENABLE_2, 37 | AGB0_REG_INT_ENABLE_3, 38 | // Break 39 | AGB0_REG_I2C_MST_STATUS = 0x17, 40 | AGB0_REG_DMP_INT_STATUS, 41 | AGB0_REG_INT_STATUS, 42 | AGB0_REG_INT_STATUS_1, 43 | AGB0_REG_INT_STATUS_2, 44 | AGB0_REG_INT_STATUS_3, 45 | // Break 46 | AGB0_REG_SINGLE_FIFO_PRIORITY_SEL = 0x26, 47 | // Break 48 | AGB0_REG_DELAY_TIMEH = 0x28, 49 | AGB0_REG_DELAY_TIMEL, 50 | // Break 51 | AGB0_REG_ACCEL_XOUT_H = 0x2D, 52 | AGB0_REG_ACCEL_XOUT_L, 53 | AGB0_REG_ACCEL_YOUT_H, 54 | AGB0_REG_ACCEL_YOUT_L, 55 | AGB0_REG_ACCEL_ZOUT_H, 56 | AGB0_REG_ACCEL_ZOUT_L, 57 | AGB0_REG_GYRO_XOUT_H, 58 | AGB0_REG_GYRO_XOUT_L, 59 | AGB0_REG_GYRO_YOUT_H, 60 | AGB0_REG_GYRO_YOUT_L, 61 | AGB0_REG_GYRO_ZOUT_H, 62 | AGB0_REG_GYRO_ZOUT_L, 63 | AGB0_REG_TEMP_OUT_H, 64 | AGB0_REG_TEMP_OUT_L, 65 | AGB0_REG_EXT_PERIPH_SENS_DATA_00, 66 | AGB0_REG_EXT_PERIPH_SENS_DATA_01, 67 | AGB0_REG_EXT_PERIPH_SENS_DATA_02, 68 | AGB0_REG_EXT_PERIPH_SENS_DATA_03, 69 | AGB0_REG_EXT_PERIPH_SENS_DATA_04, 70 | AGB0_REG_EXT_PERIPH_SENS_DATA_05, 71 | AGB0_REG_EXT_PERIPH_SENS_DATA_06, 72 | AGB0_REG_EXT_PERIPH_SENS_DATA_07, 73 | AGB0_REG_EXT_PERIPH_SENS_DATA_08, 74 | AGB0_REG_EXT_PERIPH_SENS_DATA_09, 75 | AGB0_REG_EXT_PERIPH_SENS_DATA_10, 76 | AGB0_REG_EXT_PERIPH_SENS_DATA_11, 77 | AGB0_REG_EXT_PERIPH_SENS_DATA_12, 78 | AGB0_REG_EXT_PERIPH_SENS_DATA_13, 79 | AGB0_REG_EXT_PERIPH_SENS_DATA_14, 80 | AGB0_REG_EXT_PERIPH_SENS_DATA_15, 81 | AGB0_REG_EXT_PERIPH_SENS_DATA_16, 82 | AGB0_REG_EXT_PERIPH_SENS_DATA_17, 83 | AGB0_REG_EXT_PERIPH_SENS_DATA_18, 84 | AGB0_REG_EXT_PERIPH_SENS_DATA_19, 85 | AGB0_REG_EXT_PERIPH_SENS_DATA_20, 86 | AGB0_REG_EXT_PERIPH_SENS_DATA_21, 87 | AGB0_REG_EXT_PERIPH_SENS_DATA_22, 88 | AGB0_REG_EXT_PERIPH_SENS_DATA_23, 89 | // Break 90 | AGB0_REG_TEMP_CONFIG = 0x53, 91 | // Break 92 | AGB0_REG_FIFO_EN_1 = 0x66, 93 | AGB0_REG_FIFO_EN_2, 94 | AGB0_REG_FIFO_RST, 95 | AGB0_REG_FIFO_MODE, 96 | // Break 97 | AGB0_REG_FIFO_COUNT_H = 0x70, 98 | AGB0_REG_FIFO_COUNT_L, 99 | AGB0_REG_FIFO_R_W, 100 | // Break 101 | AGB0_REG_DATA_RDY_STATUS = 0x74, 102 | AGB0_REG_HW_FIX_DISABLE, 103 | AGB0_REG_FIFO_CFG, 104 | // Break 105 | AGB0_REG_MEM_START_ADDR = 0x7C, // Hmm, Invensense thought they were sneaky not listing these locations on the datasheet... 106 | AGB0_REG_MEM_R_W = 0x7D, // These three locations seem to be able to access some memory within the device 107 | AGB0_REG_MEM_BANK_SEL = 0x7E, // And that location is also where the DMP image gets loaded 108 | AGB0_REG_REG_BANK_SEL = 0x7F, 109 | 110 | // Bank 1 111 | AGB1_REG_SELF_TEST_X_GYRO = 0x02, 112 | AGB1_REG_SELF_TEST_Y_GYRO, 113 | AGB1_REG_SELF_TEST_Z_GYRO, 114 | // Break 115 | AGB1_REG_SELF_TEST_X_ACCEL = 0x0E, 116 | AGB1_REG_SELF_TEST_Y_ACCEL, 117 | AGB1_REG_SELF_TEST_Z_ACCEL, 118 | // Break 119 | AGB1_REG_XA_OFFS_H = 0x14, 120 | AGB1_REG_XA_OFFS_L, 121 | // Break 122 | AGB1_REG_YA_OFFS_H = 0x17, 123 | AGB1_REG_YA_OFFS_L, 124 | // Break 125 | AGB1_REG_ZA_OFFS_H = 0x1A, 126 | AGB1_REG_ZA_OFFS_L, 127 | // Break 128 | AGB1_REG_TIMEBASE_CORRECTION_PLL = 0x28, 129 | // Break 130 | AGB1_REG_REG_BANK_SEL = 0x7F, 131 | 132 | // Bank 2 133 | AGB2_REG_GYRO_SMPLRT_DIV = 0x00, 134 | AGB2_REG_GYRO_CONFIG_1, 135 | AGB2_REG_GYRO_CONFIG_2, 136 | AGB2_REG_XG_OFFS_USRH, 137 | AGB2_REG_XG_OFFS_USRL, 138 | AGB2_REG_YG_OFFS_USRH, 139 | AGB2_REG_YG_OFFS_USRL, 140 | AGB2_REG_ZG_OFFS_USRH, 141 | AGB2_REG_ZG_OFFS_USRL, 142 | AGB2_REG_ODR_ALIGN_EN, 143 | // Break 144 | AGB2_REG_ACCEL_SMPLRT_DIV_1 = 0x10, 145 | AGB2_REG_ACCEL_SMPLRT_DIV_2, 146 | AGB2_REG_ACCEL_INTEL_CTRL, 147 | AGB2_REG_ACCEL_WOM_THR, 148 | AGB2_REG_ACCEL_CONFIG, 149 | AGB2_REG_ACCEL_CONFIG_2, 150 | // Break 151 | AGB2_REG_PRS_ODR_CONFIG = 0x20, 152 | // Break 153 | AGB2_REG_PRGM_START_ADDRH = 0x50, 154 | AGB2_REG_PRGM_START_ADDRL, 155 | AGB2_REG_FSYNC_CONFIG, 156 | AGB2_REG_TEMP_CONFIG, 157 | AGB2_REG_MOD_CTRL_USR, 158 | // Break 159 | AGB2_REG_REG_BANK_SEL = 0x7F, 160 | 161 | // Bank 3 162 | AGB3_REG_I2C_MST_ODR_CONFIG = 0x00, 163 | AGB3_REG_I2C_MST_CTRL, 164 | AGB3_REG_I2C_MST_DELAY_CTRL, 165 | AGB3_REG_I2C_PERIPH0_ADDR, 166 | AGB3_REG_I2C_PERIPH0_REG, 167 | AGB3_REG_I2C_PERIPH0_CTRL, 168 | AGB3_REG_I2C_PERIPH0_DO, 169 | AGB3_REG_I2C_PERIPH1_ADDR, 170 | AGB3_REG_I2C_PERIPH1_REG, 171 | AGB3_REG_I2C_PERIPH1_CTRL, 172 | AGB3_REG_I2C_PERIPH1_DO, 173 | AGB3_REG_I2C_PERIPH2_ADDR, 174 | AGB3_REG_I2C_PERIPH2_REG, 175 | AGB3_REG_I2C_PERIPH2_CTRL, 176 | AGB3_REG_I2C_PERIPH2_DO, 177 | AGB3_REG_I2C_PERIPH3_ADDR, 178 | AGB3_REG_I2C_PERIPH3_REG, 179 | AGB3_REG_I2C_PERIPH3_CTRL, 180 | AGB3_REG_I2C_PERIPH3_DO, 181 | AGB3_REG_I2C_PERIPH4_ADDR, 182 | AGB3_REG_I2C_PERIPH4_REG, 183 | AGB3_REG_I2C_PERIPH4_CTRL, 184 | AGB3_REG_I2C_PERIPH4_DO, 185 | AGB3_REG_I2C_PERIPH4_DI, 186 | // Break 187 | AGB3_REG_REG_BANK_SEL = 0x7F, 188 | 189 | // Magnetometer 190 | M_REG_WIA2 = 0x01, 191 | // Break 192 | M_REG_ST1 = 0x10, 193 | M_REG_HXL, 194 | M_REG_HXH, 195 | M_REG_HYL, 196 | M_REG_HYH, 197 | M_REG_HZL, 198 | M_REG_HZH, 199 | M_REG_ST2, 200 | // Break 201 | M_REG_CNTL2 = 0x31, 202 | M_REG_CNTL3, 203 | M_REG_TS1, 204 | M_REG_TS2, 205 | } ICM_20948_Reg_Addr_e; // These enums are not needed for the user, so they stay in this scope (simplifies naming among other things) 206 | 207 | // Type definitions for the registers 208 | typedef struct 209 | { 210 | uint8_t WHO_AM_I; 211 | } ICM_20948_WHO_AM_I_t; 212 | 213 | typedef struct 214 | { 215 | uint8_t reserved_0 : 1; 216 | uint8_t I2C_MST_RST : 1; 217 | uint8_t SRAM_RST : 1; 218 | uint8_t DMP_RST : 1; 219 | uint8_t I2C_IF_DIS : 1; 220 | uint8_t I2C_MST_EN : 1; 221 | uint8_t FIFO_EN : 1; 222 | uint8_t DMP_EN : 1; 223 | } ICM_20948_USER_CTRL_t; 224 | 225 | typedef struct 226 | { 227 | uint8_t reserved_0 : 4; 228 | uint8_t GYRO_CYCLE : 1; 229 | uint8_t ACCEL_CYCLE : 1; 230 | uint8_t I2C_MST_CYCLE : 1; 231 | uint8_t reserved_1 : 1; 232 | } ICM_20948_LP_CONFIG_t; 233 | 234 | typedef struct 235 | { 236 | uint8_t CLKSEL : 3; 237 | uint8_t TEMP_DIS : 1; 238 | uint8_t reserved_0 : 1; 239 | uint8_t LP_EN : 1; 240 | uint8_t SLEEP : 1; 241 | uint8_t DEVICE_RESET : 1; 242 | } ICM_20948_PWR_MGMT_1_t; 243 | 244 | typedef struct 245 | { 246 | uint8_t DISABLE_GYRO : 3; 247 | uint8_t DIABLE_ACCEL : 3; 248 | uint8_t reserved_0 : 2; 249 | } ICM_20948_PWR_MGMT_2_t; 250 | 251 | typedef struct 252 | { 253 | uint8_t reserved_0 : 1; 254 | uint8_t BYPASS_EN : 1; 255 | uint8_t FSYNC_INT_MODE_EN : 1; 256 | uint8_t ACTL_FSYNC : 1; 257 | uint8_t INT_ANYRD_2CLEAR : 1; 258 | uint8_t INT1_LATCH_EN : 1; 259 | uint8_t INT1_OPEN : 1; 260 | uint8_t INT1_ACTL : 1; 261 | } ICM_20948_INT_PIN_CFG_t; 262 | 263 | typedef struct 264 | { 265 | uint8_t I2C_MST_INT_EN : 1; 266 | uint8_t DMP_INT1_EN : 1; 267 | uint8_t PLL_READY_EN : 1; 268 | uint8_t WOM_INT_EN : 1; 269 | uint8_t reserved_0 : 3; 270 | uint8_t REG_WOF_EN : 1; 271 | } ICM_20948_INT_ENABLE_t; 272 | 273 | typedef struct 274 | { 275 | uint8_t RAW_DATA_0_RDY_EN : 1; 276 | uint8_t reserved_0 : 7; 277 | } ICM_20948_INT_ENABLE_1_t; 278 | 279 | typedef union 280 | { 281 | struct 282 | { 283 | uint8_t FIFO_OVERFLOW_EN_40 : 5; 284 | uint8_t reserved_0 : 3; 285 | } grouped; 286 | struct 287 | { 288 | uint8_t FIFO_OVERFLOW_EN_0 : 1; 289 | uint8_t FIFO_OVERFLOW_EN_1 : 1; 290 | uint8_t FIFO_OVERFLOW_EN_2 : 1; 291 | uint8_t FIFO_OVERFLOW_EN_3 : 1; 292 | uint8_t FIFO_OVERFLOW_EN_4 : 1; 293 | uint8_t reserved_0 : 3; 294 | } individual; 295 | } ICM_20948_INT_ENABLE_2_t; 296 | 297 | // typedef struct{ 298 | // uint8_t FIFO_OVERFLOW_EN_40 : 5; 299 | // uint8_t reserved_0 : 3; 300 | // }ICM_20948_INT_ENABLE_2_t; 301 | 302 | typedef union 303 | { 304 | struct 305 | { 306 | uint8_t FIFO_WM_EN_40 : 5; 307 | uint8_t reserved_0 : 3; 308 | } grouped; 309 | struct 310 | { 311 | uint8_t FIFO_WM_EN_0 : 1; 312 | uint8_t FIFO_WM_EN_1 : 1; 313 | uint8_t FIFO_WM_EN_2 : 1; 314 | uint8_t FIFO_WM_EN_3 : 1; 315 | uint8_t FIFO_WM_EN_4 : 1; 316 | uint8_t reserved_0 : 3; 317 | } individual; 318 | } ICM_20948_INT_ENABLE_3_t; 319 | 320 | // typedef struct{ 321 | // uint8_t FIFO_WM_EN_40 : 5; 322 | // uint8_t reserved_0 : 3; 323 | // }ICM_20948_INT_ENABLE_3_t; 324 | 325 | typedef struct 326 | { 327 | uint8_t I2C_PERIPH0_NACK : 1; 328 | uint8_t I2C_PERIPH1_NACK : 1; 329 | uint8_t I2C_PERIPH2_NACK : 1; 330 | uint8_t I2C_PERIPH3_NACK : 1; 331 | uint8_t I2C_PERIPH4_NACK : 1; 332 | uint8_t I2C_LOST_ARB : 1; 333 | uint8_t I2C_PERIPH4_DONE : 1; 334 | uint8_t PASS_THROUGH : 1; 335 | } ICM_20948_I2C_MST_STATUS_t; 336 | 337 | typedef struct 338 | { 339 | uint8_t reserved0 : 1; 340 | uint8_t DMP_INT_Motion_Detect_SMD : 1; 341 | uint8_t reserved1 : 1; 342 | uint8_t DMP_INT_Tilt_Event : 1; 343 | uint8_t reserved2 : 4; 344 | } ICM_20948_DMP_INT_STATUS_t; // Mostly guesswork from InvenSense App Note 345 | 346 | typedef struct 347 | { 348 | uint8_t I2C_MST_INT : 1; 349 | uint8_t DMP_INT1 : 1; 350 | uint8_t PLL_RDY_INT : 1; 351 | uint8_t WOM_INT : 1; 352 | uint8_t reserved_0 : 4; 353 | } ICM_20948_INT_STATUS_t; 354 | 355 | typedef struct 356 | { 357 | uint8_t RAW_DATA_0_RDY_INT : 1; 358 | uint8_t reserved_0 : 7; 359 | } ICM_20948_INT_STATUS_1_t; 360 | 361 | // typedef union{ 362 | // struct{ 363 | // uint8_t FIFO_OVERFLOW_INT_40 : 5; 364 | // uint8_t reserved_0 : 3; 365 | // }grouped; 366 | // struct{ 367 | // uint8_t FIFO_OVERFLOW_INT_0 : 1; 368 | // uint8_t FIFO_OVERFLOW_INT_1 : 1; 369 | // uint8_t FIFO_OVERFLOW_INT_2 : 1; 370 | // uint8_t FIFO_OVERFLOW_INT_3 : 1; 371 | // uint8_t FIFO_OVERFLOW_INT_4 : 1; 372 | // uint8_t reserved_0 : 3; 373 | // }individual; 374 | // }ICM_20948_INT_STATUS_2_t; 375 | 376 | typedef struct 377 | { 378 | uint8_t FIFO_OVERFLOW_INT_40 : 5; 379 | uint8_t reserved_0 : 3; 380 | } ICM_20948_INT_STATUS_2_t; 381 | 382 | // typedef union{ 383 | // struct{ 384 | // uint8_t FIFO_WM_INT_40 : 5; 385 | // uint8_t reserved_0 : 3; 386 | // }grouped; 387 | // struct{ 388 | // uint8_t FIFO_WM_INT_0 : 1; 389 | // uint8_t FIFO_WM_INT_1 : 1; 390 | // uint8_t FIFO_WM_INT_2 : 1; 391 | // uint8_t FIFO_WM_INT_3 : 1; 392 | // uint8_t FIFO_WM_INT_4 : 1; 393 | // uint8_t reserved_0 : 3; 394 | // }individual; 395 | // }ICM_20948_INT_STATUS_3_t; 396 | 397 | typedef struct 398 | { 399 | uint8_t FIFO_WM_INT40 : 5; 400 | uint8_t reserved_0 : 3; 401 | } ICM_20948_INT_STATUS_3_t; 402 | 403 | typedef struct 404 | { 405 | uint8_t DELAY_TIMEH; 406 | } ICM_20948_DELAY_TIMEH_t; 407 | 408 | typedef struct 409 | { 410 | uint8_t DELAY_TIMEL; 411 | } ICM_20948_DELAY_TIMEL_t; 412 | 413 | typedef struct 414 | { 415 | uint8_t ACCEL_XOUT_H; 416 | } ICM_20948_ACCEL_XOUT_H_t; 417 | 418 | typedef struct 419 | { 420 | uint8_t ACCEL_XOUT_L; 421 | } ICM_20948_ACCEL_XOUT_L_t; 422 | 423 | typedef struct 424 | { 425 | uint8_t ACCEL_YOUT_H; 426 | } ICM_20948_ACCEL_YOUT_H_t; 427 | 428 | typedef struct 429 | { 430 | uint8_t ACCEL_YOUT_L; 431 | } ICM_20948_ACCEL_YOUT_L_t; 432 | 433 | typedef struct 434 | { 435 | uint8_t ACCEL_ZOUT_H; 436 | } ICM_20948_ACCEL_ZOUT_H_t; 437 | 438 | typedef struct 439 | { 440 | uint8_t ACCEL_ZOUT_L; 441 | } ICM_20948_ACCEL_ZOUT_L_t; 442 | 443 | typedef struct 444 | { 445 | uint8_t GYRO_XOUT_H; 446 | } ICM_20948_GYRO_XOUT_H_t; 447 | 448 | typedef struct 449 | { 450 | uint8_t GYRO_XOUT_L; 451 | } ICM_20948_GYRO_XOUT_L_t; 452 | 453 | typedef struct 454 | { 455 | uint8_t GYRO_YOUT_H; 456 | } ICM_20948_GYRO_YOUT_H_t; 457 | 458 | typedef struct 459 | { 460 | uint8_t GYRO_YOUT_L; 461 | } ICM_20948_GYRO_YOUT_L_t; 462 | 463 | typedef struct 464 | { 465 | uint8_t GYRO_ZOUT_H; 466 | } ICM_20948_GYRO_ZOUT_H_t; 467 | 468 | typedef struct 469 | { 470 | uint8_t GYRO_ZOUT_L; 471 | } ICM_20948_GYRO_ZOUT_L_t; 472 | 473 | typedef struct 474 | { 475 | uint8_t TEMP_OUT_H; 476 | } ICM_20948_TEMP_OUT_H_t; 477 | 478 | typedef struct 479 | { 480 | uint8_t TEMP_OUT_L; 481 | } ICM_20948_TEMP_OUT_L_t; 482 | 483 | typedef struct 484 | { 485 | uint8_t DATA; // Note: this is not worth copying 24 times, despite there being 24 registers like this one 486 | } ICM_20948_EXT_PERIPH_SENS_DATA_t; 487 | 488 | typedef struct 489 | { 490 | uint8_t PERIPH_0_FIFO_EN : 1; 491 | uint8_t PERIPH_1_FIFO_EN : 1; 492 | uint8_t PERIPH_2_FIFO_EN : 1; 493 | uint8_t PERIPH_3_FIFO_EN : 1; 494 | uint8_t reserved_0 : 4; 495 | } ICM_20948_FIFO_EN_1_t; 496 | 497 | typedef struct 498 | { 499 | uint8_t TEMP_FIFO_EN : 1; 500 | uint8_t GYRO_X_FIFO_EN : 1; 501 | uint8_t GYRO_Y_FIFO_EN : 1; 502 | uint8_t GYRO_Z_FIFO_EN : 1; 503 | uint8_t ACCEL_FIFO_EN : 1; 504 | uint8_t reserved_0 : 3; 505 | } ICM_20948_FIFO_EN_2_t; 506 | 507 | typedef struct 508 | { 509 | uint8_t FIFO_RESET : 5; 510 | uint8_t reserved_0 : 3; 511 | } ICM_20948_FIFO_RST_t; 512 | 513 | typedef struct 514 | { 515 | uint8_t FIFO_MODE : 5; 516 | uint8_t reserved_0 : 3; 517 | } ICM_20948_FIFO_MODE_t; 518 | 519 | typedef struct 520 | { 521 | uint8_t FIFO_COUNTH; 522 | } ICM_20948_FIFO_COUNTH_t; 523 | 524 | typedef struct 525 | { 526 | uint8_t FIFO_COUNTL; 527 | } ICM_20948_FIFO_COUNTL_t; 528 | 529 | typedef struct 530 | { 531 | uint8_t RAW_DATA_RDY : 4; 532 | uint8_t reserved_0 : 3; 533 | uint8_t WOF_STATUS : 1; 534 | } ICM_20948_DATA_RDY_STATUS_t; 535 | 536 | typedef struct 537 | { 538 | uint8_t FIFO_CFG : 1; 539 | uint8_t reserved_0 : 7; 540 | } ICM_20948_FIFO_CFG_t; 541 | 542 | // User bank 1 Types 543 | 544 | typedef struct 545 | { 546 | uint8_t XG_ST_DATA; 547 | } ICM_20948_SELF_TEST_X_GYRO_t; 548 | 549 | typedef struct 550 | { 551 | uint8_t YG_ST_DATA; 552 | } ICM_20948_SELF_TEST_Y_GYRO_t; 553 | 554 | typedef struct 555 | { 556 | uint8_t ZG_ST_DATA; 557 | } ICM_20948_SELF_TEST_Z_GYRO_t; 558 | 559 | typedef struct 560 | { 561 | uint8_t XA_ST_DATA; 562 | } ICM_20948_SELF_TEST_X_ACCEL_t; 563 | 564 | typedef struct 565 | { 566 | uint8_t YA_ST_DATA; 567 | } ICM_20948_SELF_TEST_Y_ACCEL_t; 568 | 569 | typedef struct 570 | { 571 | uint8_t ZA_ST_DATA; 572 | } ICM_20948_SELF_TEST_Z_ACCEL_t; 573 | 574 | typedef struct 575 | { 576 | uint8_t XA_OFFS_14_7; 577 | } ICM_20948_XA_OFFS_H_t; 578 | 579 | typedef struct 580 | { 581 | uint8_t reserved_0 : 1; 582 | uint8_t XA_OFFS_6_0 : 7; 583 | } ICM_20948_XA_OFFS_L_t; 584 | 585 | typedef struct 586 | { 587 | uint8_t YA_OFFS_14_7; 588 | } ICM_20948_YA_OFFS_H_t; 589 | 590 | typedef struct 591 | { 592 | uint8_t reserved_0 : 1; 593 | uint8_t YA_OFFS_6_0 : 7; 594 | } ICM_20948_YA_OFFS_L_t; 595 | 596 | typedef struct 597 | { 598 | uint8_t ZA_OFFS_14_7; 599 | } ICM_20948_ZA_OFFS_H_t; 600 | 601 | typedef struct 602 | { 603 | uint8_t reserved_0 : 1; 604 | uint8_t ZA_OFFS_6_0 : 7; 605 | } ICM_20948_ZA_OFFS_L_t; 606 | 607 | typedef struct 608 | { 609 | uint8_t TBC_PLL; 610 | } ICM_20948_TIMEBASE_CORRECTION_PLL_t; 611 | 612 | // User Bank 2 Types 613 | typedef struct 614 | { 615 | uint8_t GYRO_SMPLRT_DIV; 616 | } ICM_20948_GYRO_SMPLRT_DIV_t; 617 | 618 | typedef struct 619 | { 620 | uint8_t GYRO_FCHOICE : 1; 621 | uint8_t GYRO_FS_SEL : 2; 622 | uint8_t GYRO_DLPFCFG : 3; 623 | uint8_t reserved_0 : 2; 624 | } ICM_20948_GYRO_CONFIG_1_t; 625 | 626 | typedef struct 627 | { 628 | uint8_t GYRO_AVGCFG : 3; 629 | uint8_t ZGYRO_CTEN : 1; 630 | uint8_t YGYRO_CTEN : 1; 631 | uint8_t XGYRO_CTEN : 1; 632 | uint8_t reserved_0 : 2; 633 | } ICM_20948_GYRO_CONFIG_2_t; 634 | 635 | typedef struct 636 | { 637 | uint8_t XG_OFFS_USER_H; 638 | } ICM_20948_XG_OFFS_USRH_t; 639 | 640 | typedef struct 641 | { 642 | uint8_t XG_OFFS_USER_L; 643 | } ICM_20948_XG_OFFS_USRL_t; 644 | 645 | typedef struct 646 | { 647 | uint8_t YG_OFFS_USER_H; 648 | } ICM_20948_YG_OFFS_USRH_t; 649 | 650 | typedef struct 651 | { 652 | uint8_t YG_OFFS_USER_L; 653 | } ICM_20948_YG_OFFS_USRL_t; 654 | 655 | typedef struct 656 | { 657 | uint8_t ZG_OFFS_USER_H; 658 | } ICM_20948_ZG_OFFS_USRH_t; 659 | 660 | typedef struct 661 | { 662 | uint8_t ZG_OFFS_USER_L; 663 | } ICM_20948_ZG_OFFS_USRL_t; 664 | 665 | typedef struct 666 | { 667 | uint8_t ODR_ALIGN_EN : 1; 668 | uint8_t reserved_0 : 7; 669 | } ICM_20948_ODR_ALIGN_EN_t; 670 | 671 | typedef struct 672 | { 673 | uint8_t ACCEL_SMPLRT_DIV_11_8 : 4; 674 | uint8_t reserved_0 : 4; 675 | } ICM_20948_ACCEL_SMPLRT_DIV_1_t; 676 | 677 | typedef struct 678 | { 679 | uint8_t ACCEL_SMPLRT_DIV_7_0; 680 | } ICM_20948_ACCEL_SMPLRT_DIV_2_t; 681 | 682 | typedef struct 683 | { 684 | uint8_t ACCEL_INTEL_MODE_INT : 1; 685 | uint8_t ACCEL_INTEL_EN : 1; 686 | uint8_t reserved_0 : 6; 687 | } ICM_20948_ACCEL_INTEL_CTRL_t; 688 | 689 | typedef struct 690 | { 691 | uint8_t WOM_THRESHOLD; 692 | } ICM_20948_ACCEL_WOM_THR_t; 693 | 694 | typedef struct 695 | { 696 | uint8_t ACCEL_FCHOICE : 1; 697 | uint8_t ACCEL_FS_SEL : 2; 698 | uint8_t ACCEL_DLPFCFG : 3; 699 | uint8_t reserved_0 : 2; 700 | } ICM_20948_ACCEL_CONFIG_t; 701 | 702 | typedef struct 703 | { 704 | uint8_t DEC3_CFG : 2; 705 | uint8_t AZ_ST_EN : 1; 706 | uint8_t AY_ST_EN : 1; 707 | uint8_t AX_ST_EN : 1; 708 | uint8_t reserved_0 : 3; 709 | } ICM_20948_ACCEL_CONFIG_2_t; 710 | 711 | typedef struct 712 | { 713 | uint8_t EXT_SYNC_SET : 4; 714 | uint8_t WOF_EDGE_INT : 1; 715 | uint8_t WOF_DEGLITCH_EN : 1; 716 | uint8_t reserved_0 : 1; 717 | uint8_t DELAY_TIME_EN : 1; 718 | } ICM_20948_FSYNC_CONFIG_t; 719 | 720 | typedef struct 721 | { 722 | uint8_t TEMP_DLPFCFG : 3; 723 | uint8_t reserved_0 : 5; 724 | } ICM_20948_TEMP_CONFIG_t; 725 | 726 | typedef struct 727 | { 728 | uint8_t REG_LP_DMP_EN : 1; 729 | uint8_t reserved_0 : 7; 730 | } ICM_20948_MOD_CTRL_USR_t; 731 | 732 | // Bank 3 Types 733 | 734 | typedef struct 735 | { 736 | uint8_t I2C_MST_ODR_CONFIG : 4; 737 | uint8_t reserved_0 : 4; 738 | } ICM_20948_I2C_MST_ODR_CONFIG_t; 739 | 740 | typedef struct 741 | { 742 | uint8_t I2C_MST_CLK : 4; 743 | uint8_t I2C_MST_P_NSR : 1; 744 | uint8_t reserved_0 : 2; 745 | uint8_t MULT_MST_EN : 1; 746 | } ICM_20948_I2C_MST_CTRL_t; 747 | 748 | typedef struct 749 | { 750 | uint8_t I2C_PERIPH0_DELAY_EN : 1; 751 | uint8_t I2C_PERIPH1_DELAY_EN : 1; 752 | uint8_t I2C_PERIPH2_DELAY_EN : 1; 753 | uint8_t I2C_PERIPH3_DELAY_EN : 1; 754 | uint8_t I2C_PERIPH4_DELAY_EN : 1; 755 | uint8_t reserved_0 : 2; 756 | uint8_t DELAY_ES_SHADOW : 1; 757 | } ICM_20948_I2C_MST_DELAY_CTRL_t; 758 | 759 | typedef struct 760 | { 761 | uint8_t ID : 7; 762 | uint8_t RNW : 1; 763 | } ICM_20948_I2C_PERIPHX_ADDR_t; 764 | 765 | typedef struct 766 | { 767 | uint8_t REG; 768 | } ICM_20948_I2C_PERIPHX_REG_t; 769 | 770 | typedef struct 771 | { 772 | uint8_t LENG : 4; 773 | uint8_t GRP : 1; 774 | uint8_t REG_DIS : 1; 775 | uint8_t BYTE_SW : 1; 776 | uint8_t EN : 1; 777 | } ICM_20948_I2C_PERIPHX_CTRL_t; 778 | 779 | typedef struct 780 | { 781 | uint8_t DO; 782 | } ICM_20948_I2C_PERIPHX_DO_t; 783 | 784 | typedef struct 785 | { 786 | uint8_t DLY : 5; 787 | uint8_t REG_DIS : 1; 788 | uint8_t INT_EN : 1; 789 | uint8_t EN : 1; 790 | } ICM_20948_I2C_PERIPH4_CTRL_t; 791 | 792 | typedef struct 793 | { 794 | uint8_t DI; 795 | } ICM_20948_I2C_PERIPH4_DI_t; 796 | 797 | // Bank select register! 798 | 799 | typedef struct 800 | { 801 | uint8_t reserved_0 : 4; 802 | uint8_t USER_BANK : 2; 803 | uint8_t reserved_1 : 2; 804 | } ICM_20948_REG_BANK_SEL_t; 805 | 806 | #ifdef __cplusplus 807 | } 808 | #endif /* __cplusplus */ 809 | 810 | #endif /* _ICM_20948_REGISTERS_H_ */ 811 | --------------------------------------------------------------------------------