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

145 lines
8.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.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`
- `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`
- `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_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.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_debug``rc_ctrl_debug``RemoteControlDebugPoll``RXINV``RxPinLevelInvert`、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.jdebug``ozonedeb/windebnewestux.jdebug.user` 是 Ozone/J-Link 调试器本地状态变化,包括探针序列号、打开窗口、布局、打开文件等,和代码逻辑无关,建议不要混进本次提交。
- `rc_valid_frame_count` 等计数是 `static volatile`,调试符号里一般能看到,但 C 代码外部不能直接引用;这不是接收链路问题,只是“是否方便 watch”的问题。
总体看SBUS 主链路相关 diff 没看到明显可疑残留;最需要清理的是 `buzzer.cpp``ozonedeb/*.jdebug*` 这些无关 dirty 文件。