/* MPU6000 + ICM-42688-P dual driver — auto-detects based on WHO_AM_I */ #include "stm32f7xx_hal.h" #include "config.h" #include "icm42688.h" static SPI_HandleTypeDef hspi1; static uint8_t imu_type = 0; /* 0=unknown, 1=MPU6000, 2=ICM42688 */ /* MPU6000 registers */ #define MPU_REG_SMPLRT_DIV 0x19 #define MPU_REG_CONFIG 0x1A #define MPU_REG_GYRO_CONFIG 0x1B #define MPU_REG_ACCEL_CONFIG 0x1C #define MPU_REG_ACCEL_XOUT_H 0x3B #define MPU_REG_PWR_MGMT_1 0x6B #define MPU_REG_PWR_MGMT_2 0x6C #define MPU_REG_WHO_AM_I 0x75 #define MPU6000_WHO 0x68 /* ICM-42688-P registers */ #define ICM_REG_DEVICE_CONFIG 0x11 #define ICM_REG_TEMP_DATA1 0x1D #define ICM_REG_PWR_MGMT0 0x4E #define ICM_REG_GYRO_CONFIG0 0x4F #define ICM_REG_ACCEL_CONFIG0 0x50 #define ICM_REG_WHO_AM_I 0x75 #define ICM_REG_BANK_SEL 0x76 #define ICM42688_WHO 0x47 static void cs_low(void) { HAL_GPIO_WritePin(MPU_CS_PORT, MPU_CS_PIN, GPIO_PIN_RESET); } static void cs_high(void) { HAL_GPIO_WritePin(MPU_CS_PORT, MPU_CS_PIN, GPIO_PIN_SET); } static void wreg(uint8_t reg, uint8_t val) { uint8_t tx[2] = { reg & 0x7F, val }; uint8_t rx[2]; cs_low(); HAL_SPI_TransmitReceive(&hspi1, tx, rx, 2, 100); cs_high(); HAL_Delay(1); } static uint8_t rreg(uint8_t reg) { uint8_t tx[2] = { reg | 0x80, 0x00 }; uint8_t rx[2] = {0, 0}; cs_low(); HAL_SPI_TransmitReceive(&hspi1, tx, rx, 2, 100); cs_high(); /* DCache coherency: invalidate rx so CPU reads SPI-written SRAM, not stale cache. * No-op when DCache is disabled; required if DCache is on (e.g. SCB_EnableDCache). */ SCB_InvalidateDCache_by_Addr((uint32_t *)(uintptr_t)rx, (int32_t)sizeof(rx)); return rx[1]; } static uint8_t trace[16]; static int trace_idx = 0; static void tr(uint8_t v) { if (trace_idx < 16) trace[trace_idx++] = v; } static int init_mpu6000(void) { /* Reset */ wreg(MPU_REG_PWR_MGMT_1, 0x80); HAL_Delay(100); /* Wake up, use PLL with X gyro ref */ wreg(MPU_REG_PWR_MGMT_1, 0x01); HAL_Delay(10); /* Sample rate = 1kHz (divider=0) */ wreg(MPU_REG_SMPLRT_DIV, 0x00); /* DLPF = 42Hz (config=3) */ wreg(MPU_REG_CONFIG, 0x03); /* Gyro: ±2000°/s (FS_SEL=3) */ wreg(MPU_REG_GYRO_CONFIG, 0x18); /* Accel: ±16g (AFS_SEL=3) */ wreg(MPU_REG_ACCEL_CONFIG, 0x18); /* Enable all axes */ wreg(MPU_REG_PWR_MGMT_2, 0x00); HAL_Delay(50); /* Verify */ uint8_t pwr = rreg(MPU_REG_PWR_MGMT_1); tr(pwr); /* Should be 0x01 */ return (pwr == 0x01) ? 0 : -200 - (int)pwr; } int icm42688_init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_SPI1_CLK_ENABLE(); GPIO_InitTypeDef gpio = {0}; gpio.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; gpio.Mode = GPIO_MODE_AF_PP; gpio.Pull = GPIO_NOPULL; gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; gpio.Alternate = GPIO_AF5_SPI1; HAL_GPIO_Init(GPIOA, &gpio); /* CS on PA4 for MAMBA */ gpio.Pin = MPU_CS_PIN; gpio.Mode = GPIO_MODE_OUTPUT_PP; gpio.Speed = GPIO_SPEED_FREQ_HIGH; gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(MPU_CS_PORT, &gpio); cs_high(); /* DCache: main.c does NOT call SCB_EnableDCache(), so DCache is currently OFF. * If DCache is ever enabled, all SPI rx buffers need SCB_InvalidateDCache_by_Addr() * (already added in rreg() and icm42688_read()) and USB buffers must remain mapped * non-cacheable via MPU Region 0 in usbd_conf.c. */ hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH; hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; if (HAL_SPI_Init(&hspi1) != HAL_OK) return -1; HAL_Delay(200); /* Wake from sleep first - MPU6000 needs this before WHO_AM_I */ wreg(0x6B, 0x80); /* Reset */ HAL_Delay(100); wreg(0x6B, 0x01); /* Wake, PLL */ HAL_Delay(50); /* Retry WHO_AM_I up to 3 times: a single SPI glitch returning 0x00 * would otherwise abort init and prevent calibration from ever running. */ uint8_t who = 0; for (int attempt = 0; attempt < 3 && who == 0; attempt++) { if (attempt > 0) HAL_Delay(10); who = rreg(MPU_REG_WHO_AM_I); } tr(who); /* trace[0] */ int ret; if (who == MPU6000_WHO) { imu_type = 1; ret = init_mpu6000(); } else if (who == ICM42688_WHO) { imu_type = 2; ret = -99; /* TODO: ICM init */ } else { /* who==0 means no SPI response — must not return 0 (false success) */ ret = (who != 0) ? -(int)who : -128; } /* Speed up SPI for reads */ hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; HAL_SPI_Init(&hspi1); tr((uint8_t)imu_type); /* trace[last] */ return ret; } void icm42688_read(icm42688_data_t *d) { if (imu_type == 1) { /* MPU6000: ACCEL_XOUT_H (0x3B) → 14 bytes: accel(6)+temp(2)+gyro(6) */ uint8_t tx[15] = {0}; uint8_t rx[15] = {0}; /* zero-init: failed SPI transfers return 0, not garbage */ tx[0] = MPU_REG_ACCEL_XOUT_H | 0x80; cs_low(); HAL_SPI_TransmitReceive(&hspi1, tx, rx, 15, 100); cs_high(); /* DCache coherency: force CPU to read SPI-written SRAM, not a stale cache line. * No-op when DCache is disabled; critical if SCB_EnableDCache() is called. */ SCB_InvalidateDCache_by_Addr((uint32_t *)(uintptr_t)rx, (int32_t)sizeof(rx)); d->ax = (int16_t)((rx[1] << 8) | rx[2]); d->ay = (int16_t)((rx[3] << 8) | rx[4]); d->az = (int16_t)((rx[5] << 8) | rx[6]); int16_t temp_raw = (int16_t)((rx[7] << 8) | rx[8]); d->temp_x10 = (int16_t)((temp_raw + 12421) / 34); /* MPU6000 formula */ d->gx = (int16_t)((rx[9] << 8) | rx[10]); d->gy = (int16_t)((rx[11] << 8) | rx[12]); d->gz = (int16_t)((rx[13] << 8) | rx[14]); } } void icm42688_get_trace(uint8_t *out, int *len) { *len = trace_idx; for (int i = 0; i < trace_idx; i++) out[i] = trace[i]; }