Modified Arduino library for ICM_20948 IMU for Nucleo boards
util/ICM_20948_C.c
- Committer:
- saloutos
- Date:
- 2022-01-31
- Revision:
- 0:894b603d32ee
File content as of revision 0:894b603d32ee:
#include "ICM_20948_C.h" #include "ICM_20948_REGISTERS.h" #include "AK09916_REGISTERS.h" /* * Icm20948 device require a DMP image to be loaded on init * Provide such images by mean of a byte array */ #if defined(ICM_20948_USE_DMP) // Only include the 14301 Bytes of DMP if ICM_20948_USE_DMP is defined #if defined(ARDUINO_ARCH_MBED) // ARDUINO_ARCH_MBED (APOLLO3 v2) does not support or require pgmspace.h / PROGMEM const uint8_t dmp3_image[] = { #elif (defined(__AVR__) || defined(__arm__) || defined(__ARDUINO_ARC__)) && !defined(__linux__) // Store the DMP firmware in PROGMEM on older AVR (ATmega) platforms #define ICM_20948_USE_PROGMEM_FOR_DMP #include <avr/pgmspace.h> const uint8_t dmp3_image[] PROGMEM = { #else const uint8_t dmp3_image[] = { #endif #include "icm20948_img.dmp3a.h" }; #endif // ICM-20948 data is big-endian. We need to make it little-endian when writing into icm_20948_DMP_data_t const int DMP_Quat9_Byte_Ordering[icm_20948_DMP_Quat9_Bytes] = { 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 13, 12 // Also used for Geomag }; const int DMP_Quat6_Byte_Ordering[icm_20948_DMP_Quat6_Bytes] = { 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8 // Also used for Gyro_Calibr, Compass_Calibr }; const int DMP_PQuat6_Byte_Ordering[icm_20948_DMP_PQuat6_Bytes] = { 1, 0, 3, 2, 5, 4 // Also used for Raw_Accel, Compass }; const int DMP_Raw_Gyro_Byte_Ordering[icm_20948_DMP_Raw_Gyro_Bytes + icm_20948_DMP_Gyro_Bias_Bytes] = { 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10}; const int DMP_Activity_Recognition_Byte_Ordering[icm_20948_DMP_Activity_Recognition_Bytes] = { 0, 1, 5, 4, 3, 2}; const int DMP_Secondary_On_Off_Byte_Ordering[icm_20948_DMP_Secondary_On_Off_Bytes] = { 1, 0}; const uint16_t inv_androidSensor_to_control_bits[ANDROID_SENSOR_NUM_MAX] = { // Data output control 1 register bit definition // 16-bit accel 0x8000 // 16-bit gyro 0x4000 // 16-bit compass 0x2000 // 16-bit ALS 0x1000 // 32-bit 6-axis quaternion 0x0800 // 32-bit 9-axis quaternion + heading accuracy 0x0400 // 16-bit pedometer quaternion 0x0200 // 32-bit Geomag rv + heading accuracy 0x0100 // 16-bit Pressure 0x0080 // 32-bit calibrated gyro 0x0040 // 32-bit calibrated compass 0x0020 // Pedometer Step Detector 0x0010 // Header 2 0x0008 // Pedometer Step Indicator Bit 2 0x0004 // Pedometer Step Indicator Bit 1 0x0002 // Pedometer Step Indicator Bit 0 0x0001 // Unsupported Sensors are 0xFFFF 0xFFFF, // 0 Meta Data 0x8008, // 1 Accelerometer 0x0028, // 2 Magnetic Field 0x0408, // 3 Orientation 0x4048, // 4 Gyroscope 0x1008, // 5 Light 0x0088, // 6 Pressure 0xFFFF, // 7 Temperature 0xFFFF, // 8 Proximity <----------- fixme 0x0808, // 9 Gravity 0x8808, // 10 Linear Acceleration 0x0408, // 11 Rotation Vector 0xFFFF, // 12 Humidity 0xFFFF, // 13 Ambient Temperature 0x2008, // 14 Magnetic Field Uncalibrated 0x0808, // 15 Game Rotation Vector 0x4008, // 16 Gyroscope Uncalibrated 0x0000, // 17 Significant Motion 0x0018, // 18 Step Detector 0x0010, // 19 Step Counter <----------- fixme 0x0108, // 20 Geomagnetic Rotation Vector 0xFFFF, // 21 ANDROID_SENSOR_HEART_RATE, 0xFFFF, // 22 ANDROID_SENSOR_PROXIMITY, 0x8008, // 23 ANDROID_SENSOR_WAKEUP_ACCELEROMETER, 0x0028, // 24 ANDROID_SENSOR_WAKEUP_MAGNETIC_FIELD, 0x0408, // 25 ANDROID_SENSOR_WAKEUP_ORIENTATION, 0x4048, // 26 ANDROID_SENSOR_WAKEUP_GYROSCOPE, 0x1008, // 27 ANDROID_SENSOR_WAKEUP_LIGHT, 0x0088, // 28 ANDROID_SENSOR_WAKEUP_PRESSURE, 0x0808, // 29 ANDROID_SENSOR_WAKEUP_GRAVITY, 0x8808, // 30 ANDROID_SENSOR_WAKEUP_LINEAR_ACCELERATION, 0x0408, // 31 ANDROID_SENSOR_WAKEUP_ROTATION_VECTOR, 0xFFFF, // 32 ANDROID_SENSOR_WAKEUP_RELATIVE_HUMIDITY, 0xFFFF, // 33 ANDROID_SENSOR_WAKEUP_AMBIENT_TEMPERATURE, 0x2008, // 34 ANDROID_SENSOR_WAKEUP_MAGNETIC_FIELD_UNCALIBRATED, 0x0808, // 35 ANDROID_SENSOR_WAKEUP_GAME_ROTATION_VECTOR, 0x4008, // 36 ANDROID_SENSOR_WAKEUP_GYROSCOPE_UNCALIBRATED, 0x0018, // 37 ANDROID_SENSOR_WAKEUP_STEP_DETECTOR, 0x0010, // 38 ANDROID_SENSOR_WAKEUP_STEP_COUNTER, 0x0108, // 39 ANDROID_SENSOR_WAKEUP_GEOMAGNETIC_ROTATION_VECTOR 0xFFFF, // 40 ANDROID_SENSOR_WAKEUP_HEART_RATE, 0x0000, // 41 ANDROID_SENSOR_WAKEUP_TILT_DETECTOR, 0x8008, // 42 Raw Acc 0x4048, // 43 Raw Gyr }; const ICM_20948_Serif_t NullSerif = { NULL, // write NULL, // read NULL, // user }; // Private function prototypes // Function definitions ICM_20948_Status_e ICM_20948_init_struct(ICM_20948_Device_t *pdev) { // Initialize all elements by 0 except for _last_bank // Initialize _last_bank to 4 (invalid bank number) // so ICM_20948_set_bank function does not skip issuing bank change operation static const ICM_20948_Device_t init_device = { ._last_bank = 4 }; *pdev = init_device; return ICM_20948_Stat_Ok; } ICM_20948_Status_e ICM_20948_link_serif(ICM_20948_Device_t *pdev, const ICM_20948_Serif_t *s) { if (s == NULL) { return ICM_20948_Stat_ParamErr; } if (pdev == NULL) { return ICM_20948_Stat_ParamErr; } pdev->_serif = s; return ICM_20948_Stat_Ok; } ICM_20948_Status_e ICM_20948_execute_w(ICM_20948_Device_t *pdev, uint8_t regaddr, uint8_t *pdata, uint32_t len) { if (pdev->_serif->write == NULL) { return ICM_20948_Stat_NotImpl; } return (*pdev->_serif->write)(regaddr, pdata, len, pdev->_serif->user); } ICM_20948_Status_e ICM_20948_execute_r(ICM_20948_Device_t *pdev, uint8_t regaddr, uint8_t *pdata, uint32_t len) { if (pdev->_serif->read == NULL) { return ICM_20948_Stat_NotImpl; } return (*pdev->_serif->read)(regaddr, pdata, len, pdev->_serif->user); } //Transact directly with an I2C device, one byte at a time //Used to configure a device before it is setup into a normal 0-3 peripheral slot 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) { // Thanks MikeFair! // https://github.com/kriswiner/MPU9250/issues/86 ICM_20948_Status_e retval = ICM_20948_Stat_Ok; addr = (((Rw) ? 0x80 : 0x00) | addr); retval = ICM_20948_set_bank(pdev, 3); retval = ICM_20948_execute_w(pdev, AGB3_REG_I2C_PERIPH4_ADDR, (uint8_t *)&addr, 1); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_set_bank(pdev, 3); retval = ICM_20948_execute_w(pdev, AGB3_REG_I2C_PERIPH4_REG, (uint8_t *)®, 1); if (retval != ICM_20948_Stat_Ok) { return retval; } ICM_20948_I2C_PERIPH4_CTRL_t ctrl; ctrl.EN = 1; ctrl.INT_EN = false; ctrl.DLY = 0; ctrl.REG_DIS = !send_reg_addr; ICM_20948_I2C_MST_STATUS_t i2c_mst_status; bool txn_failed = false; uint16_t nByte = 0; while (nByte < len) { if (!Rw) { retval = ICM_20948_set_bank(pdev, 3); retval = ICM_20948_execute_w(pdev, AGB3_REG_I2C_PERIPH4_DO, (uint8_t *)&(data[nByte]), 1); if (retval != ICM_20948_Stat_Ok) { return retval; } } // Kick off txn retval = ICM_20948_set_bank(pdev, 3); retval = ICM_20948_execute_w(pdev, AGB3_REG_I2C_PERIPH4_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_I2C_PERIPH4_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } // long tsTimeout = millis() + 3000; // Emergency timeout for txn (hard coded to 3 secs) uint32_t max_cycles = 1000; uint32_t count = 0; bool peripheral4Done = false; while (!peripheral4Done) { retval = ICM_20948_set_bank(pdev, 0); retval = ICM_20948_execute_r(pdev, AGB0_REG_I2C_MST_STATUS, (uint8_t *)&i2c_mst_status, 1); peripheral4Done = (i2c_mst_status.I2C_PERIPH4_DONE /*| (millis() > tsTimeout) */); //Avoid forever-loops peripheral4Done |= (count >= max_cycles); count++; } txn_failed = (i2c_mst_status.I2C_PERIPH4_NACK /*| (millis() > tsTimeout) */); txn_failed |= (count >= max_cycles); if (txn_failed) break; if (Rw) { retval = ICM_20948_set_bank(pdev, 3); retval = ICM_20948_execute_r(pdev, AGB3_REG_I2C_PERIPH4_DI, &data[nByte], 1); } nByte++; } if (txn_failed) { //We often fail here if mag is stuck return ICM_20948_Stat_Err; } return retval; } ICM_20948_Status_e ICM_20948_i2c_master_single_w(ICM_20948_Device_t *pdev, uint8_t addr, uint8_t reg, uint8_t *data) { return ICM_20948_i2c_controller_periph4_txn(pdev, addr, reg, data, 1, false, true); } ICM_20948_Status_e ICM_20948_i2c_master_single_r(ICM_20948_Device_t *pdev, uint8_t addr, uint8_t reg, uint8_t *data) { return ICM_20948_i2c_controller_periph4_txn(pdev, addr, reg, data, 1, true, true); } ICM_20948_Status_e ICM_20948_set_bank(ICM_20948_Device_t *pdev, uint8_t bank) { if (bank > 3) { return ICM_20948_Stat_ParamErr; } // Only 4 possible banks if (bank == pdev->_last_bank) // Do we need to change bank? return ICM_20948_Stat_Ok; // Bail if we don't need to change bank to avoid unnecessary bus traffic pdev->_last_bank = bank; // Store the requested bank (before we bit-shift) bank = (bank << 4) & 0x30; // bits 5:4 of REG_BANK_SEL return ICM_20948_execute_w(pdev, REG_BANK_SEL, &bank, 1); } ICM_20948_Status_e ICM_20948_sw_reset(ICM_20948_Device_t *pdev) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_PWR_MGMT_1_t reg; ICM_20948_set_bank(pdev, 0); // Must be in the right bank retval = ICM_20948_execute_r(pdev, AGB0_REG_PWR_MGMT_1, (uint8_t *)®, sizeof(ICM_20948_PWR_MGMT_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } reg.DEVICE_RESET = 1; retval = ICM_20948_execute_w(pdev, AGB0_REG_PWR_MGMT_1, (uint8_t *)®, sizeof(ICM_20948_PWR_MGMT_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_sleep(ICM_20948_Device_t *pdev, bool on) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_PWR_MGMT_1_t reg; ICM_20948_set_bank(pdev, 0); // Must be in the right bank retval = ICM_20948_execute_r(pdev, AGB0_REG_PWR_MGMT_1, (uint8_t *)®, sizeof(ICM_20948_PWR_MGMT_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } if (on) { reg.SLEEP = 1; } else { reg.SLEEP = 0; } retval = ICM_20948_execute_w(pdev, AGB0_REG_PWR_MGMT_1, (uint8_t *)®, sizeof(ICM_20948_PWR_MGMT_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_low_power(ICM_20948_Device_t *pdev, bool on) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_PWR_MGMT_1_t reg; ICM_20948_set_bank(pdev, 0); // Must be in the right bank retval = ICM_20948_execute_r(pdev, AGB0_REG_PWR_MGMT_1, (uint8_t *)®, sizeof(ICM_20948_PWR_MGMT_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } if (on) { reg.LP_EN = 1; } else { reg.LP_EN = 0; } retval = ICM_20948_execute_w(pdev, AGB0_REG_PWR_MGMT_1, (uint8_t *)®, sizeof(ICM_20948_PWR_MGMT_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_set_clock_source(ICM_20948_Device_t *pdev, ICM_20948_PWR_MGMT_1_CLKSEL_e source) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_PWR_MGMT_1_t reg; ICM_20948_set_bank(pdev, 0); // Must be in the right bank retval = ICM_20948_execute_r(pdev, AGB0_REG_PWR_MGMT_1, (uint8_t *)®, sizeof(ICM_20948_PWR_MGMT_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } reg.CLKSEL = source; retval = ICM_20948_execute_w(pdev, AGB0_REG_PWR_MGMT_1, (uint8_t *)®, sizeof(ICM_20948_PWR_MGMT_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_get_who_am_i(ICM_20948_Device_t *pdev, uint8_t *whoami) { if (whoami == NULL) { return ICM_20948_Stat_ParamErr; } ICM_20948_set_bank(pdev, 0); // Must be in the right bank return ICM_20948_execute_r(pdev, AGB0_REG_WHO_AM_I, whoami, 1); } ICM_20948_Status_e ICM_20948_check_id(ICM_20948_Device_t *pdev) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; uint8_t whoami = 0x00; retval = ICM_20948_get_who_am_i(pdev, &whoami); if (retval != ICM_20948_Stat_Ok) { return retval; } if (whoami != ICM_20948_WHOAMI) { return ICM_20948_Stat_WrongID; } return retval; } ICM_20948_Status_e ICM_20948_data_ready(ICM_20948_Device_t *pdev) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_INT_STATUS_1_t reg; retval = ICM_20948_set_bank(pdev, 0); // Must be in the right bank if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_INT_STATUS_1, (uint8_t *)®, sizeof(ICM_20948_INT_STATUS_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } if (!reg.RAW_DATA_0_RDY_INT) { retval = ICM_20948_Stat_NoData; } return retval; } // Interrupt Configuration 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) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; retval = ICM_20948_set_bank(pdev, 0); // Must be in the right bank if (write != NULL) { // write first, if available retval = ICM_20948_execute_w(pdev, AGB0_REG_INT_PIN_CONFIG, (uint8_t *)write, sizeof(ICM_20948_INT_PIN_CFG_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } } if (read != NULL) { // then read, to allow for verification retval = ICM_20948_execute_r(pdev, AGB0_REG_INT_PIN_CONFIG, (uint8_t *)read, sizeof(ICM_20948_INT_PIN_CFG_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } } return retval; } 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) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_INT_ENABLE_t en_0; ICM_20948_INT_ENABLE_1_t en_1; ICM_20948_INT_ENABLE_2_t en_2; ICM_20948_INT_ENABLE_3_t en_3; retval = ICM_20948_set_bank(pdev, 0); // Must be in the right bank if (write != NULL) { // If the write pointer is not NULL then write to the registers BEFORE reading en_0.I2C_MST_INT_EN = write->I2C_MST_INT_EN; en_0.DMP_INT1_EN = write->DMP_INT1_EN; en_0.PLL_READY_EN = write->PLL_RDY_EN; en_0.WOM_INT_EN = write->WOM_INT_EN; en_0.reserved_0 = 0; // Clear RAM garbage en_0.REG_WOF_EN = write->REG_WOF_EN; en_1.RAW_DATA_0_RDY_EN = write->RAW_DATA_0_RDY_EN; en_1.reserved_0 = 0; // Clear RAM garbage en_2.individual.FIFO_OVERFLOW_EN_4 = write->FIFO_OVERFLOW_EN_4; en_2.individual.FIFO_OVERFLOW_EN_3 = write->FIFO_OVERFLOW_EN_3; en_2.individual.FIFO_OVERFLOW_EN_2 = write->FIFO_OVERFLOW_EN_2; en_2.individual.FIFO_OVERFLOW_EN_1 = write->FIFO_OVERFLOW_EN_1; en_2.individual.FIFO_OVERFLOW_EN_0 = write->FIFO_OVERFLOW_EN_0; en_2.individual.reserved_0 = 0; // Clear RAM garbage en_3.individual.FIFO_WM_EN_4 = write->FIFO_WM_EN_4; en_3.individual.FIFO_WM_EN_3 = write->FIFO_WM_EN_3; en_3.individual.FIFO_WM_EN_2 = write->FIFO_WM_EN_2; en_3.individual.FIFO_WM_EN_1 = write->FIFO_WM_EN_1; en_3.individual.FIFO_WM_EN_0 = write->FIFO_WM_EN_0; en_3.individual.reserved_0 = 0; // Clear RAM garbage retval = ICM_20948_execute_w(pdev, AGB0_REG_INT_ENABLE, (uint8_t *)&en_0, sizeof(ICM_20948_INT_ENABLE_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_w(pdev, AGB0_REG_INT_ENABLE_1, (uint8_t *)&en_1, sizeof(ICM_20948_INT_ENABLE_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_w(pdev, AGB0_REG_INT_ENABLE_2, (uint8_t *)&en_2, sizeof(ICM_20948_INT_ENABLE_2_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_w(pdev, AGB0_REG_INT_ENABLE_3, (uint8_t *)&en_3, sizeof(ICM_20948_INT_ENABLE_3_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } } if (read != NULL) { // If read pointer is not NULL then read the registers (if write is not NULL then this should read back the results of write into read) retval = ICM_20948_execute_r(pdev, AGB0_REG_INT_ENABLE, (uint8_t *)&en_0, sizeof(ICM_20948_INT_ENABLE_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_INT_ENABLE_1, (uint8_t *)&en_1, sizeof(ICM_20948_INT_ENABLE_1_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_INT_ENABLE_2, (uint8_t *)&en_2, sizeof(ICM_20948_INT_ENABLE_2_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_INT_ENABLE_3, (uint8_t *)&en_3, sizeof(ICM_20948_INT_ENABLE_3_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } read->I2C_MST_INT_EN = en_0.I2C_MST_INT_EN; read->DMP_INT1_EN = en_0.DMP_INT1_EN; read->PLL_RDY_EN = en_0.PLL_READY_EN; read->WOM_INT_EN = en_0.WOM_INT_EN; read->REG_WOF_EN = en_0.REG_WOF_EN; read->RAW_DATA_0_RDY_EN = en_1.RAW_DATA_0_RDY_EN; read->FIFO_OVERFLOW_EN_4 = en_2.individual.FIFO_OVERFLOW_EN_4; read->FIFO_OVERFLOW_EN_3 = en_2.individual.FIFO_OVERFLOW_EN_3; read->FIFO_OVERFLOW_EN_2 = en_2.individual.FIFO_OVERFLOW_EN_2; read->FIFO_OVERFLOW_EN_1 = en_2.individual.FIFO_OVERFLOW_EN_1; read->FIFO_OVERFLOW_EN_0 = en_2.individual.FIFO_OVERFLOW_EN_0; read->FIFO_WM_EN_4 = en_3.individual.FIFO_WM_EN_4; read->FIFO_WM_EN_3 = en_3.individual.FIFO_WM_EN_3; read->FIFO_WM_EN_2 = en_3.individual.FIFO_WM_EN_2; read->FIFO_WM_EN_1 = en_3.individual.FIFO_WM_EN_1; read->FIFO_WM_EN_0 = en_3.individual.FIFO_WM_EN_0; } return retval; } 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) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_ACCEL_WOM_THR_t thr; retval = ICM_20948_set_bank(pdev, 2); // Must be in the right bank if (write != NULL) { // If the write pointer is not NULL then write to the registers BEFORE reading thr.WOM_THRESHOLD = write->WOM_THRESHOLD; retval = ICM_20948_execute_w(pdev, AGB2_REG_ACCEL_WOM_THR, (uint8_t *)&thr, sizeof(ICM_20948_ACCEL_WOM_THR_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } } if (read != NULL) { // If read pointer is not NULL then read the registers (if write is not NULL then this should read back the results of write into read) retval = ICM_20948_execute_r(pdev, AGB2_REG_ACCEL_WOM_THR, (uint8_t *)&thr, sizeof(ICM_20948_ACCEL_WOM_THR_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } read->WOM_THRESHOLD = thr.WOM_THRESHOLD; } return retval; } 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) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_LP_CONFIG_t reg; if (!(sensors & (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr | ICM_20948_Internal_Mst))) { return ICM_20948_Stat_SensorNotSupported; } retval = ICM_20948_set_bank(pdev, 0); // Must be in the right bank if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_LP_CONFIG, (uint8_t *)®, sizeof(ICM_20948_LP_CONFIG_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } if (sensors & ICM_20948_Internal_Acc) { reg.ACCEL_CYCLE = mode; } // Set all desired sensors to this setting if (sensors & ICM_20948_Internal_Gyr) { reg.GYRO_CYCLE = mode; } if (sensors & ICM_20948_Internal_Mst) { reg.I2C_MST_CYCLE = mode; } retval = ICM_20948_execute_w(pdev, AGB0_REG_LP_CONFIG, (uint8_t *)®, sizeof(ICM_20948_LP_CONFIG_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } // Check the data was written correctly retval = ICM_20948_execute_r(pdev, AGB0_REG_LP_CONFIG, (uint8_t *)®, sizeof(ICM_20948_LP_CONFIG_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } if (sensors & ICM_20948_Internal_Acc) { if (reg.ACCEL_CYCLE != mode) retval = ICM_20948_Stat_Err; } if (sensors & ICM_20948_Internal_Gyr) { if (reg.GYRO_CYCLE != mode) retval = ICM_20948_Stat_Err; } if (sensors & ICM_20948_Internal_Mst) { if (reg.I2C_MST_CYCLE != mode) retval = ICM_20948_Stat_Err; } return retval; } ICM_20948_Status_e ICM_20948_set_full_scale(ICM_20948_Device_t *pdev, ICM_20948_InternalSensorID_bm sensors, ICM_20948_fss_t fss) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; if (!(sensors & (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr))) { return ICM_20948_Stat_SensorNotSupported; } if (sensors & ICM_20948_Internal_Acc) { ICM_20948_ACCEL_CONFIG_t reg; retval |= ICM_20948_set_bank(pdev, 2); // Must be in the right bank retval |= ICM_20948_execute_r(pdev, AGB2_REG_ACCEL_CONFIG, (uint8_t *)®, sizeof(ICM_20948_ACCEL_CONFIG_t)); reg.ACCEL_FS_SEL = fss.a; retval |= ICM_20948_execute_w(pdev, AGB2_REG_ACCEL_CONFIG, (uint8_t *)®, sizeof(ICM_20948_ACCEL_CONFIG_t)); // Check the data was written correctly retval |= ICM_20948_execute_r(pdev, AGB2_REG_ACCEL_CONFIG, (uint8_t *)®, sizeof(ICM_20948_ACCEL_CONFIG_t)); if (reg.ACCEL_FS_SEL != fss.a) retval |= ICM_20948_Stat_Err; } if (sensors & ICM_20948_Internal_Gyr) { ICM_20948_GYRO_CONFIG_1_t reg; retval |= ICM_20948_set_bank(pdev, 2); // Must be in the right bank retval |= ICM_20948_execute_r(pdev, AGB2_REG_GYRO_CONFIG_1, (uint8_t *)®, sizeof(ICM_20948_GYRO_CONFIG_1_t)); reg.GYRO_FS_SEL = fss.g; retval |= ICM_20948_execute_w(pdev, AGB2_REG_GYRO_CONFIG_1, (uint8_t *)®, sizeof(ICM_20948_GYRO_CONFIG_1_t)); // Check the data was written correctly retval |= ICM_20948_execute_r(pdev, AGB2_REG_GYRO_CONFIG_1, (uint8_t *)®, sizeof(ICM_20948_GYRO_CONFIG_1_t)); if (reg.GYRO_FS_SEL != fss.g) retval |= ICM_20948_Stat_Err; } return retval; } ICM_20948_Status_e ICM_20948_set_dlpf_cfg(ICM_20948_Device_t *pdev, ICM_20948_InternalSensorID_bm sensors, ICM_20948_dlpcfg_t cfg) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; if (!(sensors & (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr))) { return ICM_20948_Stat_SensorNotSupported; } if (sensors & ICM_20948_Internal_Acc) { ICM_20948_ACCEL_CONFIG_t reg; retval |= ICM_20948_set_bank(pdev, 2); // Must be in the right bank retval |= ICM_20948_execute_r(pdev, AGB2_REG_ACCEL_CONFIG, (uint8_t *)®, sizeof(ICM_20948_ACCEL_CONFIG_t)); reg.ACCEL_DLPFCFG = cfg.a; retval |= ICM_20948_execute_w(pdev, AGB2_REG_ACCEL_CONFIG, (uint8_t *)®, sizeof(ICM_20948_ACCEL_CONFIG_t)); // Check the data was written correctly retval |= ICM_20948_execute_r(pdev, AGB2_REG_ACCEL_CONFIG, (uint8_t *)®, sizeof(ICM_20948_ACCEL_CONFIG_t)); if (reg.ACCEL_DLPFCFG != cfg.a) retval |= ICM_20948_Stat_Err; } if (sensors & ICM_20948_Internal_Gyr) { ICM_20948_GYRO_CONFIG_1_t reg; retval |= ICM_20948_set_bank(pdev, 2); // Must be in the right bank retval |= ICM_20948_execute_r(pdev, AGB2_REG_GYRO_CONFIG_1, (uint8_t *)®, sizeof(ICM_20948_GYRO_CONFIG_1_t)); reg.GYRO_DLPFCFG = cfg.g; retval |= ICM_20948_execute_w(pdev, AGB2_REG_GYRO_CONFIG_1, (uint8_t *)®, sizeof(ICM_20948_GYRO_CONFIG_1_t)); // Check the data was written correctly retval |= ICM_20948_execute_r(pdev, AGB2_REG_GYRO_CONFIG_1, (uint8_t *)®, sizeof(ICM_20948_GYRO_CONFIG_1_t)); if (reg.GYRO_DLPFCFG != cfg.g) retval |= ICM_20948_Stat_Err; } return retval; } ICM_20948_Status_e ICM_20948_enable_dlpf(ICM_20948_Device_t *pdev, ICM_20948_InternalSensorID_bm sensors, bool enable) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; if (!(sensors & (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr))) { return ICM_20948_Stat_SensorNotSupported; } if (sensors & ICM_20948_Internal_Acc) { ICM_20948_ACCEL_CONFIG_t reg; retval |= ICM_20948_set_bank(pdev, 2); // Must be in the right bank retval |= ICM_20948_execute_r(pdev, AGB2_REG_ACCEL_CONFIG, (uint8_t *)®, sizeof(ICM_20948_ACCEL_CONFIG_t)); if (enable) { reg.ACCEL_FCHOICE = 1; } else { reg.ACCEL_FCHOICE = 0; } retval |= ICM_20948_execute_w(pdev, AGB2_REG_ACCEL_CONFIG, (uint8_t *)®, sizeof(ICM_20948_ACCEL_CONFIG_t)); // Check the data was written correctly retval |= ICM_20948_execute_r(pdev, AGB2_REG_ACCEL_CONFIG, (uint8_t *)®, sizeof(ICM_20948_ACCEL_CONFIG_t)); if (enable) { if (reg.ACCEL_FCHOICE != 1) retval |= ICM_20948_Stat_Err; } else { if (reg.ACCEL_FCHOICE != 0) retval |= ICM_20948_Stat_Err; } } if (sensors & ICM_20948_Internal_Gyr) { ICM_20948_GYRO_CONFIG_1_t reg; retval |= ICM_20948_set_bank(pdev, 2); // Must be in the right bank retval |= ICM_20948_execute_r(pdev, AGB2_REG_GYRO_CONFIG_1, (uint8_t *)®, sizeof(ICM_20948_GYRO_CONFIG_1_t)); if (enable) { reg.GYRO_FCHOICE = 1; } else { reg.GYRO_FCHOICE = 0; } retval |= ICM_20948_execute_w(pdev, AGB2_REG_GYRO_CONFIG_1, (uint8_t *)®, sizeof(ICM_20948_GYRO_CONFIG_1_t)); // Check the data was written correctly retval |= ICM_20948_execute_r(pdev, AGB2_REG_GYRO_CONFIG_1, (uint8_t *)®, sizeof(ICM_20948_GYRO_CONFIG_1_t)); if (enable) { if (reg.GYRO_FCHOICE != 1) retval |= ICM_20948_Stat_Err; } else { if (reg.GYRO_FCHOICE != 0) retval |= ICM_20948_Stat_Err; } } return retval; } ICM_20948_Status_e ICM_20948_set_sample_rate(ICM_20948_Device_t *pdev, ICM_20948_InternalSensorID_bm sensors, ICM_20948_smplrt_t smplrt) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; if (!(sensors & (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr))) { return ICM_20948_Stat_SensorNotSupported; } if (sensors & ICM_20948_Internal_Acc) { retval |= ICM_20948_set_bank(pdev, 2); // Must be in the right bank uint8_t div1 = (smplrt.a << 8); uint8_t div2 = (smplrt.a & 0xFF); retval |= ICM_20948_execute_w(pdev, AGB2_REG_ACCEL_SMPLRT_DIV_1, &div1, 1); retval |= ICM_20948_execute_w(pdev, AGB2_REG_ACCEL_SMPLRT_DIV_2, &div2, 1); } if (sensors & ICM_20948_Internal_Gyr) { retval |= ICM_20948_set_bank(pdev, 2); // Must be in the right bank uint8_t div = (smplrt.g); retval |= ICM_20948_execute_w(pdev, AGB2_REG_GYRO_SMPLRT_DIV, &div, 1); } return retval; } // Interface Things ICM_20948_Status_e ICM_20948_i2c_master_passthrough(ICM_20948_Device_t *pdev, bool passthrough) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_INT_PIN_CFG_t reg; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_INT_PIN_CONFIG, (uint8_t *)®, sizeof(ICM_20948_INT_PIN_CFG_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } reg.BYPASS_EN = passthrough; retval = ICM_20948_execute_w(pdev, AGB0_REG_INT_PIN_CONFIG, (uint8_t *)®, sizeof(ICM_20948_INT_PIN_CFG_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_i2c_master_enable(ICM_20948_Device_t *pdev, bool enable) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; // Disable BYPASS_EN retval = ICM_20948_i2c_master_passthrough(pdev, false); if (retval != ICM_20948_Stat_Ok) { return retval; } ICM_20948_I2C_MST_CTRL_t ctrl; retval = ICM_20948_set_bank(pdev, 3); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB3_REG_I2C_MST_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_I2C_MST_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } ctrl.I2C_MST_CLK = 0x07; // corresponds to 345.6 kHz, good for up to 400 kHz ctrl.I2C_MST_P_NSR = 1; retval = ICM_20948_execute_w(pdev, AGB3_REG_I2C_MST_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_I2C_MST_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } ICM_20948_USER_CTRL_t reg; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_USER_CTRL, (uint8_t *)®, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } if (enable) { reg.I2C_MST_EN = 1; } else { reg.I2C_MST_EN = 0; } retval = ICM_20948_execute_w(pdev, AGB0_REG_USER_CTRL, (uint8_t *)®, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_i2c_master_reset(ICM_20948_Device_t *pdev) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_USER_CTRL_t ctrl; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_USER_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } ctrl.I2C_MST_RST = 1; //Reset! retval = ICM_20948_execute_w(pdev, AGB0_REG_USER_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } 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) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; uint8_t periph_addr_reg; uint8_t periph_reg_reg; uint8_t periph_ctrl_reg; uint8_t periph_do_reg; switch (peripheral) { case 0: periph_addr_reg = AGB3_REG_I2C_PERIPH0_ADDR; periph_reg_reg = AGB3_REG_I2C_PERIPH0_REG; periph_ctrl_reg = AGB3_REG_I2C_PERIPH0_CTRL; periph_do_reg = AGB3_REG_I2C_PERIPH0_DO; break; case 1: periph_addr_reg = AGB3_REG_I2C_PERIPH1_ADDR; periph_reg_reg = AGB3_REG_I2C_PERIPH1_REG; periph_ctrl_reg = AGB3_REG_I2C_PERIPH1_CTRL; periph_do_reg = AGB3_REG_I2C_PERIPH1_DO; break; case 2: periph_addr_reg = AGB3_REG_I2C_PERIPH2_ADDR; periph_reg_reg = AGB3_REG_I2C_PERIPH2_REG; periph_ctrl_reg = AGB3_REG_I2C_PERIPH2_CTRL; periph_do_reg = AGB3_REG_I2C_PERIPH2_DO; break; case 3: periph_addr_reg = AGB3_REG_I2C_PERIPH3_ADDR; periph_reg_reg = AGB3_REG_I2C_PERIPH3_REG; periph_ctrl_reg = AGB3_REG_I2C_PERIPH3_CTRL; periph_do_reg = AGB3_REG_I2C_PERIPH3_DO; break; default: return ICM_20948_Stat_ParamErr; } retval = ICM_20948_set_bank(pdev, 3); if (retval != ICM_20948_Stat_Ok) { return retval; } // Set the peripheral address and the Rw flag ICM_20948_I2C_PERIPHX_ADDR_t address; address.ID = addr; if (Rw) { address.RNW = 1; } else { address.RNW = 0; // Make sure bit is clear (just in case there is any garbage in that RAM location) } retval = ICM_20948_execute_w(pdev, periph_addr_reg, (uint8_t *)&address, sizeof(ICM_20948_I2C_PERIPHX_ADDR_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } // If we are setting up a write, configure the Data Out register too if (!Rw) { ICM_20948_I2C_PERIPHX_DO_t dataOutByte; dataOutByte.DO = dataOut; retval = ICM_20948_execute_w(pdev, periph_do_reg, (uint8_t *)&dataOutByte, sizeof(ICM_20948_I2C_PERIPHX_DO_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } } // Set the peripheral sub-address (register address) ICM_20948_I2C_PERIPHX_REG_t subaddress; subaddress.REG = reg; retval = ICM_20948_execute_w(pdev, periph_reg_reg, (uint8_t *)&subaddress, sizeof(ICM_20948_I2C_PERIPHX_REG_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } // Set up the control info ICM_20948_I2C_PERIPHX_CTRL_t ctrl; ctrl.LENG = len; ctrl.EN = enable; ctrl.REG_DIS = data_only; ctrl.GRP = grp; ctrl.BYTE_SW = swap; retval = ICM_20948_execute_w(pdev, periph_ctrl_reg, (uint8_t *)&ctrl, sizeof(ICM_20948_I2C_PERIPHX_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } // Higher Level ICM_20948_Status_e ICM_20948_get_agmt(ICM_20948_Device_t *pdev, ICM_20948_AGMT_t *pagmt) { if (pagmt == NULL) { return ICM_20948_Stat_ParamErr; } ICM_20948_Status_e retval = ICM_20948_Stat_Ok; const uint8_t numbytes = 14 + 9; //Read Accel, gyro, temp, and 9 bytes of mag uint8_t buff[numbytes]; // Get readings retval |= ICM_20948_set_bank(pdev, 0); retval |= ICM_20948_execute_r(pdev, (uint8_t)AGB0_REG_ACCEL_XOUT_H, buff, numbytes); pagmt->acc.axes.x = ((buff[0] << 8) | (buff[1] & 0xFF)); pagmt->acc.axes.y = ((buff[2] << 8) | (buff[3] & 0xFF)); pagmt->acc.axes.z = ((buff[4] << 8) | (buff[5] & 0xFF)); pagmt->gyr.axes.x = ((buff[6] << 8) | (buff[7] & 0xFF)); pagmt->gyr.axes.y = ((buff[8] << 8) | (buff[9] & 0xFF)); pagmt->gyr.axes.z = ((buff[10] << 8) | (buff[11] & 0xFF)); pagmt->tmp.val = ((buff[12] << 8) | (buff[13] & 0xFF)); pagmt->magStat1 = buff[14]; pagmt->mag.axes.x = ((buff[16] << 8) | (buff[15] & 0xFF)); //Mag data is read little endian pagmt->mag.axes.y = ((buff[18] << 8) | (buff[17] & 0xFF)); pagmt->mag.axes.z = ((buff[20] << 8) | (buff[19] & 0xFF)); pagmt->magStat2 = buff[22]; // Get settings to be able to compute scaled values retval |= ICM_20948_set_bank(pdev, 2); ICM_20948_ACCEL_CONFIG_t acfg; retval |= ICM_20948_execute_r(pdev, (uint8_t)AGB2_REG_ACCEL_CONFIG, (uint8_t *)&acfg, 1 * sizeof(acfg)); pagmt->fss.a = acfg.ACCEL_FS_SEL; // Worth noting that without explicitly setting the FS range of the accelerometer it was showing the register value for +/- 2g but the reported values were actually scaled to the +/- 16g range // Wait a minute... now it seems like this problem actually comes from the digital low-pass filter. When enabled the value is 1/8 what it should be... retval |= ICM_20948_set_bank(pdev, 2); ICM_20948_GYRO_CONFIG_1_t gcfg1; retval |= ICM_20948_execute_r(pdev, (uint8_t)AGB2_REG_GYRO_CONFIG_1, (uint8_t *)&gcfg1, 1 * sizeof(gcfg1)); pagmt->fss.g = gcfg1.GYRO_FS_SEL; ICM_20948_ACCEL_CONFIG_2_t acfg2; retval |= ICM_20948_execute_r(pdev, (uint8_t)AGB2_REG_ACCEL_CONFIG_2, (uint8_t *)&acfg2, 1 * sizeof(acfg2)); return retval; } // FIFO ICM_20948_Status_e ICM_20948_enable_FIFO(ICM_20948_Device_t *pdev, bool enable) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_USER_CTRL_t ctrl; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_USER_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } if (enable) ctrl.FIFO_EN = 1; else ctrl.FIFO_EN = 0; retval = ICM_20948_execute_w(pdev, AGB0_REG_USER_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_reset_FIFO(ICM_20948_Device_t *pdev) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_FIFO_RST_t ctrl; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_FIFO_RST, (uint8_t *)&ctrl, sizeof(ICM_20948_FIFO_RST_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } ctrl.FIFO_RESET = 0x1F; // Datasheet says "FIFO_RESET[4:0]" retval = ICM_20948_execute_w(pdev, AGB0_REG_FIFO_RST, (uint8_t *)&ctrl, sizeof(ICM_20948_FIFO_RST_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } //delay ??? ctrl.FIFO_RESET = 0x1E; // The InvenSense Nucleo examples write 0x1F followed by 0x1E retval = ICM_20948_execute_w(pdev, AGB0_REG_FIFO_RST, (uint8_t *)&ctrl, sizeof(ICM_20948_FIFO_RST_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_set_FIFO_mode(ICM_20948_Device_t *pdev, bool snapshot) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_FIFO_MODE_t ctrl; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_FIFO_MODE, (uint8_t *)&ctrl, sizeof(ICM_20948_FIFO_MODE_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } if (snapshot) ctrl.FIFO_MODE = 0x1F; // Datasheet says "FIFO_MODE[4:0]" else ctrl.FIFO_MODE = 0; retval = ICM_20948_execute_w(pdev, AGB0_REG_FIFO_MODE, (uint8_t *)&ctrl, sizeof(ICM_20948_FIFO_MODE_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_get_FIFO_count(ICM_20948_Device_t *pdev, uint16_t *count) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_FIFO_COUNTH_t ctrlh; ICM_20948_FIFO_COUNTL_t ctrll; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_FIFO_COUNT_H, (uint8_t *)&ctrlh, sizeof(ICM_20948_FIFO_COUNTH_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } ctrlh.FIFO_COUNTH &= 0x1F; // Datasheet says "FIFO_CNT[12:8]" retval = ICM_20948_execute_r(pdev, AGB0_REG_FIFO_COUNT_L, (uint8_t *)&ctrll, sizeof(ICM_20948_FIFO_COUNTL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } *count = (((uint16_t)ctrlh.FIFO_COUNTH) << 8) | (uint16_t)ctrll.FIFO_COUNTL; return retval; } ICM_20948_Status_e ICM_20948_read_FIFO(ICM_20948_Device_t *pdev, uint8_t *data, uint8_t len) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_FIFO_R_W, data, len); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } // DMP ICM_20948_Status_e ICM_20948_enable_DMP(ICM_20948_Device_t *pdev, bool enable) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_USER_CTRL_t ctrl; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_USER_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } if (enable) ctrl.DMP_EN = 1; else ctrl.DMP_EN = 0; retval = ICM_20948_execute_w(pdev, AGB0_REG_USER_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_reset_DMP(ICM_20948_Device_t *pdev) { ICM_20948_Status_e retval = ICM_20948_Stat_Ok; ICM_20948_USER_CTRL_t ctrl; retval = ICM_20948_set_bank(pdev, 0); if (retval != ICM_20948_Stat_Ok) { return retval; } retval = ICM_20948_execute_r(pdev, AGB0_REG_USER_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } ctrl.DMP_RST = 1; retval = ICM_20948_execute_w(pdev, AGB0_REG_USER_CTRL, (uint8_t *)&ctrl, sizeof(ICM_20948_USER_CTRL_t)); if (retval != ICM_20948_Stat_Ok) { return retval; } return retval; } ICM_20948_Status_e ICM_20948_firmware_load(ICM_20948_Device_t *pdev) { #if defined(ICM_20948_USE_DMP) return (inv_icm20948_firmware_load(pdev, dmp3_image, sizeof(dmp3_image), DMP_LOAD_START)); #else return ICM_20948_Stat_DMPNotSupported; #endif } /** @brief Loads the DMP firmware from SRAM * @param[in] data pointer where the image * @param[in] size size if the image * @param[in] load_addr address to loading the image * @return 0 in case of success, -1 for any error */ ICM_20948_Status_e inv_icm20948_firmware_load(ICM_20948_Device_t *pdev, const unsigned char *data_start, unsigned short size_start, unsigned short load_addr) { int write_size; ICM_20948_Status_e result = ICM_20948_Stat_Ok; unsigned short memaddr; const unsigned char *data; unsigned short size; unsigned char data_cmp[INV_MAX_SERIAL_READ]; int flag = 0; if (pdev->_dmp_firmware_available == false) return ICM_20948_Stat_DMPNotSupported; if (pdev->_firmware_loaded) return ICM_20948_Stat_Ok; // Bail with no error if firmware is already loaded result = ICM_20948_sleep(pdev, false); // Make sure chip is awake if (result != ICM_20948_Stat_Ok) { return result; } result = ICM_20948_low_power(pdev, false); // Make sure chip is not in low power state if (result != ICM_20948_Stat_Ok) { return result; } // Write DMP memory data = data_start; size = size_start; memaddr = load_addr; #ifdef ICM_20948_USE_PROGMEM_FOR_DMP unsigned char data_not_pg[INV_MAX_SERIAL_READ]; // Suggested by @HyperKokichi in Issue #63 #endif while (size > 0) { //write_size = min(size, INV_MAX_SERIAL_WRITE); // Write in chunks of INV_MAX_SERIAL_WRITE if (size <= INV_MAX_SERIAL_WRITE) // Write in chunks of INV_MAX_SERIAL_WRITE write_size = size; else write_size = INV_MAX_SERIAL_WRITE; if ((memaddr & 0xff) + write_size > 0x100) { // Moved across a bank write_size = (memaddr & 0xff) + write_size - 0x100; } #ifdef ICM_20948_USE_PROGMEM_FOR_DMP memcpy_P(data_not_pg, data, write_size); // Suggested by @HyperKokichi in Issue #63 result = inv_icm20948_write_mems(pdev, memaddr, write_size, (unsigned char *)data_not_pg); #else result = inv_icm20948_write_mems(pdev, memaddr, write_size, (unsigned char *)data); #endif if (result != ICM_20948_Stat_Ok) return result; data += write_size; size -= write_size; memaddr += write_size; } // Verify DMP memory data = data_start; size = size_start; memaddr = load_addr; while (size > 0) { //write_size = min(size, INV_MAX_SERIAL_READ); // Read in chunks of INV_MAX_SERIAL_READ if (size <= INV_MAX_SERIAL_READ) // Read in chunks of INV_MAX_SERIAL_READ write_size = size; else write_size = INV_MAX_SERIAL_READ; if ((memaddr & 0xff) + write_size > 0x100) { // Moved across a bank write_size = (memaddr & 0xff) + write_size - 0x100; } result = inv_icm20948_read_mems(pdev, memaddr, write_size, data_cmp); if (result != ICM_20948_Stat_Ok) flag++; // Error, DMP not written correctly #ifdef ICM_20948_USE_PROGMEM_FOR_DMP memcpy_P(data_not_pg, data, write_size); // Suggested by @HyperKokichi in Issue #63 if (memcmp(data_cmp, data_not_pg, write_size)) #else if (memcmp(data_cmp, data, write_size)) // Compare the data #endif return ICM_20948_Stat_DMPVerifyFail; data += write_size; size -= write_size; memaddr += write_size; } //Enable LP_EN since we disabled it at begining of this function. result = ICM_20948_low_power(pdev, true); // Put chip into low power state if (result != ICM_20948_Stat_Ok) return result; if (!flag) { //Serial.println("DMP Firmware was updated successfully.."); pdev->_firmware_loaded = true; } return result; } ICM_20948_Status_e ICM_20948_set_dmp_start_address(ICM_20948_Device_t *pdev, unsigned short address) { ICM_20948_Status_e result = ICM_20948_Stat_Ok; if (pdev->_dmp_firmware_available == false) return ICM_20948_Stat_DMPNotSupported; unsigned char start_address[2]; start_address[0] = (unsigned char)(address >> 8); start_address[1] = (unsigned char)(address & 0xff); // result = ICM_20948_sleep(pdev, false); // Make sure chip is awake // if (result != ICM_20948_Stat_Ok) // { // return result; // } // // result = ICM_20948_low_power(pdev, false); // Make sure chip is not in low power state // if (result != ICM_20948_Stat_Ok) // { // return result; // } result = ICM_20948_set_bank(pdev, 2); // Set bank 2 if (result != ICM_20948_Stat_Ok) { return result; } // Write the sensor control bits into memory address AGB2_REG_PRGM_START_ADDRH result = ICM_20948_execute_w(pdev, AGB2_REG_PRGM_START_ADDRH, (uint8_t *)start_address, 2); return result; } /** * @brief Write data to a register in DMP memory * @param[in] DMP memory address * @param[in] number of byte to be written * @param[out] output data from the register * @return 0 if successful. */ ICM_20948_Status_e inv_icm20948_write_mems(ICM_20948_Device_t *pdev, unsigned short reg, unsigned int length, const unsigned char *data) { ICM_20948_Status_e result = ICM_20948_Stat_Ok; unsigned int bytesWritten = 0; unsigned int thisLen; unsigned char lBankSelected; unsigned char lStartAddrSelected; if (!data) { return ICM_20948_Stat_NoData; } result = ICM_20948_set_bank(pdev, 0); // Set bank 0 if (result != ICM_20948_Stat_Ok) { return result; } lBankSelected = (reg >> 8); if (lBankSelected != pdev->_last_mems_bank) { pdev->_last_mems_bank = lBankSelected; result = ICM_20948_execute_w(pdev, AGB0_REG_MEM_BANK_SEL, &lBankSelected, 1); if (result != ICM_20948_Stat_Ok) { return result; } } while (bytesWritten < length) { lStartAddrSelected = (reg & 0xff); /* Sets the starting read or write address for the selected memory, inside of the selected page (see MEM_SEL Register). Contents are changed after read or write of the selected memory. This register must be written prior to each access to initialize the register to the proper starting address. The address will auto increment during burst transactions. Two consecutive bursts without re-initializing the start address would skip one address. */ result = ICM_20948_execute_w(pdev, AGB0_REG_MEM_START_ADDR, &lStartAddrSelected, 1); if (result != ICM_20948_Stat_Ok) { return result; } if (length - bytesWritten <= INV_MAX_SERIAL_WRITE) thisLen = length - bytesWritten; else thisLen = INV_MAX_SERIAL_WRITE; /* Write data */ result = ICM_20948_execute_w(pdev, AGB0_REG_MEM_R_W, (uint8_t *)&data[bytesWritten], thisLen); if (result != ICM_20948_Stat_Ok) { return result; } bytesWritten += thisLen; reg += thisLen; } return result; } /** * @brief Read data from a register in DMP memory * @param[in] DMP memory address * @param[in] number of byte to be read * @param[in] input data from the register * @return 0 if successful. */ ICM_20948_Status_e inv_icm20948_read_mems(ICM_20948_Device_t *pdev, unsigned short reg, unsigned int length, unsigned char *data) { ICM_20948_Status_e result = ICM_20948_Stat_Ok; unsigned int bytesRead = 0; unsigned int thisLen; unsigned char lBankSelected; unsigned char lStartAddrSelected; if (!data) { return ICM_20948_Stat_NoData; } result = ICM_20948_set_bank(pdev, 0); // Set bank 0 if (result != ICM_20948_Stat_Ok) { return result; } lBankSelected = (reg >> 8); if (lBankSelected != pdev->_last_mems_bank) { pdev->_last_mems_bank = lBankSelected; result = ICM_20948_execute_w(pdev, AGB0_REG_MEM_BANK_SEL, &lBankSelected, 1); if (result != ICM_20948_Stat_Ok) { return result; } } while (bytesRead < length) { lStartAddrSelected = (reg & 0xff); /* Sets the starting read or write address for the selected memory, inside of the selected page (see MEM_SEL Register). Contents are changed after read or write of the selected memory. This register must be written prior to each access to initialize the register to the proper starting address. The address will auto increment during burst transactions. Two consecutive bursts without re-initializing the start address would skip one address. */ result = ICM_20948_execute_w(pdev, AGB0_REG_MEM_START_ADDR, &lStartAddrSelected, 1); if (result != ICM_20948_Stat_Ok) { return result; } if (length - bytesRead <= INV_MAX_SERIAL_READ) thisLen = length - bytesRead; else thisLen = INV_MAX_SERIAL_READ; /* Read data */ result = ICM_20948_execute_r(pdev, AGB0_REG_MEM_R_W, &data[bytesRead], thisLen); if (result != ICM_20948_Stat_Ok) { return result; } bytesRead += thisLen; reg += thisLen; } return result; } ICM_20948_Status_e inv_icm20948_set_dmp_sensor_period(ICM_20948_Device_t *pdev, enum DMP_ODR_Registers odr_reg, uint16_t interval) { // Set the ODR registers and clear the ODR counter // In order to set an ODR for a given sensor data, write 2-byte value to DMP using key defined above for a particular sensor. // Setting value can be calculated as follows: // Value = (DMP running rate (225Hz) / ODR ) - 1 // E.g. For a 25Hz ODR rate, value= (225/25) -1 = 8. // During run-time, if an ODR is changed, the corresponding rate counter must be reset. // To reset, write 2-byte {0,0} to DMP using keys below for a particular sensor: ICM_20948_Status_e result = ICM_20948_Stat_Ok; ICM_20948_Status_e result2 = ICM_20948_Stat_Ok; if (pdev->_dmp_firmware_available == false) return ICM_20948_Stat_DMPNotSupported; unsigned char odr_reg_val[2]; odr_reg_val[0] = (unsigned char)(interval >> 8); odr_reg_val[1] = (unsigned char)(interval & 0xff); unsigned char odr_count_zero[2] = {0x00, 0x00}; result = ICM_20948_sleep(pdev, false); // Make sure chip is awake if (result != ICM_20948_Stat_Ok) { return result; } result = ICM_20948_low_power(pdev, false); // Make sure chip is not in low power state if (result != ICM_20948_Stat_Ok) { return result; } switch (odr_reg) { case DMP_ODR_Reg_Cpass_Calibr: { result = inv_icm20948_write_mems(pdev, ODR_CPASS_CALIBR, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_CPASS_CALIBR, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_Gyro_Calibr: { result = inv_icm20948_write_mems(pdev, ODR_GYRO_CALIBR, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_GYRO_CALIBR, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_Pressure: { result = inv_icm20948_write_mems(pdev, ODR_PRESSURE, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_PRESSURE, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_Geomag: { result = inv_icm20948_write_mems(pdev, ODR_GEOMAG, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_GEOMAG, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_PQuat6: { result = inv_icm20948_write_mems(pdev, ODR_PQUAT6, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_PQUAT6, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_Quat9: { result = inv_icm20948_write_mems(pdev, ODR_QUAT9, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_QUAT9, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_Quat6: { result = inv_icm20948_write_mems(pdev, ODR_QUAT6, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_QUAT6, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_ALS: { result = inv_icm20948_write_mems(pdev, ODR_ALS, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_ALS, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_Cpass: { result = inv_icm20948_write_mems(pdev, ODR_CPASS, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_CPASS, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_Gyro: { result = inv_icm20948_write_mems(pdev, ODR_GYRO, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_GYRO, 2, (const unsigned char *)&odr_count_zero); } break; case DMP_ODR_Reg_Accel: { result = inv_icm20948_write_mems(pdev, ODR_ACCEL, 2, (const unsigned char *)&odr_reg_val); result2 = inv_icm20948_write_mems(pdev, ODR_CNTR_ACCEL, 2, (const unsigned char *)&odr_count_zero); } break; default: result = ICM_20948_Stat_InvalDMPRegister; break; } result = ICM_20948_low_power(pdev, true); // Put chip into low power state if (result != ICM_20948_Stat_Ok) return result; if (result2 > result) result = result2; // Return the highest error return result; } ICM_20948_Status_e inv_icm20948_enable_dmp_sensor(ICM_20948_Device_t *pdev, enum inv_icm20948_sensor sensor, int state) { ICM_20948_Status_e result = ICM_20948_Stat_Ok; uint16_t inv_event_control = 0; // Use this to store the value for MOTION_EVENT_CTL uint16_t data_rdy_status = 0; // Use this to store the value for DATA_RDY_STATUS if (pdev->_dmp_firmware_available == false) return ICM_20948_Stat_DMPNotSupported; // Bail if DMP is not supported uint8_t androidSensor = sensor_type_2_android_sensor(sensor); // Convert sensor from enum inv_icm20948_sensor to Android numbering if (androidSensor >= ANDROID_SENSOR_NUM_MAX) return ICM_20948_Stat_SensorNotSupported; // Bail if the sensor is not supported (TO DO: Support B2S etc) // Convert the Android sensor into a bit mask for DATA_OUT_CTL1 uint16_t delta = inv_androidSensor_to_control_bits[androidSensor]; if (delta == 0xFFFF) return ICM_20948_Stat_SensorNotSupported; // Bail if the sensor is not supported // Convert the Android sensor number into a bitmask and set or clear that bit in _enabled_Android_0 / _enabled_Android_1 unsigned long androidSensorAsBitMask; if (androidSensor < 32) // Sensors 0-31 { androidSensorAsBitMask = 1L << androidSensor; if (state == 0) // Should we disable the sensor? { pdev->_enabled_Android_0 &= ~androidSensorAsBitMask; // Clear the bit to disable the sensor } else { pdev->_enabled_Android_0 |= androidSensorAsBitMask; // Set the bit to enable the sensor } } else // Sensors 32- { androidSensorAsBitMask = 1L << (androidSensor - 32); if (state == 0) // Should we disable the sensor? { pdev->_enabled_Android_1 &= ~androidSensorAsBitMask; // Clear the bit to disable the sensor } else { pdev->_enabled_Android_1 |= androidSensorAsBitMask; // Set the bit to enable the sensor } } // Now we know androidSensor is valid, reconstruct the value for DATA_OUT_CTL1 from _enabled_Android_0 and _enabled_Android_0 delta = 0; // Clear delta for (int i = 0; i < 32; i++) { androidSensorAsBitMask = 1L << i; if ((pdev->_enabled_Android_0 & androidSensorAsBitMask) > 0) // Check if the Android sensor (0-31) is enabled { delta |= inv_androidSensor_to_control_bits[i]; // If it is, or the required bits into delta } if ((pdev->_enabled_Android_1 & androidSensorAsBitMask) > 0) // Check if the Android sensor (32-) is enabled { delta |= inv_androidSensor_to_control_bits[i + 32]; // If it is, or the required bits into delta } // Also check which bits need to be set in the Data Ready Status and Motion Event Control registers // Compare to INV_NEEDS_ACCEL_MASK, INV_NEEDS_GYRO_MASK and INV_NEEDS_COMPASS_MASK if (((androidSensorAsBitMask & INV_NEEDS_ACCEL_MASK) > 0) || ((androidSensorAsBitMask & INV_NEEDS_ACCEL_MASK1) > 0)) { data_rdy_status |= DMP_Data_ready_Accel; inv_event_control |= DMP_Motion_Event_Control_Accel_Calibr; } if (((androidSensorAsBitMask & INV_NEEDS_GYRO_MASK) > 0) || ((androidSensorAsBitMask & INV_NEEDS_GYRO_MASK1) > 0)) { data_rdy_status |= DMP_Data_ready_Gyro; inv_event_control |= DMP_Motion_Event_Control_Gyro_Calibr; } if (((androidSensorAsBitMask & INV_NEEDS_COMPASS_MASK) > 0) || ((androidSensorAsBitMask & INV_NEEDS_COMPASS_MASK1) > 0)) { data_rdy_status |= DMP_Data_ready_Secondary_Compass; inv_event_control |= DMP_Motion_Event_Control_Compass_Calibr; } } result = ICM_20948_sleep(pdev, false); // Make sure chip is awake if (result != ICM_20948_Stat_Ok) { return result; } result = ICM_20948_low_power(pdev, false); // Make sure chip is not in low power state if (result != ICM_20948_Stat_Ok) { return result; } // Check if Accel, Gyro/Gyro_Calibr or Compass_Calibr/Quat9/GeoMag/Compass are to be enabled. If they are then we need to request the accuracy data via header2. uint16_t delta2 = 0; if ((delta & DMP_Data_Output_Control_1_Accel) > 0) { delta2 |= DMP_Data_Output_Control_2_Accel_Accuracy; } if (((delta & DMP_Data_Output_Control_1_Gyro_Calibr) > 0) || ((delta & DMP_Data_Output_Control_1_Gyro) > 0)) { delta2 |= DMP_Data_Output_Control_2_Gyro_Accuracy; } if (((delta & DMP_Data_Output_Control_1_Compass_Calibr) > 0) || ((delta & DMP_Data_Output_Control_1_Compass) > 0) || ((delta & DMP_Data_Output_Control_1_Quat9) > 0) || ((delta & DMP_Data_Output_Control_1_Geomag) > 0)) { delta2 |= DMP_Data_Output_Control_2_Compass_Accuracy; } // TO DO: Add DMP_Data_Output_Control_2_Pickup etc. if required // Write the sensor control bits into memory address DATA_OUT_CTL1 unsigned char data_output_control_reg[2]; data_output_control_reg[0] = (unsigned char)(delta >> 8); data_output_control_reg[1] = (unsigned char)(delta & 0xff); pdev->_dataOutCtl1 = delta; // Diagnostics result = inv_icm20948_write_mems(pdev, DATA_OUT_CTL1, 2, (const unsigned char *)&data_output_control_reg); if (result != ICM_20948_Stat_Ok) { return result; } // Write the 'header2' sensor control bits into memory address DATA_OUT_CTL2 data_output_control_reg[0] = (unsigned char)(delta2 >> 8); data_output_control_reg[1] = (unsigned char)(delta2 & 0xff); pdev->_dataOutCtl2 = delta2; // Diagnostics result = inv_icm20948_write_mems(pdev, DATA_OUT_CTL2, 2, (const unsigned char *)&data_output_control_reg); if (result != ICM_20948_Stat_Ok) { return result; } // Set the DATA_RDY_STATUS register data_output_control_reg[0] = (unsigned char)(data_rdy_status >> 8); data_output_control_reg[1] = (unsigned char)(data_rdy_status & 0xff); pdev->_dataRdyStatus = data_rdy_status; // Diagnostics result = inv_icm20948_write_mems(pdev, DATA_RDY_STATUS, 2, (const unsigned char *)&data_output_control_reg); if (result != ICM_20948_Stat_Ok) { return result; } // Check which extra bits need to be set in the Motion Event Control register if ((delta & DMP_Data_Output_Control_1_Quat9) > 0) { inv_event_control |= DMP_Motion_Event_Control_9axis; } if (((delta & DMP_Data_Output_Control_1_Step_Detector) > 0) || ((delta & DMP_Data_Output_Control_1_Step_Ind_0) > 0) || ((delta & DMP_Data_Output_Control_1_Step_Ind_1) > 0) || ((delta & DMP_Data_Output_Control_1_Step_Ind_2) > 0)) { inv_event_control |= DMP_Motion_Event_Control_Pedometer_Interrupt; } if ((delta & DMP_Data_Output_Control_1_Geomag) > 0) { inv_event_control |= DMP_Motion_Event_Control_Geomag; } // Set the MOTION_EVENT_CTL register data_output_control_reg[0] = (unsigned char)(inv_event_control >> 8); data_output_control_reg[1] = (unsigned char)(inv_event_control & 0xff); pdev->_motionEventCtl = inv_event_control; // Diagnostics result = inv_icm20948_write_mems(pdev, MOTION_EVENT_CTL, 2, (const unsigned char *)&data_output_control_reg); if (result != ICM_20948_Stat_Ok) { return result; } result = ICM_20948_low_power(pdev, true); // Put chip into low power state if (result != ICM_20948_Stat_Ok) return result; return result; } ICM_20948_Status_e inv_icm20948_enable_dmp_sensor_int(ICM_20948_Device_t *pdev, enum inv_icm20948_sensor sensor, int state) { ICM_20948_Status_e result = ICM_20948_Stat_Ok; if (pdev->_dmp_firmware_available == false) return ICM_20948_Stat_DMPNotSupported; // Bail if DMP is not supported uint8_t androidSensor = sensor_type_2_android_sensor(sensor); // Convert sensor from enum inv_icm20948_sensor to Android numbering if (androidSensor > ANDROID_SENSOR_NUM_MAX) return ICM_20948_Stat_SensorNotSupported; // Bail if the sensor is not supported // Convert the Android sensor into a bit mask for DATA_OUT_CTL1 uint16_t delta = inv_androidSensor_to_control_bits[androidSensor]; if (delta == 0xFFFF) return ICM_20948_Stat_SensorNotSupported; // Bail if the sensor is not supported // Convert the Android sensor number into a bitmask and set or clear that bit in _enabled_Android_intr_0 / _enabled_Android_intr_1 unsigned long androidSensorAsBitMask; if (androidSensor < 32) // Sensors 0-31 { androidSensorAsBitMask = 1L << androidSensor; if (state == 0) // Should we disable the sensor interrupt? { pdev->_enabled_Android_intr_0 &= ~androidSensorAsBitMask; // Clear the bit to disable the sensor interrupt } else { pdev->_enabled_Android_intr_0 |= androidSensorAsBitMask; // Set the bit to enable the sensor interrupt } } else // Sensors 32- { androidSensorAsBitMask = 1L << (androidSensor - 32); if (state == 0) // Should we disable the sensor? { pdev->_enabled_Android_intr_1 &= ~androidSensorAsBitMask; // Clear the bit to disable the sensor interrupt } else { pdev->_enabled_Android_intr_1 |= androidSensorAsBitMask; // Set the bit to enable the sensor interrupt } } // Now we know androidSensor is valid, reconstruct the value for DATA_INTR_CTL from _enabled_Android_intr_0 and _enabled_Android_intr_0 delta = 0; // Clear delta for (int i = 0; i < 32; i++) { androidSensorAsBitMask = 1L << i; if ((pdev->_enabled_Android_intr_0 & androidSensorAsBitMask) > 0) // Check if the Android sensor (0-31) interrupt is enabled { delta |= inv_androidSensor_to_control_bits[i]; // If it is, or the required bits into delta } if ((pdev->_enabled_Android_intr_1 & androidSensorAsBitMask) > 0) // Check if the Android sensor (32-) interrupt is enabled { delta |= inv_androidSensor_to_control_bits[i + 32]; // If it is, or the required bits into delta } } result = ICM_20948_sleep(pdev, false); // Make sure chip is awake if (result != ICM_20948_Stat_Ok) { return result; } result = ICM_20948_low_power(pdev, false); // Make sure chip is not in low power state if (result != ICM_20948_Stat_Ok) { return result; } unsigned char data_intr_ctl[2]; data_intr_ctl[0] = (unsigned char)(delta >> 8); data_intr_ctl[1] = (unsigned char)(delta & 0xff); pdev->_dataIntrCtl = delta; // Diagnostics // Write the interrupt control bits into memory address DATA_INTR_CTL result = inv_icm20948_write_mems(pdev, DATA_INTR_CTL, 2, (const unsigned char *)&data_intr_ctl); result = ICM_20948_low_power(pdev, true); // Put chip into low power state if (result != ICM_20948_Stat_Ok) return result; return result; } ICM_20948_Status_e inv_icm20948_read_dmp_data(ICM_20948_Device_t *pdev, icm_20948_DMP_data_t *data) { ICM_20948_Status_e result = ICM_20948_Stat_Ok; uint8_t fifoBytes[icm_20948_DMP_Maximum_Bytes]; // Interim storage for the FIFO data if (pdev->_dmp_firmware_available == false) return ICM_20948_Stat_DMPNotSupported; // Check how much data is in the FIFO uint16_t fifo_count; result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; if (fifo_count < icm_20948_DMP_Header_Bytes) // Has a 2-byte header arrived? return ICM_20948_Stat_FIFONoDataAvail; // Bail if no header is available // Read the header (2 bytes) data->header = 0; // Clear the existing header uint16_t aShort = 0; result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Header_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Header_Bytes; i++) { aShort |= ((uint16_t)fifoBytes[i]) << (8 - (i * 8)); } data->header = aShort; // Store the header in data->header fifo_count -= icm_20948_DMP_Header_Bytes; // Decrement the count // If the header indicates a header2 is present then read that now data->header2 = 0; // Clear the existing header2 if ((data->header & DMP_header_bitmap_Header2) > 0) // If the header2 bit is set { if (fifo_count < icm_20948_DMP_Header2_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Header2_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if no header2 is available // Read the header (2 bytes) aShort = 0; result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Header2_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Header2_Bytes; i++) { aShort |= ((uint16_t)fifoBytes[i]) << (8 - (i * 8)); } data->header2 = aShort; // Store the header2 in data->header2 fifo_count -= icm_20948_DMP_Header2_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_Accel) > 0) // case DMP_header_bitmap_Accel: { if (fifo_count < icm_20948_DMP_Raw_Accel_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Raw_Accel_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Raw_Accel_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Raw_Accel_Bytes; i++) { data->Raw_Accel.Bytes[DMP_PQuat6_Byte_Ordering[i]] = fifoBytes[i]; // Correct the byte order (map big endian to little endian) } fifo_count -= icm_20948_DMP_Raw_Accel_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_Gyro) > 0) // case DMP_header_bitmap_Gyro: { if (fifo_count < (icm_20948_DMP_Raw_Gyro_Bytes + icm_20948_DMP_Gyro_Bias_Bytes)) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < (icm_20948_DMP_Raw_Gyro_Bytes + icm_20948_DMP_Gyro_Bias_Bytes)) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], (icm_20948_DMP_Raw_Gyro_Bytes + icm_20948_DMP_Gyro_Bias_Bytes)); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < (icm_20948_DMP_Raw_Gyro_Bytes + icm_20948_DMP_Gyro_Bias_Bytes); i++) { data->Raw_Gyro.Bytes[DMP_Raw_Gyro_Byte_Ordering[i]] = fifoBytes[i]; // Correct the byte order (map big endian to little endian) } fifo_count -= (icm_20948_DMP_Raw_Gyro_Bytes + icm_20948_DMP_Gyro_Bias_Bytes); // Decrement the count } if ((data->header & DMP_header_bitmap_Compass) > 0) // case DMP_header_bitmap_Compass: { if (fifo_count < icm_20948_DMP_Compass_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Compass_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Compass_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Compass_Bytes; i++) { data->Compass.Bytes[DMP_PQuat6_Byte_Ordering[i]] = fifoBytes[i]; // Correct the byte order (map big endian to little endian) } fifo_count -= icm_20948_DMP_Compass_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_ALS) > 0) // case DMP_header_bitmap_ALS: { if (fifo_count < icm_20948_DMP_ALS_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_ALS_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_ALS_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_ALS_Bytes; i++) { data->ALS[i] = fifoBytes[i]; } fifo_count -= icm_20948_DMP_ALS_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_Quat6) > 0) // case DMP_header_bitmap_Quat6: { if (fifo_count < icm_20948_DMP_Quat6_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Quat6_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Quat6_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Quat6_Bytes; i++) { data->Quat6.Bytes[DMP_Quat6_Byte_Ordering[i]] = fifoBytes[i]; // Correct the byte order (map big endian to little endian) } fifo_count -= icm_20948_DMP_Quat6_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_Quat9) > 0) // case DMP_header_bitmap_Quat9: { if (fifo_count < icm_20948_DMP_Quat9_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Quat9_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Quat9_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Quat9_Bytes; i++) { data->Quat9.Bytes[DMP_Quat9_Byte_Ordering[i]] = fifoBytes[i]; // Correct the byte order (map big endian to little endian) } fifo_count -= icm_20948_DMP_Quat9_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_PQuat6) > 0) // case DMP_header_bitmap_PQuat6: { if (fifo_count < icm_20948_DMP_PQuat6_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_PQuat6_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_PQuat6_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_PQuat6_Bytes; i++) { data->PQuat6.Bytes[DMP_PQuat6_Byte_Ordering[i]] = fifoBytes[i]; // Correct the byte order (map big endian to little endian) } fifo_count -= icm_20948_DMP_PQuat6_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_Geomag) > 0) // case DMP_header_bitmap_Geomag: { if (fifo_count < icm_20948_DMP_Geomag_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Geomag_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Geomag_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Geomag_Bytes; i++) { data->Geomag.Bytes[DMP_Quat9_Byte_Ordering[i]] = fifoBytes[i]; // Correct the byte order (map big endian to little endian) } fifo_count -= icm_20948_DMP_Geomag_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_Pressure) > 0) // case DMP_header_bitmap_Pressure: { if (fifo_count < icm_20948_DMP_Pressure_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Pressure_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Pressure_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Pressure_Bytes; i++) { data->Pressure[i] = fifoBytes[i]; } fifo_count -= icm_20948_DMP_Pressure_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_Gyro_Calibr) > 0) // case DMP_header_bitmap_Gyro_Calibr: { // lcm20948MPUFifoControl.c suggests icm_20948_DMP_Gyro_Calibr_Bytes is not supported // and looking at DMP frames which have the Gyro_Calibr bit set, that certainly seems to be true. // So, we'll skip this...: /* if (fifo_count < icm_20948_DMP_Gyro_Calibr_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Gyro_Calibr_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Gyro_Calibr_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Gyro_Calibr_Bytes; i++) { data->Gyro_Calibr.Bytes[DMP_Quat6_Byte_Ordering[i]] = fifoBytes[i]; // Correct the byte order (map big endian to little endian) } fifo_count -= icm_20948_DMP_Gyro_Calibr_Bytes; // Decrement the count */ } if ((data->header & DMP_header_bitmap_Compass_Calibr) > 0) // case DMP_header_bitmap_Compass_Calibr: { if (fifo_count < icm_20948_DMP_Compass_Calibr_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Compass_Calibr_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Compass_Calibr_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Compass_Calibr_Bytes; i++) { data->Compass_Calibr.Bytes[DMP_Quat6_Byte_Ordering[i]] = fifoBytes[i]; // Correct the byte order (map big endian to little endian) } fifo_count -= icm_20948_DMP_Compass_Calibr_Bytes; // Decrement the count } if ((data->header & DMP_header_bitmap_Step_Detector) > 0) // case DMP_header_bitmap_Step_Detector: { if (fifo_count < icm_20948_DMP_Step_Detector_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Step_Detector_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Step_Detector_Bytes); if (result != ICM_20948_Stat_Ok) return result; uint32_t aWord = 0; for (int i = 0; i < icm_20948_DMP_Step_Detector_Bytes; i++) { aWord |= ((uint32_t)fifoBytes[i]) << (24 - (i * 8)); } data->Pedometer_Timestamp = aWord; fifo_count -= icm_20948_DMP_Step_Detector_Bytes; // Decrement the count } // Now check for header2 features if ((data->header2 & DMP_header2_bitmap_Accel_Accuracy) > 0) // case DMP_header2_bitmap_Accel_Accuracy: { if (fifo_count < icm_20948_DMP_Accel_Accuracy_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Accel_Accuracy_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available aShort = 0; result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Accel_Accuracy_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Accel_Accuracy_Bytes; i++) { aShort |= ((uint16_t)fifoBytes[i]) << (8 - (i * 8)); } data->Accel_Accuracy = aShort; fifo_count -= icm_20948_DMP_Accel_Accuracy_Bytes; // Decrement the count } if ((data->header2 & DMP_header2_bitmap_Gyro_Accuracy) > 0) // case DMP_header2_bitmap_Gyro_Accuracy: { if (fifo_count < icm_20948_DMP_Gyro_Accuracy_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Gyro_Accuracy_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available aShort = 0; result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Gyro_Accuracy_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Gyro_Accuracy_Bytes; i++) { aShort |= ((uint16_t)fifoBytes[i]) << (8 - (i * 8)); } data->Gyro_Accuracy = aShort; fifo_count -= icm_20948_DMP_Gyro_Accuracy_Bytes; // Decrement the count } if ((data->header2 & DMP_header2_bitmap_Compass_Accuracy) > 0) // case DMP_header2_bitmap_Compass_Accuracy: { if (fifo_count < icm_20948_DMP_Compass_Accuracy_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Compass_Accuracy_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available aShort = 0; result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Compass_Accuracy_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Compass_Accuracy_Bytes; i++) { aShort |= ((uint16_t)fifoBytes[i]) << (8 - (i * 8)); } data->Compass_Accuracy = aShort; fifo_count -= icm_20948_DMP_Compass_Accuracy_Bytes; // Decrement the count } if ((data->header2 & DMP_header2_bitmap_Fsync) > 0) // case DMP_header2_bitmap_Fsync: { // lcm20948MPUFifoControl.c suggests icm_20948_DMP_Fsync_Detection_Bytes is not supported. // So, we'll skip this just in case...: /* if (fifo_count < icm_20948_DMP_Fsync_Detection_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Fsync_Detection_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available aShort = 0; result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Fsync_Detection_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Fsync_Detection_Bytes; i++) { aShort |= ((uint16_t)fifoBytes[i]) << (8 - (i * 8)); } data->Fsync_Delay_Time = aShort; fifo_count -= icm_20948_DMP_Fsync_Detection_Bytes; // Decrement the count */ } if ((data->header2 & DMP_header2_bitmap_Pickup) > 0) // case DMP_header2_bitmap_Pickup: { if (fifo_count < icm_20948_DMP_Pickup_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Pickup_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available aShort = 0; result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Pickup_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Pickup_Bytes; i++) { aShort |= ((uint16_t)fifoBytes[i]) << (8 - (i * 8)); } data->Pickup = aShort; fifo_count -= icm_20948_DMP_Pickup_Bytes; // Decrement the count } if ((data->header2 & DMP_header2_bitmap_Activity_Recog) > 0) // case DMP_header2_bitmap_Activity_Recog: { if (fifo_count < icm_20948_DMP_Activity_Recognition_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Activity_Recognition_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Activity_Recognition_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Activity_Recognition_Bytes; i++) { data->Activity_Recognition.Bytes[DMP_Activity_Recognition_Byte_Ordering[i]] = fifoBytes[i]; } fifo_count -= icm_20948_DMP_Activity_Recognition_Bytes; // Decrement the count } if ((data->header2 & DMP_header2_bitmap_Secondary_On_Off) > 0) // case DMP_header2_bitmap_Secondary_On_Off: { if (fifo_count < icm_20948_DMP_Secondary_On_Off_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Secondary_On_Off_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Secondary_On_Off_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Secondary_On_Off_Bytes; i++) { data->Secondary_On_Off.Bytes[DMP_Secondary_On_Off_Byte_Ordering[i]] = fifoBytes[i]; } fifo_count -= icm_20948_DMP_Secondary_On_Off_Bytes; // Decrement the count } // Finally, extract the footer (gyro count) if (fifo_count < icm_20948_DMP_Footer_Bytes) // Check if we need to read the FIFO count again { result = ICM_20948_get_FIFO_count(pdev, &fifo_count); if (result != ICM_20948_Stat_Ok) return result; } if (fifo_count < icm_20948_DMP_Footer_Bytes) return ICM_20948_Stat_FIFOIncompleteData; // Bail if not enough data is available aShort = 0; result = ICM_20948_read_FIFO(pdev, &fifoBytes[0], icm_20948_DMP_Footer_Bytes); if (result != ICM_20948_Stat_Ok) return result; for (int i = 0; i < icm_20948_DMP_Footer_Bytes; i++) { aShort |= ((uint16_t)fifoBytes[i]) << (8 - (i * 8)); } data->Footer = aShort; fifo_count -= icm_20948_DMP_Footer_Bytes; // Decrement the count if (fifo_count > 0) // Check if there is still data waiting to be read return ICM_20948_Stat_FIFOMoreDataAvail; return result; } uint8_t sensor_type_2_android_sensor(enum inv_icm20948_sensor sensor) { switch (sensor) { case INV_ICM20948_SENSOR_ACCELEROMETER: return ANDROID_SENSOR_ACCELEROMETER; // 1 case INV_ICM20948_SENSOR_GYROSCOPE: return ANDROID_SENSOR_GYROSCOPE; // 4 case INV_ICM20948_SENSOR_RAW_ACCELEROMETER: return ANDROID_SENSOR_RAW_ACCELEROMETER; // 42 case INV_ICM20948_SENSOR_RAW_GYROSCOPE: return ANDROID_SENSOR_RAW_GYROSCOPE; // 43 case INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED: return ANDROID_SENSOR_MAGNETIC_FIELD_UNCALIBRATED; // 14 case INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED: return ANDROID_SENSOR_GYROSCOPE_UNCALIBRATED; // 16 case INV_ICM20948_SENSOR_ACTIVITY_CLASSIFICATON: return ANDROID_SENSOR_ACTIVITY_CLASSIFICATON; // 47 case INV_ICM20948_SENSOR_STEP_DETECTOR: return ANDROID_SENSOR_STEP_DETECTOR; // 18 case INV_ICM20948_SENSOR_STEP_COUNTER: return ANDROID_SENSOR_STEP_COUNTER; // 19 case INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR: return ANDROID_SENSOR_GAME_ROTATION_VECTOR; // 15 case INV_ICM20948_SENSOR_ROTATION_VECTOR: return ANDROID_SENSOR_ROTATION_VECTOR; // 11 case INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR: return ANDROID_SENSOR_GEOMAGNETIC_ROTATION_VECTOR; // 20 case INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD: return ANDROID_SENSOR_GEOMAGNETIC_FIELD; // 2 case INV_ICM20948_SENSOR_WAKEUP_SIGNIFICANT_MOTION: return ANDROID_SENSOR_WAKEUP_SIGNIFICANT_MOTION; // 17 case INV_ICM20948_SENSOR_FLIP_PICKUP: return ANDROID_SENSOR_FLIP_PICKUP; // 46 case INV_ICM20948_SENSOR_WAKEUP_TILT_DETECTOR: return ANDROID_SENSOR_WAKEUP_TILT_DETECTOR; // 41 case INV_ICM20948_SENSOR_GRAVITY: return ANDROID_SENSOR_GRAVITY; // 9 case INV_ICM20948_SENSOR_LINEAR_ACCELERATION: return ANDROID_SENSOR_LINEAR_ACCELERATION; // 10 case INV_ICM20948_SENSOR_ORIENTATION: return ANDROID_SENSOR_ORIENTATION; // 3 case INV_ICM20948_SENSOR_B2S: return ANDROID_SENSOR_B2S; // 45 default: return ANDROID_SENSOR_NUM_MAX; } } enum inv_icm20948_sensor inv_icm20948_sensor_android_2_sensor_type(int sensor) { switch (sensor) { case ANDROID_SENSOR_ACCELEROMETER: return INV_ICM20948_SENSOR_ACCELEROMETER; case ANDROID_SENSOR_GYROSCOPE: return INV_ICM20948_SENSOR_GYROSCOPE; case ANDROID_SENSOR_RAW_ACCELEROMETER: return INV_ICM20948_SENSOR_RAW_ACCELEROMETER; case ANDROID_SENSOR_RAW_GYROSCOPE: return INV_ICM20948_SENSOR_RAW_GYROSCOPE; case ANDROID_SENSOR_MAGNETIC_FIELD_UNCALIBRATED: return INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED; case ANDROID_SENSOR_GYROSCOPE_UNCALIBRATED: return INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED; case ANDROID_SENSOR_ACTIVITY_CLASSIFICATON: return INV_ICM20948_SENSOR_ACTIVITY_CLASSIFICATON; case ANDROID_SENSOR_STEP_DETECTOR: return INV_ICM20948_SENSOR_STEP_DETECTOR; case ANDROID_SENSOR_STEP_COUNTER: return INV_ICM20948_SENSOR_STEP_COUNTER; case ANDROID_SENSOR_GAME_ROTATION_VECTOR: return INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR; case ANDROID_SENSOR_ROTATION_VECTOR: return INV_ICM20948_SENSOR_ROTATION_VECTOR; case ANDROID_SENSOR_GEOMAGNETIC_ROTATION_VECTOR: return INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR; case ANDROID_SENSOR_GEOMAGNETIC_FIELD: return INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD; case ANDROID_SENSOR_WAKEUP_SIGNIFICANT_MOTION: return INV_ICM20948_SENSOR_WAKEUP_SIGNIFICANT_MOTION; case ANDROID_SENSOR_FLIP_PICKUP: return INV_ICM20948_SENSOR_FLIP_PICKUP; case ANDROID_SENSOR_WAKEUP_TILT_DETECTOR: return INV_ICM20948_SENSOR_WAKEUP_TILT_DETECTOR; case ANDROID_SENSOR_GRAVITY: return INV_ICM20948_SENSOR_GRAVITY; case ANDROID_SENSOR_LINEAR_ACCELERATION: return INV_ICM20948_SENSOR_LINEAR_ACCELERATION; case ANDROID_SENSOR_ORIENTATION: return INV_ICM20948_SENSOR_ORIENTATION; case ANDROID_SENSOR_B2S: return INV_ICM20948_SENSOR_B2S; default: return INV_ICM20948_SENSOR_MAX; } } ICM_20948_Status_e inv_icm20948_set_gyro_sf(ICM_20948_Device_t *pdev, unsigned char div, int gyro_level) { ICM_20948_Status_e result = ICM_20948_Stat_Ok; if (pdev->_dmp_firmware_available == false) return ICM_20948_Stat_DMPNotSupported; // gyro_level should be set to 4 regardless of fullscale, due to the addition of API dmp_icm20648_set_gyro_fsr() gyro_level = 4; // First read the TIMEBASE_CORRECTION_PLL register from Bank 1 int8_t pll; // Signed. Typical value is 0x18 result = ICM_20948_set_bank(pdev, 1); result = ICM_20948_execute_r(pdev, AGB1_REG_TIMEBASE_CORRECTION_PLL, (uint8_t *)&pll, 1); if (result != ICM_20948_Stat_Ok) { return result; } pdev->_gyroSFpll = pll; // Record the PLL value so we can debug print it // Now calculate the Gyro SF using code taken from the InvenSense example (inv_icm20948_set_gyro_sf) long gyro_sf; unsigned long long const MagicConstant = 264446880937391LL; unsigned long long const MagicConstantScale = 100000LL; unsigned long long ResultLL; if (pll & 0x80) { ResultLL = (MagicConstant * (long long)(1ULL << gyro_level) * (1 + div) / (1270 - (pll & 0x7F)) / MagicConstantScale); } else { ResultLL = (MagicConstant * (long long)(1ULL << gyro_level) * (1 + div) / (1270 + pll) / MagicConstantScale); } /* In above deprecated FP version, worst case arguments can produce a result that overflows a signed long. Here, for such cases, we emulate the FP behavior of setting the result to the maximum positive value, as the compiler's conversion of a u64 to an s32 is simple truncation of the u64's high half, sadly.... */ if (ResultLL > 0x7FFFFFFF) gyro_sf = 0x7FFFFFFF; else gyro_sf = (long)ResultLL; pdev->_gyroSF = gyro_sf; // Record value so we can debug print it // Finally, write the value to the DMP GYRO_SF register unsigned char gyro_sf_reg[4]; gyro_sf_reg[0] = (unsigned char)(gyro_sf >> 24); gyro_sf_reg[1] = (unsigned char)(gyro_sf >> 16); gyro_sf_reg[2] = (unsigned char)(gyro_sf >> 8); gyro_sf_reg[3] = (unsigned char)(gyro_sf & 0xff); result = inv_icm20948_write_mems(pdev, GYRO_SF, 4, (const unsigned char *)&gyro_sf_reg); return result; }