diff --git a/soc/software/gps_system/src/drivers/imu/tdk_icm42688.c b/soc/software/gps_system/src/drivers/imu/tdk_icm42688.c index a4fc829..1012c2f 100644 --- a/soc/software/gps_system/src/drivers/imu/tdk_icm42688.c +++ b/soc/software/gps_system/src/drivers/imu/tdk_icm42688.c @@ -30,6 +30,8 @@ #define ICM42688_WHO_AM_I 0x47u #define ICM_PWR_LOW_NOISE 0x0fu #define ICM_INT1_LATCHED_HIGH_PUSH_PULL 0x07u +/* DEVICE_CONFIG bit 0: self-clearing soft reset. */ +#define ICM_SOFT_RESET 0x01u #define ICM_UI_DRDY_INT1_EN 0x08u #define TDK_GRAVITY_MPS2 9.80665 @@ -224,6 +226,33 @@ bool tdk_icm42688_init(const tdk_icm42688_config_t* config) bsp_pmod_spi_set_clock_mode(BSP_PMOD_SPI_CLOCK_MODE0); bsp_pmod_spi_set_timing(10u, 10u, 10u, 10u); delay_us(1000u); + + /* + * Recover a device left mid-transfer before probing it. + * + * Halting the core with a debugger, or resetting it while a transaction is + * in flight, leaves the sensor's SPI shift register part-way through a + * word. Every later access is then misaligned, WHO_AM_I reads back 0x00, + * and initialisation fails until the board is power cycled -- the + * `sensor=none who=0x00` case seen in field logs. Toggling chip select + * around idle bytes returns the device to command state, after which a + * soft reset restores the register file to defaults so this function + * starts from the same place regardless of what ran before it. + */ + for(unsigned int attempt = 0u ; attempt < 4u ; attempt++) + { + bsp_pmod_spi_select(); + (void)bsp_pmod_spi_xfer8(ICM_SPI_DUMMY); + bsp_pmod_spi_deselect(); + delay_us(50u); + } + write_register(ICM_DEVICE_CONFIG, ICM_SOFT_RESET); + /* Datasheet requires 1 ms before any register access after a soft reset. */ + delay_ms(2u); + /* The reset asserts RESET_DONE; clear it so it cannot be mistaken for a + * data-ready later. */ + (void)read_register(ICM_INT_STATUS); + diagnostics.who_am_i = read_register(ICM_WHO_AM_I); if((diagnostics.who_am_i != ICM42688_WHO_AM_I) || (read_register(ICM_DEVICE_CONFIG) != 0x00u))