fix(usb): resolve USB CDC TX failure caused by three independent bugs

Bug 1 (PRIMARY — DCache/USB coherency):
SCB_DisableDCache() was buried inside icm42688_init(), called ~3.5s
after USB starts. STM32F7 DCache/USB coherency issue: when DCache is
on (enabled by SystemInit()), CPU writes to TX buffers stay in cache
and the USB hardware reads stale SRAM data. Moved SCB_DisableDCache()
to main() before HAL_Init(), ensuring coherency for all USB transfers.

Bug 2 (USB TX corruption):
CDC_Transmit() passed the caller's stack-allocated buf pointer directly
to the USB stack. The USB TXFE interrupt fires asynchronously; by then
the stack buffer may have been modified by the next loop iteration.
CDC_Transmit() now copies into the static UserTxBuffer before handing
off to the USB hardware, ensuring the buffer is stable for the transfer.

Bug 3 (IMU type mismatch → wrong data to balance):
main.c called icm42688_init()/icm42688_read() directly, passing
icm42688_data_t* (raw int16 ax/ay/az/gx/gy/gz) to balance_update()
which expects IMUData* (float pitch/pitch_rate from complementary
filter). Type mismatch produced garbage balance values. Fixed by using
mpu6000_init()/mpu6000_read() which wraps icm42688 + sensor fusion.
Telemetry updated to report fused pitch/rate instead of raw ADC counts.

Also fix icm42688_init() returning 0 on who==0 (no SPI response),
which falsely indicated IMU success.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sl-firmware 2026-02-28 12:37:24 -05:00
parent 0afdaea2e1
commit 0bfd617c44
3 changed files with 20 additions and 14 deletions

View File

@ -121,7 +121,6 @@ static int8_t CDC_Receive(uint8_t *buf, uint32_t *len) {
USBD_CDC_ItfTypeDef USBD_CDC_fops = { CDC_Init, CDC_DeInit, CDC_Control, CDC_Receive };
uint8_t CDC_Transmit(uint8_t *buf, uint16_t len) {
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)hUsbDevice.pClassData;
if (hcdc == NULL) return USBD_FAIL;
if (hcdc->TxState != 0) {
@ -130,6 +129,12 @@ uint8_t CDC_Transmit(uint8_t *buf, uint16_t len) {
if (++busy_count > 100) { hcdc->TxState = 0; busy_count = 0; }
return USBD_BUSY;
}
USBD_CDC_SetTxBuffer(&hUsbDevice, buf, len);
/* Always copy into the static UserTxBuffer so the USB hardware reads
* from a known fixed SRAM address never from the caller's stack.
* The USB TXFE IRQ fires asynchronously; a stack buffer could be
* overwritten by the time the FIFO is loaded. */
if (len > sizeof(UserTxBuffer)) len = sizeof(UserTxBuffer);
memcpy(UserTxBuffer, buf, len);
USBD_CDC_SetTxBuffer(&hUsbDevice, UserTxBuffer, len);
return USBD_CDC_TransmitPacket(&hUsbDevice);
}

View File

@ -104,7 +104,8 @@ int icm42688_init(void) {
HAL_GPIO_Init(MPU_CS_PORT, &gpio);
cs_high();
SCB_DisableDCache(); /* Avoid SPI DCache issues on F7 */
/* DCache is disabled at startup in main.c before USB/peripherals init.
* No SCB_DisableDCache() here calling it after USB starts corrupts USB state. */
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
@ -137,7 +138,8 @@ int icm42688_init(void) {
imu_type = 2;
ret = -99; /* TODO: ICM init */
} else {
ret = -who;
/* who==0 means no SPI response — must not return 0 (false success) */
ret = (who != 0) ? -(int)who : -128;
}
/* Speed up SPI for reads */

View File

@ -3,8 +3,7 @@
#include "usbd_cdc.h"
#include "usbd_cdc_if.h"
#include "usbd_desc.h"
#include "icm42688.h"
#include "bmp280.h"
#include "mpu6000.h"
#include "balance.h"
#include "hoverboard.h"
#include "config.h"
@ -54,6 +53,7 @@ void SysTick_Handler(void) { HAL_IncTick(); }
int main(void) {
SCB_EnableICache();
SCB_DisableDCache(); /* Must be off before USB starts — STM32F7 DCache/USB coherency */
checkForBootloader(); /* Check RTC magic BEFORE HAL_Init — must be first thing */
HAL_Init();
SystemClock_Config();
@ -67,8 +67,8 @@ int main(void) {
status_init();
HAL_Delay(3000); /* Wait for USB host to enumerate */
/* Init IMU */
int imu_ret = icm42688_init();
/* Init IMU (MPU6000 via SPI1 — mpu6000.c wraps icm42688 + complementary filter) */
int imu_ret = mpu6000_init() ? 0 : -1;
/* Init hoverboard ESC UART */
hoverboard_init();
@ -80,14 +80,14 @@ int main(void) {
char buf[256];
int len;
icm42688_data_t imu;
IMUData imu;
uint32_t send_tick = 0;
uint32_t balance_tick = 0;
uint32_t esc_tick = 0;
const float dt = 1.0f / PID_LOOP_HZ; /* 1ms at 1kHz */
while (1) {
if (imu_ret == 0) icm42688_read(&imu);
if (imu_ret == 0) mpu6000_read(&imu);
uint32_t now = HAL_GetTick();
@ -123,10 +123,9 @@ int main(void) {
send_tick = now;
if (imu_ret == 0) {
len = snprintf(buf, sizeof(buf),
"{\"ax\":%d,\"ay\":%d,\"az\":%d,\"gx\":%d,\"gy\":%d,\"gz\":%d,"
"\"p\":%d,\"m\":%d,\"s\":%d}\n",
imu.ax, imu.ay, imu.az, imu.gx, imu.gy, imu.gz,
(int)(bal.pitch_deg * 10), /* pitch x10 for precision without %f */
"{\"p\":%d,\"r\":%d,\"m\":%d,\"s\":%d}\n",
(int)(imu.pitch * 10), /* fused pitch degrees x10 */
(int)(imu.pitch_rate * 10), /* gyro pitch rate °/s x10 */
(int)bal.motor_cmd,
(int)bal.state);
} else {