fix: MPU6000 IMU calibration SPI/DCache issue (Issue #520)

Three bugs prevented mpu6000_is_calibrated() from returning true,
blocking arming and balance mode:

1. WHO_AM_I single-attempt: one SPI glitch returning 0x00 caused
   icm42688_init() to return -128, skipping mpu6000_calibrate()
   entirely. Fix: retry WHO_AM_I up to 3 times with 10ms gaps.

2. icm42688_read() rx[15] uninitialized: if HAL_SPI_TransmitReceive()
   failed, garbage stack data was accumulated as gyro bias. Fix: zero-
   init rx[15] so failed transfers produce zero data.

3. mpu6000_calibrate() raw uninitialized: UB if icm42688_read() is
   a no-op (imu_type mismatch). Fix: zero-init raw each iteration.

Also add SCB_InvalidateDCache_by_Addr() on SPI rx buffers in rreg()
and icm42688_read() for DCache coherency. Currently a no-op (DCache
is not enabled), but required if SCB_EnableDCache() is added — stack
buffers in SRAM2 are in the cacheable memory region on STM32F7.

Fix misleading DCache comment in icm42688.c (claimed DCache was
disabled by main.c; actually SCB_EnableDCache() is never called).

Build: 59904 bytes Flash (+512), 17100 bytes RAM — SUCCESS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sl-firmware 2026-03-06 23:14:49 -05:00
parent 7141e12320
commit b0a5041261
2 changed files with 20 additions and 6 deletions

View File

@ -45,6 +45,9 @@ static uint8_t rreg(uint8_t reg) {
cs_low(); cs_low();
HAL_SPI_TransmitReceive(&hspi1, tx, rx, 2, 100); HAL_SPI_TransmitReceive(&hspi1, tx, rx, 2, 100);
cs_high(); 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]; return rx[1];
} }
@ -104,8 +107,10 @@ int icm42688_init(void) {
HAL_GPIO_Init(MPU_CS_PORT, &gpio); HAL_GPIO_Init(MPU_CS_PORT, &gpio);
cs_high(); cs_high();
/* DCache is disabled at startup in main.c before USB/peripherals init. /* DCache: main.c does NOT call SCB_EnableDCache(), so DCache is currently OFF.
* No SCB_DisableDCache() here calling it after USB starts corrupts USB state. */ * 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.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Mode = SPI_MODE_MASTER;
@ -127,7 +132,13 @@ int icm42688_init(void) {
wreg(0x6B, 0x01); /* Wake, PLL */ wreg(0x6B, 0x01); /* Wake, PLL */
HAL_Delay(50); HAL_Delay(50);
uint8_t who = rreg(MPU_REG_WHO_AM_I); /* 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] */ tr(who); /* trace[0] */
int ret; int ret;
@ -153,12 +164,15 @@ int icm42688_init(void) {
void icm42688_read(icm42688_data_t *d) { void icm42688_read(icm42688_data_t *d) {
if (imu_type == 1) { if (imu_type == 1) {
/* MPU6000: ACCEL_XOUT_H (0x3B) → 14 bytes: accel(6)+temp(2)+gyro(6) */ /* MPU6000: ACCEL_XOUT_H (0x3B) → 14 bytes: accel(6)+temp(2)+gyro(6) */
uint8_t tx[15], rx[15]; 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; tx[0] = MPU_REG_ACCEL_XOUT_H | 0x80;
for (int i = 1; i < 15; i++) tx[i] = 0x00;
cs_low(); cs_low();
HAL_SPI_TransmitReceive(&hspi1, tx, rx, 15, 100); HAL_SPI_TransmitReceive(&hspi1, tx, rx, 15, 100);
cs_high(); 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->ax = (int16_t)((rx[1] << 8) | rx[2]);
d->ay = (int16_t)((rx[3] << 8) | rx[4]); d->ay = (int16_t)((rx[3] << 8) | rx[4]);

View File

@ -62,7 +62,7 @@ void mpu6000_calibrate(void) {
*/ */
int32_t sum_gx = 0, sum_gy = 0, sum_gz = 0; int32_t sum_gx = 0, sum_gy = 0, sum_gz = 0;
for (int i = 0; i < GYRO_CAL_SAMPLES; i++) { for (int i = 0; i < GYRO_CAL_SAMPLES; i++) {
icm42688_data_t raw; icm42688_data_t raw = {0}; /* zero-init: guard against icm42688_read no-op on imu_type mismatch */
icm42688_read(&raw); icm42688_read(&raw);
sum_gx += raw.gx; sum_gx += raw.gx;
sum_gy += raw.gy; sum_gy += raw.gy;