Files
tronone-h7-scaffold/REMOTE_CONTROL_SBUS_FIX_NOTE.md
2026-07-14 21:58:07 +08:00

8.3 KiB
Raw Blame History

SBUS 遥控接收修复说明

这份记录用来说明这次从“遥控器收不到数据 / rc_ctrl 不变化”一路排查到当前稳定版本的主要修改点。写得偏简略重点放在为什么改、DMA 怎么配、哪些文件动过,以及最后对当前 git diff 的复核结论。

1. 一开始的问题和定位思路

最开始的现象是:遥控器链路没有让 rc_ctrl 更新,后面又观察到 rc_debug.rx_event_count 也不增长,说明问题不只是协议解析,而是 UART/DMA 接收事件本身没有稳定进入。

排查时按链路分层看:

  1. 初始化链路:发现遥控器模块没有在 RobotInit() 里真正初始化,所以补了 RemoteControlInit(&huart5)
  2. UART 协议配置:用户确认 SBUS 硬件已经做了反向,所以保持 UART5 为 100000 / 8E2,也就是 HAL 里的 UART_WORDLENGTH_9B + UART_PARITY_EVEN + UART_STOPBITS_2。当前没有启用软件 RXINV
  3. GPIO 电平配置UART5 RX 是 PD2当前保持 GPIO_NOPULL,没有继续保留“内部上拉”的实验配置。
  4. DMA 内存可访问性H7 上 DMA1/DMA2 不能访问 DTCM。原来接收对象如果落到 DTCM就可能导致 DMA 没法真正写入数据。现在把 USART 实例池和接收 buffer 放进 .dma_buffer 段,并链接到 RAM_D1。
  5. DMA 接收方式:原来的 UART5 RX DMA 是 DMA_CIRCULAR,但这里使用的是 HAL_UARTEx_ReceiveToIdle_DMA(),为了按实际一帧 25 字节触发并重启,改成 DMA_NORMAL + ReceiveToIdle
  6. 异常恢复:如果 UART/DMA 出错或重启失败,不能只在中断里死等。现在加了 USARTServiceTask(),在 daemon 任务上下文里做 stream 级恢复。
  7. SBUS 解析:只接受完整 25 字节帧,检查帧头 0x0F,尾字节允许 0x00/0x04/0x14/0x24/0x34,并处理 failsafe/offline。

后面临时试过的 RXINV、PD2 上拉、50 字节接收、滑动同步、rc_debug 等实验代码已经撤掉,当前版本回到“硬件反相 + 正常 SBUS 帧解析”的方案。

2. DMA 当前怎么配置

UART5 RX 的 DMA 配置在 Core/Src/usart.cTronOneH7_Scaffold.ioc 中保持一致:

  • UARTUART5
  • RX 引脚:PD2
  • DMA streamDMA1_Stream5
  • RequestDMA_REQUEST_UART5_RX
  • 方向:DMA_PERIPH_TO_MEMORY
  • 外设地址不自增:DMA_PINC_DISABLE
  • 内存地址自增:DMA_MINC_ENABLE
  • 外设/内存数据宽度:BYTE
  • 模式:DMA_NORMAL
  • 优先级:DMA_PRIORITY_VERY_HIGH
  • FIFO关闭

接收 buffer 的关键点:

  • USART_Instance usart_instance_pool[DEVICE_USART_CNT] 放在 User_Code/bsp/usart/bsp_usart.c
  • 这个池加了 __attribute__((section(".dma_buffer"), aligned(32)))
  • STM32H723XG_FLASH.ld 新增 .dma_buffer (NOLOAD) 段,并放到 RAM_D1,避免 DMA 访问 DTCM 失败。
  • USART_Instance.recv_buff 也做了 32 字节对齐。

启动和重启方式:

  • USARTServiceInit() 调用 HAL_UARTEx_ReceiveToIdle_DMA() 启动接收。
  • 启动成功后关闭 DMA 半传输中断 DMA_IT_HT,避免半包回调干扰。
  • HAL_UARTEx_RxEventCallback() 中记录 rx_event_countlast_rx_size,把实际收到的 Size 传给遥控器解析回调,然后重新启动接收。
  • HAL_UART_ErrorCallback() 中记录 uart_error_countlast_uart_error,并尝试重启接收。
  • 如果中断里重启失败,会置位/累计错误,后续由 USARTServiceTask() 在任务上下文里恢复 UART/DMA。

3. 主要加了什么,在哪里

  • User_Code/application/robot.c

    • 定义全局 volatile RobotMode_t RobotMode
    • RobotInit() 中调用 RemoteControlInit(&huart5)
  • Core/Src/usart.c

    • UART5 保持 100000 / 8E2
    • UART5 AdvancedInit 保持 UART_ADVFEATURE_NO_INIT,没有软件 RXINV。
    • PD2 保持 GPIO_NOPULL
    • UART5 RX DMA 从 DMA_CIRCULAR 改为 DMA_NORMAL
  • TronOneH7_Scaffold.ioc

    • 同步把 Dma.UART5_RX.13.Mode 改为 DMA_NORMAL
  • STM32H723XG_FLASH.ld

    • 新增 .dma_buffer 段,放入 RAM_D1
  • User_Code/bsp/usart/bsp_usart.c/.h

    • USART 实例不再 malloc,改用静态实例池,并放入 .dma_buffer
    • 模块回调改为携带 (USART_Instance *instance, const uint8_t *recv_data, uint16_t recv_size)
    • 加入 rx_event_countlast_rx_sizeuart_error_countlast_uart_errorrx_restart_error_count 等接收状态字段。
    • 加入 USARTServiceTask() 做 UART/DMA 恢复。
    • USARTServiceInit() 改为返回 HAL_StatusTypeDef
  • User_Code/module/periph/remote_control/rc.c/.h

    • RemoteControlInit() 注册 UART5并显式启动 USART 接收服务。
    • SBUS 解析只接受完整 25 字节帧。
    • 加入帧头、尾字节、failsafe 校验。
    • 加入 RemoteControlReadSnapshot(),用于原子读取当前遥控器快照。
    • 修复 RemoteControlIsOnline() 和失控清零的竞态。
    • 修复 SWB/SWC 通道、开关边沿标志。
  • Core/Src/freertos.c

    • daemon task 中调用 USARTServiceTask(),让 UART/DMA 异常恢复发生在任务上下文。
  • User_Code/module/software/daemon/daemon.c/.h

    • 修正 temp_count/init_count 初始化逻辑。
    • DaemonReload()DaemonIsOnline()Daemon_Update() 加了简单临界区保护。
    • temp_count 改为 volatile
  • User_Code/application/indicator_app/ws2812status.c

    • 不再用局部 RobotMode 遮蔽全局状态。
    • LED 显示时结合 RemoteControlIsOnline() 判断遥控器离线。
  • User_Code/module/paramdef/robot_def.h

    • RobotMode 声明改为 extern volatile RobotMode_t RobotMode

4. 当前建议上板观察项

上板后重点看这些量:

  • rx_event_count 是否持续增长。
  • last_rx_size 是否稳定为 25
  • rc_valid_frame_count 是否持续增长。
  • uart_error_countrx_restart_error_count 是否不持续增长。
  • 原始帧是否类似 0F ... 00/04/14/24/34

注意:当前稳定版本已经没有 rc_debug 这个临时调试结构。rx_event_countlast_rx_sizeUSART_Instance 里,rc_valid_frame_count 等在 rc.c 内部是 static volatile。如果后续想长期在调试器里直接 watch 一个固定结构,可以再专门加一个轻量 getter 或 debug struct当前为了回到干净版本没有保留那套临时代码。

5. Git 变更复核和可疑点

已读取当前 git status --shortgit diff --statgit diff --check 和关键文件 diff。当前工作区共有 16 个已修改文件,其中 SBUS 接收链路相关的是:

  • Core/Src/freertos.c
  • Core/Src/usart.c
  • STM32H723XG_FLASH.ld
  • TronOneH7_Scaffold.ioc
  • User_Code/application/indicator_app/ws2812status.c
  • User_Code/application/robot.c
  • User_Code/bsp/usart/bsp_usart.c
  • User_Code/bsp/usart/bsp_usart.h
  • User_Code/module/paramdef/robot_def.h
  • User_Code/module/periph/remote_control/rc.c
  • User_Code/module/periph/remote_control/rc.h
  • User_Code/module/software/daemon/daemon.c
  • User_Code/module/software/daemon/daemon.h

复核结论:

  • git diff --check 没有发现空白错误,只提示这些文件下次被 Git 处理时 LF 可能转 CRLF。
  • 没有发现 rc_debugrc_ctrl_debugRemoteControlDebugPollRXINVRxPinLevelInvert、50 字节 buffer、滑动同步等实验代码残留。
  • UART5 当前确实是 100000 / 8E2,没有软件 RXINVPD2 也是 GPIO_NOPULL
  • UART5 RX DMA 在 .ioc 和生成代码里都已经是 DMA_NORMAL,没有一边改一边没同步的问题。
  • RobotMode 目前只有一个定义,在 robot.c;其它地方通过 extern volatile 使用,没看到重复定义。

需要特别注意的可疑/无关变更:

  • User_Code/module/periph/buzzer/buzzer.cpp 只改了两行注释空格,和 SBUS 修复无关,建议不要混进本次提交。
  • ozonedeb/windebnewestux.jdebugozonedeb/windebnewestux.jdebug.user 是 Ozone/J-Link 调试器本地状态变化,包括探针序列号、打开窗口、布局、打开文件等,和代码逻辑无关,建议不要混进本次提交。
  • rc_valid_frame_count 等计数是 static volatile,调试符号里一般能看到,但 C 代码外部不能直接引用;这不是接收链路问题,只是“是否方便 watch”的问题。

总体看SBUS 主链路相关 diff 没看到明显可疑残留;最需要清理的是 buzzer.cppozonedeb/*.jdebug* 这些无关 dirty 文件。