/* * ________________________________________________________________________________________________________ * Copyright © 2014-2015 InvenSense Inc. Portions Copyright © 2014-2015 Movea. All rights reserved. * This software, related documentation and any modifications thereto (collectively “Software”) is subject * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and * other intellectual property rights laws. * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software * and any use, reproduction, disclosure or distribution of the Software without an express license * agreement from InvenSense is strictly prohibited. * ________________________________________________________________________________________________________ */ #include "Icm20948.h" #include "Icm20948DataBaseDriver.h" #include "Icm20948Defs.h" #include "Icm20948DataBaseControl.h" #include "Icm20948AuxCompassAkm.h" #include "Icm20948AuxTransport.h" #include "Icm20948Dmp3Driver.h" static unsigned char inv_is_gyro_enabled(struct inv_icm20948 * s); void inv_icm20948_prevent_lpen_control(struct inv_icm20948 * s) { s->sAllowLpEn = 0; } void inv_icm20948_allow_lpen_control(struct inv_icm20948 * s) { s->sAllowLpEn = 1; inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1); } static uint8_t inv_icm20948_get_lpen_control(struct inv_icm20948 * s) { return s->sAllowLpEn; } /*! ****************************************************************************** * @brief This function sets the power state of the Ivory chip * loop * @param[in] Function - CHIP_AWAKE, CHIP_LP_ENABLE * @param[in] On/Off - The functions are enabled if previously disabled and disabled if previously enabled based on the value of On/Off. ****************************************************************************** */ int inv_icm20948_set_chip_power_state(struct inv_icm20948 * s, unsigned char func, unsigned char on_off) { int status = 0; switch(func) { case CHIP_AWAKE: if(on_off){ if((s->base_state.wake_state & CHIP_AWAKE) == 0) {// undo sleep_en s->base_state.pwr_mgmt_1 &= ~BIT_SLEEP; status = inv_icm20948_write_single_mems_reg_core(s, REG_PWR_MGMT_1, s->base_state.pwr_mgmt_1); s->base_state.wake_state |= CHIP_AWAKE; inv_icm20948_sleep_100us(1); // after writing the bit wait 100 Micro Seconds } } else { if(s->base_state.wake_state & CHIP_AWAKE) {// set sleep_en s->base_state.pwr_mgmt_1 |= BIT_SLEEP; status = inv_icm20948_write_single_mems_reg_core(s, REG_PWR_MGMT_1, s->base_state.pwr_mgmt_1); s->base_state.wake_state &= ~CHIP_AWAKE; inv_icm20948_sleep_100us(1); // after writing the bit wait 100 Micro Seconds } } break; case CHIP_LP_ENABLE: if(s->base_state.lp_en_support == 1) { if(on_off) { if( (inv_icm20948_get_lpen_control(s)) && ((s->base_state.wake_state & CHIP_LP_ENABLE) == 0)){ s->base_state.pwr_mgmt_1 |= BIT_LP_EN; // lp_en ON status = inv_icm20948_write_single_mems_reg_core(s, REG_PWR_MGMT_1, s->base_state.pwr_mgmt_1); s->base_state.wake_state |= CHIP_LP_ENABLE; } } else { if(s->base_state.wake_state & CHIP_LP_ENABLE){ s->base_state.pwr_mgmt_1 &= ~BIT_LP_EN; // lp_en off status = inv_icm20948_write_single_mems_reg_core(s, REG_PWR_MGMT_1, s->base_state.pwr_mgmt_1); s->base_state.wake_state &= ~CHIP_LP_ENABLE; inv_icm20948_sleep_100us(1); // after writing the bit wait 100 Micro Seconds } } } break; default: break; }// end switch return status; } /*! ****************************************************************************** * @return Current wake status of the Ivory chip. ****************************************************************************** */ uint8_t inv_icm20948_get_chip_power_state(struct inv_icm20948 * s) { return s->base_state.wake_state; } /** Wakes up DMP3 (SMARTSENSOR). */ int inv_icm20948_wakeup_mems(struct inv_icm20948 * s) { unsigned char data; int result = 0; result = inv_icm20948_set_chip_power_state(s, CHIP_AWAKE, 1); if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) { s->base_state.user_ctrl |= BIT_I2C_IF_DIS; inv_icm20948_write_single_mems_reg(s, REG_USER_CTRL, s->base_state.user_ctrl); } data = 0x47; // FIXME, should set up according to sensor/engines enabled. result |= inv_icm20948_write_mems_reg(s, REG_PWR_MGMT_2, 1, &data); if(s->base_state.firmware_loaded == 1) { s->base_state.user_ctrl |= BIT_DMP_EN | BIT_FIFO_EN; result |= inv_icm20948_write_single_mems_reg(s, REG_USER_CTRL, s->base_state.user_ctrl); } result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1); return result; } /** Puts DMP3 (SMARTSENSOR) into the lowest power state. Assumes sensors are all off. */ int inv_icm20948_sleep_mems(struct inv_icm20948 * s) { int result; unsigned char data; data = 0x7F; result = inv_icm20948_write_mems_reg(s, REG_PWR_MGMT_2, 1, &data); result |= inv_icm20948_set_chip_power_state(s, CHIP_AWAKE, 0); return result; } int inv_icm20948_set_dmp_address(struct inv_icm20948 * s) { int result; unsigned char dmp_cfg[2] = {0}; unsigned short config; // Write DMP Start address inv_icm20948_get_dmp_start_address(s, &config); /* setup DMP start address and firmware */ dmp_cfg[0] = (unsigned char)((config >> 8) & 0xff); dmp_cfg[1] = (unsigned char)(config & 0xff); result = inv_icm20948_write_mems_reg(s, REG_PRGM_START_ADDRH, 2, dmp_cfg); return result; } /** * @brief Set up the secondary I2C bus on 20630. * @param[in] MPU state varible * @return 0 if successful. */ int inv_icm20948_set_secondary(struct inv_icm20948 * s) { int r = 0; static uint8_t lIsInited = 0; if(lIsInited == 0) { r = inv_icm20948_write_single_mems_reg(s, REG_I2C_MST_CTRL, BIT_I2C_MST_P_NSR); r |= inv_icm20948_write_single_mems_reg(s, REG_I2C_MST_ODR_CONFIG, MIN_MST_ODR_CONFIG); lIsInited = 1; } return r; } int inv_icm20948_enter_duty_cycle_mode(struct inv_icm20948 * s) { /* secondary cycle mode should be set all the time */ unsigned char data = BIT_I2C_MST_CYCLE|BIT_ACCEL_CYCLE|BIT_GYRO_CYCLE; s->base_state.chip_lp_ln_mode = CHIP_LOW_POWER_ICM20948; return inv_icm20948_write_mems_reg(s, REG_LP_CONFIG, 1, &data); } int inv_icm20948_enter_low_noise_mode(struct inv_icm20948 * s) { /* secondary cycle mode should be set all the time */ unsigned char data = BIT_I2C_MST_CYCLE; s->base_state.chip_lp_ln_mode = CHIP_LOW_NOISE_ICM20948; return inv_icm20948_write_mems_reg(s, REG_LP_CONFIG, 1, &data); } /** Should be called once on power up. Loads DMP3, initializes internal variables needed * for other lower driver functions. */ int inv_icm20948_initialize_lower_driver(struct inv_icm20948 * s, enum SMARTSENSOR_SERIAL_INTERFACE type, const uint8_t *dmp3_image, uint32_t dmp3_image_size) { int result = 0; static unsigned char data; // set static variable s->sAllowLpEn = 1; s->s_compass_available = 0; // ICM20948 do not support the proximity sensor for the moment. // s_proximity_available variable is nerver changes s->s_proximity_available = 0; // Set varialbes to default values memset(&s->base_state, 0, sizeof(s->base_state)); s->base_state.pwr_mgmt_1 = BIT_CLK_PLL; s->base_state.pwr_mgmt_2 = BIT_PWR_ACCEL_STBY | BIT_PWR_GYRO_STBY | BIT_PWR_PRESSURE_STBY; s->base_state.serial_interface = type; result |= inv_icm20948_read_mems_reg(s, REG_USER_CTRL, 1, &s->base_state.user_ctrl); result |= inv_icm20948_wakeup_mems(s); result |= inv_icm20948_read_mems_reg(s, REG_WHO_AM_I, 1, &data); /* secondary cycle mode should be set all the time */ data = BIT_I2C_MST_CYCLE|BIT_ACCEL_CYCLE|BIT_GYRO_CYCLE; // Set default mode to low power mode result |= inv_icm20948_set_lowpower_or_highperformance(s, 0); // Disable Ivory DMP. if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) s->base_state.user_ctrl = BIT_I2C_IF_DIS; else s->base_state.user_ctrl = 0; result |= inv_icm20948_write_single_mems_reg(s, REG_USER_CTRL, s->base_state.user_ctrl); //Setup Ivory DMP. result |= inv_icm20948_load_firmware(s, dmp3_image, dmp3_image_size); if(result) return result; else s->base_state.firmware_loaded = 1; result |= inv_icm20948_set_dmp_address(s); // Turn off all sensors on DMP by default. //result |= dmp_set_data_output_control1(0); // FIXME in DMP, these should be off by default. result |= dmp_icm20948_reset_control_registers(s); // set FIFO watermark to 80% of actual FIFO size result |= dmp_icm20948_set_FIFO_watermark(s, 800); // Enable Interrupts. data = 0x2; result |= inv_icm20948_write_mems_reg(s, REG_INT_ENABLE, 1, &data); // Enable DMP Interrupt data = 0x1; result |= inv_icm20948_write_mems_reg(s, REG_INT_ENABLE_2, 1, &data); // Enable FIFO Overflow Interrupt // TRACKING : To have accelerometers datas and the interrupt without gyro enables. data = 0XE4; result |= inv_icm20948_write_mems_reg(s, REG_SINGLE_FIFO_PRIORITY_SEL, 1, &data); // Disable HW temp fix inv_icm20948_read_mems_reg(s, REG_HW_FIX_DISABLE,1,&data); data |= 0x08; inv_icm20948_write_mems_reg(s, REG_HW_FIX_DISABLE,1,&data); // Setup MEMs properties. s->base_state.accel_averaging = 1; //Change this value if higher sensor sample avergaing is required. s->base_state.gyro_averaging = 1; //Change this value if higher sensor sample avergaing is required. inv_icm20948_set_gyro_divider(s, FIFO_DIVIDER); //Initial sampling rate 1125Hz/19+1 = 56Hz. inv_icm20948_set_accel_divider(s, FIFO_DIVIDER); //Initial sampling rate 1125Hz/19+1 = 56Hz. // Init the sample rate to 56 Hz for BAC,STEPC and B2S dmp_icm20948_set_bac_rate(s, DMP_ALGO_FREQ_56); dmp_icm20948_set_b2s_rate(s, DMP_ALGO_FREQ_56); // FIFO Setup. result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_CFG, BIT_SINGLE_FIFO_CFG); // FIFO Config. fixme do once? burst write? result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_RST, 0x1f); // Reset all FIFOs. result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_RST, 0x1e); // Keep all but Gyro FIFO in reset. result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_EN, 0x0); // Slave FIFO turned off. result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_EN_2, 0x0); // Hardware FIFO turned off. s->base_state.lp_en_support = 1; if(s->base_state.lp_en_support == 1) inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1); result |= inv_icm20948_sleep_mems(s); return result; } static void activate_compass(struct inv_icm20948 * s) { s->s_compass_available = 1; } static void desactivate_compass(struct inv_icm20948 * s) { s->s_compass_available = 0; } int inv_icm20948_get_compass_availability(struct inv_icm20948 * s) { return s->s_compass_available; } // return true 1 if gyro was enabled, otherwise false 0 static unsigned char inv_is_gyro_enabled(struct inv_icm20948 * s) { if ((s->inv_androidSensorsOn_mask[0] & INV_NEEDS_GYRO_MASK) || (s->inv_androidSensorsOn_mask[1] & INV_NEEDS_GYRO_MASK1)) return 1; return 0; } int inv_icm20948_get_proximity_availability(struct inv_icm20948 * s) { return s->s_proximity_available; } int inv_icm20948_set_slave_compass_id(struct inv_icm20948 * s, int id) { int result = 0; (void)id; //result = inv_icm20948_wakeup_mems(s); //if (result) // return result; inv_icm20948_prevent_lpen_control(s); activate_compass(s); inv_icm20948_init_secondary(s); // Set up the secondary I2C bus on 20630. inv_icm20948_set_secondary(s); //Setup Compass result = inv_icm20948_setup_compass_akm(s); //Setup Compass mounting matrix into DMP result |= inv_icm20948_compass_dmp_cal(s, s->mounting_matrix, s->mounting_matrix_secondary_compass); if (result) desactivate_compass(s); //result = inv_icm20948_sleep_mems(s); inv_icm20948_allow_lpen_control(s); return result; } int inv_icm20948_set_gyro_divider(struct inv_icm20948 * s, unsigned char div) { s->base_state.gyro_div = div; return inv_icm20948_write_mems_reg(s, REG_GYRO_SMPLRT_DIV, 1, &div); } unsigned char inv_icm20948_get_gyro_divider(struct inv_icm20948 * s) { return s->base_state.gyro_div; } int inv_icm20948_set_secondary_divider(struct inv_icm20948 * s, unsigned char div) { s->base_state.secondary_div = 1UL<