8.3 KiB
SBUS 遥控接收修复说明
这份记录用来说明这次从“遥控器收不到数据 / rc_ctrl 不变化”一路排查到当前稳定版本的主要修改点。写得偏简略,重点放在为什么改、DMA 怎么配、哪些文件动过,以及最后对当前 git diff 的复核结论。
1. 一开始的问题和定位思路
最开始的现象是:遥控器链路没有让 rc_ctrl 更新,后面又观察到 rc_debug.rx_event_count 也不增长,说明问题不只是协议解析,而是 UART/DMA 接收事件本身没有稳定进入。
排查时按链路分层看:
- 初始化链路:发现遥控器模块没有在
RobotInit()里真正初始化,所以补了RemoteControlInit(&huart5)。 - UART 协议配置:用户确认 SBUS 硬件已经做了反向,所以保持 UART5 为
100000 / 8E2,也就是 HAL 里的UART_WORDLENGTH_9B + UART_PARITY_EVEN + UART_STOPBITS_2。当前没有启用软件RXINV。 - GPIO 电平配置:UART5 RX 是 PD2,当前保持
GPIO_NOPULL,没有继续保留“内部上拉”的实验配置。 - DMA 内存可访问性:H7 上 DMA1/DMA2 不能访问 DTCM。原来接收对象如果落到 DTCM,就可能导致 DMA 没法真正写入数据。现在把 USART 实例池和接收 buffer 放进
.dma_buffer段,并链接到 RAM_D1。 - DMA 接收方式:原来的 UART5 RX DMA 是
DMA_CIRCULAR,但这里使用的是HAL_UARTEx_ReceiveToIdle_DMA(),为了按实际一帧 25 字节触发并重启,改成DMA_NORMAL + ReceiveToIdle。 - 异常恢复:如果 UART/DMA 出错或重启失败,不能只在中断里死等。现在加了
USARTServiceTask(),在 daemon 任务上下文里做 stream 级恢复。 - SBUS 解析:只接受完整 25 字节帧,检查帧头
0x0F,尾字节允许0x00/0x04/0x14/0x24/0x34,并处理 failsafe/offline。
后面临时试过的 RXINV、PD2 上拉、50 字节接收、滑动同步、rc_debug 等实验代码已经撤掉,当前版本回到“硬件反相 + 正常 SBUS 帧解析”的方案。
2. DMA 当前怎么配置
UART5 RX 的 DMA 配置在 Core/Src/usart.c 和 TronOneH7_Scaffold.ioc 中保持一致:
- UART:
UART5 - RX 引脚:
PD2 - DMA stream:
DMA1_Stream5 - Request:
DMA_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_count、last_rx_size,把实际收到的Size传给遥控器解析回调,然后重新启动接收。HAL_UART_ErrorCallback()中记录uart_error_count、last_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。
- UART5 保持
-
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_count、last_rx_size、uart_error_count、last_uart_error、rx_restart_error_count等接收状态字段。 - 加入
USARTServiceTask()做 UART/DMA 恢复。 USARTServiceInit()改为返回HAL_StatusTypeDef。
- USART 实例不再
-
User_Code/module/periph/remote_control/rc.c/.hRemoteControlInit()注册 UART5,并显式启动 USART 接收服务。- SBUS 解析只接受完整 25 字节帧。
- 加入帧头、尾字节、failsafe 校验。
- 加入
RemoteControlReadSnapshot(),用于原子读取当前遥控器快照。 - 修复
RemoteControlIsOnline()和失控清零的竞态。 - 修复 SWB/SWC 通道、开关边沿标志。
-
Core/Src/freertos.c- daemon task 中调用
USARTServiceTask(),让 UART/DMA 异常恢复发生在任务上下文。
- daemon task 中调用
-
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.hRobotMode声明改为extern volatile RobotMode_t RobotMode。
4. 当前建议上板观察项
上板后重点看这些量:
rx_event_count是否持续增长。last_rx_size是否稳定为25。rc_valid_frame_count是否持续增长。uart_error_count、rx_restart_error_count是否不持续增长。- 原始帧是否类似
0F ... 00/04/14/24/34。
注意:当前稳定版本已经没有 rc_debug 这个临时调试结构。rx_event_count 和 last_rx_size 在 USART_Instance 里,rc_valid_frame_count 等在 rc.c 内部是 static volatile。如果后续想长期在调试器里直接 watch 一个固定结构,可以再专门加一个轻量 getter 或 debug struct;当前为了回到干净版本,没有保留那套临时代码。
5. Git 变更复核和可疑点
已读取当前 git status --short、git diff --stat、git diff --check 和关键文件 diff。当前工作区共有 16 个已修改文件,其中 SBUS 接收链路相关的是:
Core/Src/freertos.cCore/Src/usart.cSTM32H723XG_FLASH.ldTronOneH7_Scaffold.iocUser_Code/application/indicator_app/ws2812status.cUser_Code/application/robot.cUser_Code/bsp/usart/bsp_usart.cUser_Code/bsp/usart/bsp_usart.hUser_Code/module/paramdef/robot_def.hUser_Code/module/periph/remote_control/rc.cUser_Code/module/periph/remote_control/rc.hUser_Code/module/software/daemon/daemon.cUser_Code/module/software/daemon/daemon.h
复核结论:
git diff --check没有发现空白错误,只提示这些文件下次被 Git 处理时 LF 可能转 CRLF。- 没有发现
rc_debug、rc_ctrl_debug、RemoteControlDebugPoll、RXINV、RxPinLevelInvert、50 字节 buffer、滑动同步等实验代码残留。 - UART5 当前确实是
100000 / 8E2,没有软件 RXINV;PD2 也是GPIO_NOPULL。 - UART5 RX DMA 在
.ioc和生成代码里都已经是DMA_NORMAL,没有一边改一边没同步的问题。 RobotMode目前只有一个定义,在robot.c;其它地方通过extern volatile使用,没看到重复定义。
需要特别注意的可疑/无关变更:
User_Code/module/periph/buzzer/buzzer.cpp只改了两行注释空格,和 SBUS 修复无关,建议不要混进本次提交。ozonedeb/windebnewestux.jdebug和ozonedeb/windebnewestux.jdebug.user是 Ozone/J-Link 调试器本地状态变化,包括探针序列号、打开窗口、布局、打开文件等,和代码逻辑无关,建议不要混进本次提交。rc_valid_frame_count等计数是static volatile,调试符号里一般能看到,但 C 代码外部不能直接引用;这不是接收链路问题,只是“是否方便 watch”的问题。
总体看,SBUS 主链路相关 diff 没看到明显可疑残留;最需要清理的是 buzzer.cpp 和 ozonedeb/*.jdebug* 这些无关 dirty 文件。