/* * * Copyright (c) [2018] by InvenSense, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "imu/inv_imu_selftest.h" #include "imu/inv_imu_extfunc.h" static int configure_selftest_parameters(inv_imu_device_t * s, const inv_imu_selftest_parameters_t st_params); int inv_imu_run_selftest(inv_imu_device_t *s, const inv_imu_selftest_parameters_t st_params, inv_imu_selftest_output_t *st_output) { int status = 0; uint8_t value; uint8_t data = 0; uint8_t st_done = 0; int polling_timeout_ms = 1000; #ifdef ICM42680 /* Enable self-test */ status |= inv_imu_read_reg(s, SENSOR_CONFIG3_MREG1, 1, &value); value &= ~SENSOR_CONFIG3_APEX_DISABLE_MASK; status |= inv_imu_write_reg(s, SENSOR_CONFIG3_MREG1, 1, &value); #endif /* Disables Gyro/Accel sensors */ status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &value); value &= ~PWR_MGMT0_ACCEL_MODE_MASK; #if INV_IMU_IS_GYRO_SUPPORTED value &= ~PWR_MGMT0_GYRO_MODE_MASK; #endif status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &value); /* Enable RC oscillator */ status |= inv_imu_switch_on_mclk(s); /* Clear DMP SRAM (1 ms wait included in `inv_imu_reset_dmp()`) */ status |= inv_imu_reset_dmp(s, APEX_CONFIG0_DMP_MEM_RESET_APEX_ST_EN); /* Update `dmp_is_on` since APEX features will have to restart from scratch */ s->dmp_is_on = 0; /* Load self-test data */ status |= inv_imu_load_selftest_data(s); /* Set self-test parameters */ status |= configure_selftest_parameters(s, st_params); /* * Enable accel and/or gyro self-test. * If both accel and gyro self-test are enabled, * they should be set simultaneously in the same write access */ status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value); value &= ~SELFTEST_ACCEL_ST_EN_MASK; #if INV_IMU_IS_GYRO_SUPPORTED value &= ~SELFTEST_GYRO_ST_EN_MASK; #endif value |= (uint8_t)st_params.st_control; status |= inv_imu_write_reg(s, SELFTEST_MREG1, 1, &value); /* Poll int_status_st_done bit */ do { inv_imu_sleep_us(1000); status |= inv_imu_read_reg(s, INT_STATUS, 1, &st_done); st_done &= INT_STATUS_ST_INT_MASK; if (0 == --polling_timeout_ms) return (status | -1); /* Return error if timeout is reached */ } while (!st_done /* Exit if ST_DONE */ && !status /* Or if error is detected */); /* Read self-test results (must start with ST_STATUS2) */ status |= inv_imu_read_reg(s, ST_STATUS2_MREG1, 1, &data); #if INV_IMU_IS_GYRO_SUPPORTED st_output->gyro_status = (data & ST_STATUS2_GYRO_ST_PASS_MASK) >> ST_STATUS2_GYRO_ST_PASS_POS; st_output->gyro_status |= ((data & ST_STATUS2_ST_INCOMPLETE_MASK) >> ST_STATUS2_ST_INCOMPLETE_POS) << 1; st_output->gx_status = (data & ST_STATUS2_GX_ST_PASS_MASK) >> ST_STATUS2_GX_ST_PASS_POS; st_output->gy_status = (data & ST_STATUS2_GY_ST_PASS_MASK) >> ST_STATUS2_GY_ST_PASS_POS; st_output->gz_status = (data & ST_STATUS2_GZ_ST_PASS_MASK) >> ST_STATUS2_GZ_ST_PASS_POS; #endif status |= inv_imu_read_reg(s, ST_STATUS1_MREG1, 1, &data); st_output->accel_status = (data & ST_STATUS1_ACCEL_ST_PASS_MASK) >> ST_STATUS1_ACCEL_ST_PASS_POS; st_output->ax_status = (data & ST_STATUS1_AX_ST_PASS_MASK) >> ST_STATUS1_AX_ST_PASS_POS; st_output->ay_status = (data & ST_STATUS1_AY_ST_PASS_MASK) >> ST_STATUS1_AY_ST_PASS_POS; st_output->az_status = (data & ST_STATUS1_AZ_ST_PASS_MASK) >> ST_STATUS1_AZ_ST_PASS_POS; /* Disable self-test */ status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value); value &= ~SELFTEST_ACCEL_ST_EN_MASK; #if INV_IMU_IS_GYRO_SUPPORTED value &= ~SELFTEST_GYRO_ST_EN_MASK; #endif value |= (uint8_t)SELFTEST_DIS; status |= inv_imu_write_reg(s, SELFTEST_MREG1, 1, &value); /* Reset FIFO because ST data may have been pushed to it */ status |= inv_imu_reset_fifo(s); /* Restore idle bit */ status |= inv_imu_switch_off_mclk(s); #ifdef ICM42680 /* Disable self-test */ status |= inv_imu_read_reg(s, SENSOR_CONFIG3_MREG1, 1, &value); value &= ~SENSOR_CONFIG3_APEX_DISABLE_MASK; value |= 1 << SENSOR_CONFIG3_APEX_DISABLE_POS; status |= inv_imu_write_reg(s, SENSOR_CONFIG3_MREG1, 1, &value); #endif return status; } int inv_imu_init_selftest_parameters_struct(inv_imu_device_t * s, inv_imu_selftest_parameters_t *st_params) { (void)s; st_params->st_num_samples = ST_CONFIG_16_SAMPLES; #if INV_IMU_IS_GYRO_SUPPORTED st_params->st_control = (SELFTEST_ACCEL_GYRO_ST_EN_t)SELFTEST_EN; #else st_params->st_control = (SELFTEST_ACCEL_GYRO_ST_EN_t)SELFTEST_ACCEL_EN; #endif return 0; } int inv_imu_load_selftest_data(inv_imu_device_t *s) { int status = 0; uint8_t value; /* Enable RC oscillator */ status |= inv_imu_switch_on_mclk(s); /* Set up OTP controller to reload factory-trimmed self-test response into SRAM */ status |= inv_imu_read_reg(s, OTP_CONFIG_MREG1, 1, &value); value &= ~OTP_CONFIG_OTP_COPY_MODE_MASK; value |= (uint8_t)OTP_CONFIG_OTP_COPY_ST_DATA; status |= inv_imu_write_reg(s, OTP_CONFIG_MREG1, 1, &value); /* Take the OTP macro out of power-down mode */ status |= inv_imu_read_reg(s, OTP_CTRL7_MREG2, 1, &value); value &= ~OTP_CTRL7_OTP_PWR_DOWN_MASK; value |= (uint8_t)OTP_CTRL7_PWR_DOWN_DIS; status |= inv_imu_write_reg(s, OTP_CTRL7_MREG2, 1, &value); /* Wait for voltage generator to power on */ inv_imu_sleep_us(100); /* Host should disable INT function first before kicking off OTP copy operation */ /* Trigger OTP to reload data (this time in self-test mode) */ status |= inv_imu_read_reg(s, OTP_CTRL7_MREG2, 1, &value); value &= ~OTP_CTRL7_OTP_RELOAD_MASK; value |= (uint8_t)OTP_CTRL7_OTP_RELOAD_EN; status |= inv_imu_write_reg(s, OTP_CTRL7_MREG2, 1, &value); /* Wait for OTP reload */ inv_imu_sleep_us(20); /* Disable RC oscillator */ status |= inv_imu_switch_off_mclk(s); return status; } static int configure_selftest_parameters(inv_imu_device_t * s, const inv_imu_selftest_parameters_t st_params) { int status = 0; uint8_t value; /* Self-test configuration cannot be updated if it already running */ status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value); if (value & (SELFTEST_ACCEL_ST_EN_MASK | SELFTEST_GYRO_ST_EN_MASK)) return INV_ERROR_UNEXPECTED; status |= inv_imu_read_reg(s, ST_CONFIG_MREG1, 1, &value); value &= ~((uint8_t)ST_CONFIG_ST_NUMBER_SAMPLE_MASK); value &= ~((uint8_t)ST_CONFIG_ACCEL_ST_LIM_MASK); #if INV_IMU_IS_GYRO_SUPPORTED value &= ~((uint8_t)ST_CONFIG_GYRO_ST_LIM_MASK); #endif value |= (uint8_t)st_params.st_num_samples; value |= (uint8_t)ST_CONFIG_ACCEL_ST_LIM_50; #if INV_IMU_IS_GYRO_SUPPORTED value |= (uint8_t)ST_CONFIG_GYRO_ST_LIM_50; #endif status |= inv_imu_write_reg(s, ST_CONFIG_MREG1, 1, &value); return status; }