Skip to content

fix(firmware/c_board): Switch SPI1 async transfer to DMA to avoid OVR errors#25

Merged
qzhhhi merged 3 commits intomainfrom
fix/cboard-spi
Feb 27, 2026
Merged

fix(firmware/c_board): Switch SPI1 async transfer to DMA to avoid OVR errors#25
qzhhhi merged 3 commits intomainfrom
fix/cboard-spi

Conversation

@qzhhhi
Copy link
Member

@qzhhhi qzhhhi commented Feb 26, 2026

The previous interrupt-based SPI async path could hit HAL_SPI_ERROR_OVR at high
SPI clock rates because RXNE was not serviced fast enough before the next byte.

  • Replace HAL_SPI_TransmitReceive_IT with HAL_SPI_TransmitReceive_DMA
  • Add SPI1 DMA resources (DMA2 Stream0/3), DMA init, and DMA IRQ handlers
  • Wire CubeMX-generated DMA files into build and startup init sequence
  • Add SPI error callback fallback to finish transfer and release CS/lock
  • Fix BMI088 DRDY EXTI to falling edge (active-low configuration)
  • Synchronize SPI async callback interface changes in firmware/rmcs_board

概述

本 PR 将 STM32F4 上 SPI1 的异步传输从中断方式(HAL_SPI_TransmitReceive_IT)切换为 DMA(HAL_SPI_TransmitReceive_DMA),以避免在高 SPI 时钟下因 RXNE 未及时读取导致的 HAL_SPI_ERROR_OVR。为此新增并集成了 SPI1 所需的 DMA 资源与中断处理、将 DMA 初始化并入启动流程,增加 SPI 错误回调兜底,修正 BMI088 的 DRDY EXTI 配置,并在断言路径增加 LED 可视化以提高故障可见性。上层 SPI 回调接口在驱动层改动后已同步更新。

主要改动点

  • DMA 基础设施

    • 新增 firmware/c_board/bsp/HAL/Core/Inc/dma.h 与 Core/Src/dma.c,提供 MX_DMA_Init(),使能 DMA2 并配置 DMA2_Stream0/Stream3 的 NVIC 优先级与中断使能。
    • 将 dma.c 纳入 CubeMX/CMake 生成的源列表,并在 main.c 启动序列中调用 MX_DMA_Init()。
  • SPI1 的 DMA 支持(HAL MSP 层)

    • 在 spi.c 中新增 hdma_spi1_rx、hdma_spi1_tx,并在 HAL_SPI_MspInit 为 SPI1 配置 DMA2_Stream0(RX,Channel3,外设->内存)与 DMA2_Stream3(TX,Channel3,内存->外设);在 HAL_SPI_MspDeInit 中反初始化 DMA。
    • 在 stm32f4xx_it.h / stm32f4xx_it.c 中增加 DMA2_Stream0_IRQHandler 与 DMA2_Stream3_IRQHandler,调用 HAL_DMA_IRQHandler(&hdma_spi1_rx/tx)。
  • SPI 异步传输与回调接口变更(驱动层与上层同步)

    • 驱动层将异步传输由 HAL_IT 改为 HAL_DMA,新增 DMA 管理逻辑(init_dma_transfer、trigger_dma、active_dma_count_ 等)、DMA 完成与错误回调适配。
    • 在 c_board 层新增全局 DMA 回调封装(dma_transfer_complete_callback_global / dma_error_callback_global),并在 HAL_SPI_ErrorCallback 中对 hspi1 调用驱动失败路径以确保片选/锁释放。
    • 注意:HAL_SPI_TxRxCpltCallback 被替换为断言(表明不再走 SPI 中断完成路径),DMA 完成由 DMA 回调驱动。
  • 接口与上层驱动适配(BMI088)

    • SpiModule::transmit_receive_async_callback 签名由 (rx_buffer, size) 改为仅 (size);各实现(c_board 与 rmcs_board 的 accel/gyro)已同步,且仅在 size>0 时从 spi_.rx_buffer 解析数据,size==0 时仅释放锁并跳过处理。
  • EXTI 与 GPIO 配置修正

    • 将 BMI088 的 INT1_ACC / INT1_GYRO EXTI 触发由上升沿改为下降沿(GPIO_MODE_IT_FALLING),以匹配 active-low DRDY。
  • 断言与可视化

    • 在 assert 路径中新增 force_led_red() 并在断言前加中断锁与点亮红灯(GPIOH LED),以增强故障可视化。
  • 其它启动/构建调整

    • 将 DMA 源与 NVIC/向量表相关项加入 CubeMX 配置(rmcs_slave.ioc)并调整 ProjectManager 初始化顺序(插入 MX_DMA_Init)。
    • 在主循环(app.cpp)中增加 spi->update() 调用以驱动 DMA 相关状态更新。

重要文件(要点)

  • HAL / 启动相关

    • 新增/修改: firmware/c_board/bsp/HAL/Core/Inc/dma.h, Core/Src/dma.c
    • 修改: firmware/c_board/bsp/HAL/Core/Src/spi.c(hdma_spi1_rx/tx、DMA 初始化与链接)
    • 修改: firmware/c_board/bsp/HAL/Core/Inc/stm32f4xx_it.h / Core/Src/stm32f4xx_it.c(新增 DMA IRQ)
    • 修改: firmware/c_board/bsp/HAL/Core/Src/main.c(调用 MX_DMA_Init)
    • 修改: firmware/c_board/bsp/HAL/Core/Src/gpio.c(EXTI 改为 FALLING)
    • 修改: firmware/c_board/bsp/HAL/cmake/stm32cubemx/CMakeLists.txt(加入 dma.c)
    • 修改: firmware/c_board/bsp/HAL/rmcs_slave.ioc(CubeMX 添加 DMA/NVIC,并插入 MX_DMA_Init)
  • 驱动/上层适配

    • 修改: firmware/c_board/src/spi/spi.hpp / spi.cpp(DMA 启动、回调、错误处理、接口变更)
    • 修改: firmware/c_board/src/spi/bmi088/accel.hpp, gyro.hpp(回调签名与读取改为 spi_.rx_buffer)
    • 修改: firmware/rmcs_board/src/spi/spi.hpp 与 bmi088 的 accel/gyro(同步接口变更)
    • 修改: firmware/c_board/src/utility/assert.cpp(force_led_red 与断言路径)
    • 修改: firmware/c_board/src/app.cpp(主循环增加 spi->update())

兼容性与行为影响

  • API 变更:SpiModule::transmit_receive_async_callback 签名由 (rx_buffer, size) → (size),所有模块实现已同步;外部模块不能再依赖回传的 rx_buffer 指针。
  • 行为改动:SPI 异步路径改为 DMA,可显著降低高速 SPI 下的 OVR 错误;在错误场景下通过 HAL_SPI_ErrorCallback 将失败上报为 size==0,确保资源(片选/锁)被释放。
  • 构建/启动:新增的 DMA 源与 CubeMX 配置已并入构建与初始化,向量表与 NVIC 已对应更新。

风险与审查建议

  • DMA 资源冲突:确认 DMA2_Stream0/Stream3 与 Channel3 的选择不会与板上其它外设冲突(尤其 USB、USART 或其他使用 DMA 的外设)。
  • 并发与回调顺序:验证 active_dma_count_ 与 DMA 完成/错误/中止回调在并发、重入或错误场景下的正确性,防止资源泄漏或重复释放。
  • HAL 与 DMA 中止交互:检查 HAL_SPI_ErrorCallback、HAL_DMA_IRQHandler 与 DMA 中止路径在不同错误条件下不会导致双重回调或遗漏清理。
  • EXTI 与硬件一致性:确认 INT1_ACC/INT1_GYRO 的物理连线确实为 active-low,下降沿配置符合器件手册。
  • 断言路径安全性:断言处理里直接操作 GPIO(点亮 LED)在异常/中断上下文下是否始终安全。

结论

该 PR 将 SPI1 异步传输切换为 DMA 并在 HAL/MSP、向量表、CubeMX 配置、驱动接口与上层实现间做了全面适配。总体设计合理,能有效降低高速 SPI 导致的 OVR 错误;审查重点应放在 DMA 资源分配冲突、回调并发与错误处理的正确性,以及 EXTI 与断言处理的上下文安全性。

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Walkthrough

在 STM32F4 平台引入 DMA(DMA2_Stream0/Stream3)并将 SPI1 异步传输切换为 DMA 路径,新增 DMA 初始化、IRQ 处理、SPI DMA 绑定及回调签名调整,同时修改若干 GPIO EXTI 边沿、断言指示和构建配置。

Changes

Cohort / File(s) Summary
HAL 项目与构建配置
firmware/c_board/bsp/HAL/.mxproject, firmware/c_board/bsp/HAL/rmcs_slave.ioc, firmware/c_board/bsp/HAL/cmake/stm32cubemx/CMakeLists.txt
.ioc 中加入 DMA 请求/流、NVIC 条目并重排 MCU IP;.mxproject 有新增后删除的段(净无影响);CMakeLists 将 Core/Src/dma.c 加入构建。
DMA 头与实现
firmware/c_board/bsp/HAL/Core/Inc/dma.h, firmware/c_board/bsp/HAL/Core/Src/dma.c
新增 MX_DMA_Init() 声明与实现:使能 DMA2 时钟,配置并使能 DMA2_Stream0DMA2_Stream3 的 NVIC 优先级与中断。
中断向量与处理
firmware/c_board/bsp/HAL/Core/Inc/stm32f4xx_it.h, firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c
新增 DMA2_Stream0_IRQHandlerDMA2_Stream3_IRQHandler,并 extern 出 hdma_spi1_rx/hdma_spi1_tx;处理函数调用 HAL_DMA_IRQHandler
SPI HAL 与 DMA 绑定
firmware/c_board/bsp/HAL/Core/Src/spi.c, firmware/c_board/src/spi/spi.hpp, firmware/c_board/src/spi/spi.cpp
为 SPI1 添加 hdma_spi1_rx/hdma_spi1_tx,在 MspInit/DeInit 中配置/反配置 DMA;驱动新增 DMA 初始化/触发、全局 DMA 回调与错误处理,将异步传输改为 DMA 驱动路径并维护 DMA 状态。
模块回调签名变更(C++ 层)
firmware/c_board/src/spi/bmi088/accel.hpp, firmware/c_board/src/spi/bmi088/gyro.hpp, firmware/rmcs_board/src/spi/bmi088/accel.hpp, firmware/rmcs_board/src/spi/bmi088/gyro.hpp, firmware/rmcs_board/src/spi/spi.hpp
将模块层 transmit_receive_async_callback 签名由 (rx_buffer, size) 改为仅 (size);模块改为使用 spi_.rx_buffer,并在 size==0 时跳过处理。
主循环、头文件与应用层调用
firmware/c_board/bsp/HAL/Core/Src/main.c, firmware/c_board/bsp/HAL/Core/Inc/main.h, firmware/c_board/src/app.cpp
main 中包含并调用 MX_DMA_Init()main.h 调整包含去重;主循环中加入 spi1->update() 并新增一次 USB 传输尝试调用。
GPIO EXTI 边沿调整
firmware/c_board/bsp/HAL/Core/Src/gpio.c
INT1_ACC_PinINT1_GYRO_Pin 的 EXTI 触发从上升沿改为下降沿(RISING → FALLING)。
断言/故障指示增强
firmware/c_board/src/utility/assert.cpp
断言路径加入中断锁并调用内部 force_led_red(),点亮红 LED 以指示断言/故障。

Sequence Diagram(s)

sequenceDiagram
    participant Init as 初始化
    participant DMA as DMA 控制器
    participant SPI as SPI1 外设
    participant IRQ as NVIC/IRQ

    Init->>DMA: MX_DMA_Init()
    DMA->>DMA: 使能 DMA2 时钟,配置 Stream0/Stream3 IRQ 优先级
    DMA->>IRQ: 使能 DMA 中断向量

    Init->>SPI: MX_SPI1_Init()
    SPI->>DMA: 配置 Stream0(RX) 与 Stream3(TX) 并链接到 SPI
    SPI->>DMA: 触发 DMA RX/TX 传输
    DMA->>IRQ: 传输完成或错误触发中断
    IRQ->>SPI: DMA2_Stream0/3_IRQHandler -> HAL_DMA_IRQHandler -> HAL 回调
    SPI->>Module: 驱动回调 -> module->transmit_receive_async_callback(size)
Loading
sequenceDiagram
    participant HAL as HAL 回调
    participant SpiDrv as Spi 驱动
    participant Module as SpiModule (加速计/陀螺)
    participant USB as USB 上行

    HAL->>SpiDrv: DMA 完成/错误回调 (hal_dma_handle)
    SpiDrv->>Module: module->transmit_receive_async_callback(size)
    Module->>Module: if size>0: 从 spi_.rx_buffer 解析数据
    Module->>USB: 通过 serializer 发送上行
    Module->>SpiDrv: spi_.unlock()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

Poem

🐰 我是小兔画电路线,DMA 勤快不知倦,
SPI 数据溜溜走,缓冲静候不再乱。
下降沿轻敲传感器,断言红灯夜中盼,
回调更短更利落,驱动合拍奏新弦。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题清晰、具体地总结了主要变更:将SPI1异步传输从中断模式切换到DMA模式以避免溢出错误。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/cboard-spi

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c (1)

339-350: 存在死代码:HAL_PCD_IRQHandler 永远不会被执行。

return 语句在第 343 行导致 HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS) 永远不会被调用。如果这是预期行为(TinyUSB 完全接管 USB 中断处理),建议删除或注释掉死代码以提高可读性。

♻️ 建议清理死代码
 void OTG_FS_IRQHandler(void)
 {
   /* USER CODE BEGIN OTG_FS_IRQn 0 */
   tusb_int_handler(0, true);
   return;
-
-  /* USER CODE END OTG_FS_IRQn 0 */
-  HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS);
-  /* USER CODE BEGIN OTG_FS_IRQn 1 */
-
-  /* USER CODE END OTG_FS_IRQn 1 */
+  /* USER CODE END OTG_FS_IRQn 0 */
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c` around lines 339 - 350,
OTG_FS_IRQHandler contains dead code because the early return after calling
tusb_int_handler(0, true) prevents HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS) from
ever executing; if TinyUSB should fully own the ISR, remove or comment out the
unreachable HAL_PCD_IRQHandler call and its surrounding USER CODE markers in
OTG_FS_IRQHandler, otherwise remove the early return and ensure tusb_int_handler
is only called conditionally so HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS) can run;
update the function OTG_FS_IRQHandler and references to tusb_int_handler and
hpcd_USB_OTG_FS accordingly to keep intent clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@firmware/c_board/src/spi/spi.hpp`:
- Around line 81-83: The async path currently conflates "0" as both a valid
transfer length and a failure signal; enforce tx_rx_size_ > 0 at the async
transfer entry (where the transfer is queued/scheduled) so zero-length requests
are rejected/returned early and never reach the async callback, and update
transmit_receive_async_callback to only call
module->transmit_receive_async_callback(tx_rx_size_) when success && tx_rx_size_
> 0 (otherwise treat as failure). If you prefer a clearer API, change the module
callback signature to accept an explicit success flag (e.g.,
transmit_receive_async_callback(bool success, size_t size)) and use
finish_transfer()/tx_rx_size_ to populate both fields.

---

Nitpick comments:
In `@firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c`:
- Around line 339-350: OTG_FS_IRQHandler contains dead code because the early
return after calling tusb_int_handler(0, true) prevents
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS) from ever executing; if TinyUSB should
fully own the ISR, remove or comment out the unreachable HAL_PCD_IRQHandler call
and its surrounding USER CODE markers in OTG_FS_IRQHandler, otherwise remove the
early return and ensure tusb_int_handler is only called conditionally so
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS) can run; update the function
OTG_FS_IRQHandler and references to tusb_int_handler and hpcd_USB_OTG_FS
accordingly to keep intent clear.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8651e16 and 248d1ca.

📒 Files selected for processing (18)
  • firmware/c_board/bsp/HAL/.mxproject
  • firmware/c_board/bsp/HAL/Core/Inc/dma.h
  • firmware/c_board/bsp/HAL/Core/Inc/stm32f4xx_it.h
  • firmware/c_board/bsp/HAL/Core/Src/dma.c
  • firmware/c_board/bsp/HAL/Core/Src/gpio.c
  • firmware/c_board/bsp/HAL/Core/Src/main.c
  • firmware/c_board/bsp/HAL/Core/Src/spi.c
  • firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c
  • firmware/c_board/bsp/HAL/cmake/stm32cubemx/CMakeLists.txt
  • firmware/c_board/bsp/HAL/rmcs_slave.ioc
  • firmware/c_board/src/spi/bmi088/accel.hpp
  • firmware/c_board/src/spi/bmi088/gyro.hpp
  • firmware/c_board/src/spi/spi.cpp
  • firmware/c_board/src/spi/spi.hpp
  • firmware/c_board/src/utility/assert.cpp
  • firmware/rmcs_board/src/spi/bmi088/accel.hpp
  • firmware/rmcs_board/src/spi/bmi088/gyro.hpp
  • firmware/rmcs_board/src/spi/spi.hpp

Improve on-board failure visibility in assert path.

- Initialize GPIOH in assert handler when needed
- Drive RGB LED to red before __builtin_trap()
- Disable interrupt in assert_func
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
firmware/c_board/bsp/HAL/Core/Inc/main.h (1)

35-36: 建议移除冗余的设备头 stm32f407xx.h 包含(第35行)。

stm32f4xx.h 作为 CMSIS 统一入口,已在构建配置中通过 STM32F407xx 编译宏自动激活该设备头的条件包含。直接包含 stm32f407xx.h 属于冗余,会增加芯片型号耦合。建议仅保留 stm32f4xx.h,这样日后迁移到同系列其他型号时,仅需修改编译宏定义,无需调整代码。

修正 diff
-#include "stm32f407xx.h"       // IWYU pragma: export
 `#include` "stm32f4xx.h"         // IWYU pragma: export
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/c_board/bsp/HAL/Core/Inc/main.h` around lines 35 - 36, Remove the
redundant device-specific include: delete the `#include` "stm32f407xx.h" line and
keep only `#include` "stm32f4xx.h" in main.h so the CMSIS umbrella header
(activated by the STM32F407xx compile macro) selects the device header; preserve
existing IWYU pragmas on the remaining include if needed.
firmware/c_board/src/spi/spi.hpp (1)

94-111: 建议在失败分支重置 active_dma_count_,避免残留 DMA 状态。

当前失败路径会清理 SPI/DMA,但没有把 active_dma_count_ 复位到 kDmaNotPerformed。这会让状态机留下陈旧计数,后续诊断和边界行为更难推断。

🔧 建议修改(示意)
     if (!success) [[unlikely]] {
+        active_dma_count_.store(kDmaNotPerformed, std::memory_order::relaxed);
         HAL_DMA_Abort(hal_spi_handle_->hdmarx);
         HAL_DMA_Abort(hal_spi_handle_->hdmatx);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/c_board/src/spi/spi.hpp` around lines 94 - 111, In
transmit_receive_async_callback(bool success) ensure that on the failure branch
you reset the DMA state by setting active_dma_count_ = kDmaNotPerformed before
returning/continuing; specifically, inside the if (!success) [[unlikely]] block
(or immediately after DMA aborts) update active_dma_count_ to kDmaNotPerformed
so the state machine and diagnostics no longer see a stale DMA count—adjust any
related logic in finish_transfer() expectations if needed.
firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c (1)

339-347: 建议清理 OTG_FS_IRQHandler 中的不可达代码,避免误导维护者。

Line 343 已直接 return,因此 Line 346 的 HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); 永远不会执行。若这是有意切到 TinyUSB 直连路径,建议删除不可达分支或补一条注释说明“故意绕过 HAL_PCD_IRQHandler”。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c` around lines 339 - 347, The
OTG_FS_IRQHandler function contains unreachable code because tusb_int_handler(0,
true); is followed by an immediate return, so
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); is never executed; either remove the
unreachable HAL_PCD_IRQHandler call or clearly document why the HAL path is
intentionally bypassed (e.g., add comment "intentionally using TinyUSB direct
handler, bypass HAL_PCD_IRQHandler"), and ensure the tusb_int_handler call and
return remain correct; update only the OTG_FS_IRQHandler function to remove
ambiguity by deleting the unreachable branch or adding the explanatory comment
referencing OTG_FS_IRQHandler, tusb_int_handler, HAL_PCD_IRQHandler, and
hpcd_USB_OTG_FS.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@firmware/c_board/src/spi/spi.hpp`:
- Around line 152-163: 目前对 HAL_DMA_Start_IT 的调用仅用 core::utility::assert_debug
校验,发布版会被去除导致失败时状态不一致并使 update() 无限等待。请在对 hal_spi_handle_->hdmarx 和
hal_spi_handle_->hdmatx 的两次 HAL_DMA_Start_IT 调用后增加显式错误检查:若返回值非 HAL_OK,应立即调用
transmit_receive_async_callback(false) 清理并恢复 active_dma_count_ / SPI
状态(确保与正常路径一样递减/重置),并避免继续执行启动后的流程;在检查位置引用 HAL_DMA_Start_IT、hal_spi_handle_,
transmit_receive_async_callback 和 active_dma_count_ 以定位修改点。

---

Nitpick comments:
In `@firmware/c_board/bsp/HAL/Core/Inc/main.h`:
- Around line 35-36: Remove the redundant device-specific include: delete the
`#include` "stm32f407xx.h" line and keep only `#include` "stm32f4xx.h" in main.h so
the CMSIS umbrella header (activated by the STM32F407xx compile macro) selects
the device header; preserve existing IWYU pragmas on the remaining include if
needed.

In `@firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c`:
- Around line 339-347: The OTG_FS_IRQHandler function contains unreachable code
because tusb_int_handler(0, true); is followed by an immediate return, so
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); is never executed; either remove the
unreachable HAL_PCD_IRQHandler call or clearly document why the HAL path is
intentionally bypassed (e.g., add comment "intentionally using TinyUSB direct
handler, bypass HAL_PCD_IRQHandler"), and ensure the tusb_int_handler call and
return remain correct; update only the OTG_FS_IRQHandler function to remove
ambiguity by deleting the unreachable branch or adding the explanatory comment
referencing OTG_FS_IRQHandler, tusb_int_handler, HAL_PCD_IRQHandler, and
hpcd_USB_OTG_FS.

In `@firmware/c_board/src/spi/spi.hpp`:
- Around line 94-111: In transmit_receive_async_callback(bool success) ensure
that on the failure branch you reset the DMA state by setting active_dma_count_
= kDmaNotPerformed before returning/continuing; specifically, inside the if
(!success) [[unlikely]] block (or immediately after DMA aborts) update
active_dma_count_ to kDmaNotPerformed so the state machine and diagnostics no
longer see a stale DMA count—adjust any related logic in finish_transfer()
expectations if needed.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 248d1ca and cc0c804.

📒 Files selected for processing (20)
  • firmware/c_board/bsp/HAL/.mxproject
  • firmware/c_board/bsp/HAL/Core/Inc/dma.h
  • firmware/c_board/bsp/HAL/Core/Inc/main.h
  • firmware/c_board/bsp/HAL/Core/Inc/stm32f4xx_it.h
  • firmware/c_board/bsp/HAL/Core/Src/dma.c
  • firmware/c_board/bsp/HAL/Core/Src/gpio.c
  • firmware/c_board/bsp/HAL/Core/Src/main.c
  • firmware/c_board/bsp/HAL/Core/Src/spi.c
  • firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c
  • firmware/c_board/bsp/HAL/cmake/stm32cubemx/CMakeLists.txt
  • firmware/c_board/bsp/HAL/rmcs_slave.ioc
  • firmware/c_board/src/app.cpp
  • firmware/c_board/src/spi/bmi088/accel.hpp
  • firmware/c_board/src/spi/bmi088/gyro.hpp
  • firmware/c_board/src/spi/spi.cpp
  • firmware/c_board/src/spi/spi.hpp
  • firmware/c_board/src/utility/assert.cpp
  • firmware/rmcs_board/src/spi/bmi088/accel.hpp
  • firmware/rmcs_board/src/spi/bmi088/gyro.hpp
  • firmware/rmcs_board/src/spi/spi.hpp
🚧 Files skipped from review as they are similar to previous changes (6)
  • firmware/c_board/bsp/HAL/Core/Src/main.c
  • firmware/c_board/bsp/HAL/cmake/stm32cubemx/CMakeLists.txt
  • firmware/c_board/bsp/HAL/Core/Src/spi.c
  • firmware/c_board/src/spi/bmi088/accel.hpp
  • firmware/c_board/bsp/HAL/Core/Src/gpio.c
  • firmware/c_board/bsp/HAL/Core/Inc/dma.h

… errors (#25)

The previous interrupt-based SPI async path could hit HAL_SPI_ERROR_OVR at high
SPI clock rates because RXNE was not serviced fast enough before the next byte.

- Replace HAL_SPI_TransmitReceive_IT with HAL_SPI_TransmitReceive_DMA
- Add SPI1 DMA resources (DMA2 Stream0/3), DMA init, and DMA IRQ handlers
- Wire CubeMX-generated DMA files into build and startup init sequence
- Add SPI error callback fallback to finish transfer and release CS/lock
- Fix BMI088 DRDY EXTI to falling edge (active-low configuration)
- Synchronize SPI async callback interface changes in firmware/rmcs_board
…reams finish (#25)

- Replace HAL_SPI_TransmitReceive_DMA usage with a SPI-owned minimal DMA trigger path
- Register SPI1 RX/TX DMA callbacks in Spi and start both streams directly
- Track active DMA stream count and finalize transfer only after RX+TX both complete
- Move async finalize trigger into main loop via spi::spi1->update()
- Keep HAL SPI callbacks as fail-fast/error fallback

This addresses an inherent issue in HAL_SPI_TransmitReceive_DMA completion behavior (RX-driven completion while TX DMA completion can lag), which can surface as HAL_ERROR under CAN RX interrupt pressure.
@qzhhhi
Copy link
Member Author

qzhhhi commented Feb 27, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c (1)

339-347: OTG_FS_IRQHandler 里存在不可达分支,建议收敛为单一路径。

Line 343 的 return 让 Line 346 的 HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS) 永远不会执行。建议明确只保留一种处理中断的实现,避免后续维护误判。

♻️ 建议精简(若目标是仅由 TinyUSB 处理中断)
 void OTG_FS_IRQHandler(void)
 {
   /* USER CODE BEGIN OTG_FS_IRQn 0 */
   tusb_int_handler(0, true);
-  return;
-
-  /* USER CODE END OTG_FS_IRQn 0 */
-  HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS);
+  /* USER CODE END OTG_FS_IRQn 0 */
   /* USER CODE BEGIN OTG_FS_IRQn 1 */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c` around lines 339 - 347,
OTG_FS_IRQHandler contains an unreachable branch because the call to
tusb_int_handler(0, true) is followed by an early return, so
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS) never runs; fix by choosing a single
interrupt handling path: either remove the return and let both handlers run (if
both are required) or remove the HAL_PCD_IRQHandler call (and its related code)
so only tusb_int_handler is used. Update OTG_FS_IRQHandler accordingly and
ensure no leftover comment blocks or dead code remain referencing
HAL_PCD_IRQHandler or hpcd_USB_OTG_FS.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@firmware/c_board/src/spi/spi.hpp`:
- Around line 93-112: In transmit_receive_async_callback, the debug assert on
success runs before the failure cleanup and can abort cleanup in Debug builds;
move the core::utility::assert_debug_lazy([&]() noexcept { return success; }) so
it executes after the failure-handling and resource-release steps (the
HAL_DMA_Abort calls, active_dma_count_.store(...), CLEAR_BIT on CR2, resetting
hal_spi_handle_->TxXferCount/RxXferCount/State, and the
finish_transfer()/module->transmit_receive_async_callback call) so that even
when success==false the DMA/SPI abort, state reset and finish_transfer
invocation always run before the debug-only fail-fast assertion. Ensure
references remain to transmit_receive_async_callback, HAL_DMA_Abort,
active_dma_count_, CLEAR_BIT, hal_spi_handle_->State, and finish_transfer().

---

Nitpick comments:
In `@firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c`:
- Around line 339-347: OTG_FS_IRQHandler contains an unreachable branch because
the call to tusb_int_handler(0, true) is followed by an early return, so
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS) never runs; fix by choosing a single
interrupt handling path: either remove the return and let both handlers run (if
both are required) or remove the HAL_PCD_IRQHandler call (and its related code)
so only tusb_int_handler is used. Update OTG_FS_IRQHandler accordingly and
ensure no leftover comment blocks or dead code remain referencing
HAL_PCD_IRQHandler or hpcd_USB_OTG_FS.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8651e16 and 11adb62.

📒 Files selected for processing (20)
  • firmware/c_board/bsp/HAL/.mxproject
  • firmware/c_board/bsp/HAL/Core/Inc/dma.h
  • firmware/c_board/bsp/HAL/Core/Inc/main.h
  • firmware/c_board/bsp/HAL/Core/Inc/stm32f4xx_it.h
  • firmware/c_board/bsp/HAL/Core/Src/dma.c
  • firmware/c_board/bsp/HAL/Core/Src/gpio.c
  • firmware/c_board/bsp/HAL/Core/Src/main.c
  • firmware/c_board/bsp/HAL/Core/Src/spi.c
  • firmware/c_board/bsp/HAL/Core/Src/stm32f4xx_it.c
  • firmware/c_board/bsp/HAL/cmake/stm32cubemx/CMakeLists.txt
  • firmware/c_board/bsp/HAL/rmcs_slave.ioc
  • firmware/c_board/src/app.cpp
  • firmware/c_board/src/spi/bmi088/accel.hpp
  • firmware/c_board/src/spi/bmi088/gyro.hpp
  • firmware/c_board/src/spi/spi.cpp
  • firmware/c_board/src/spi/spi.hpp
  • firmware/c_board/src/utility/assert.cpp
  • firmware/rmcs_board/src/spi/bmi088/accel.hpp
  • firmware/rmcs_board/src/spi/bmi088/gyro.hpp
  • firmware/rmcs_board/src/spi/spi.hpp

@qzhhhi qzhhhi merged commit 11adb62 into main Feb 27, 2026
5 checks passed
@github-project-automation github-project-automation bot moved this from Todo to Done in RMCS Slave SDK Feb 27, 2026
@qzhhhi qzhhhi deleted the fix/cboard-spi branch February 27, 2026 05:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant