From e5b4182a9f02dfe9f1f06dd58d086f64a157cc5c Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Thu, 29 Oct 2026 20:13:49 +0800 Subject: [PATCH] =?UTF-8?q?docs(=E5=B1=8F=E6=9D=BF):=20=E6=89=B9=201=20?= =?UTF-8?q?=E6=94=B6=E5=B0=BE=EF=BC=8C1-2/1-3=20=E5=88=A4=E6=8D=AE?= =?UTF-8?q?=E5=9B=9E=E5=A1=AB=E5=B9=B6=E8=AE=A2=E6=AD=A3=E4=B8=89=E5=A4=84?= =?UTF-8?q?=E9=94=99=E5=8F=A3=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1-2 / 1-3 已于 2026-10-29 完成,判据按实测回填。同时订正原稿三处 会把人带错的口径: 1. 1-2 原写"改 .ioc 而不是只改 Src/usart.c,否则 CubeMX 重生成会丢"—— 这条路不存在:.ioc 不保存任何 UART 的波特率(只有 USART1.IPParameters=VirtualMode),GUI 里改也不落盘。实际做法是在 Src/usart.c 的 USER CODE BEGIN USART1_Init 2 区覆盖后重调一次 HAL_UART_Init,重生成会保留(KeepUserCode)。 2. 1-3 原写"本链路要自写这三个回调"——HAL_UARTEx_RxEventCallback 与 HAL_UART_ErrorCallback 已经有唯一实现(rs485.c),再写一份就是链接期 multiple definition。正确做法是各加一行转发。 3. §1.3 同一处说法同步订正。 另补两条台架实测口径(都会让人误判成"链路坏了"): - 错误回调里不能无条件重挂:NE/FE/PE 是非阻塞型,HAL 不停接收,再调 ReceiveToIdle_IT 只会拿到 HAL_BUSY(实测 err=160/rearm=160 同步涨)。 - PC + 杜邦线顶替屏板时 NE 每帧约 1.3 次,但 crc=0、ok==rx —— 数据完好, 属"能用的告警";真出问题的是 ORE。 §4.1 模块清单、§5 台架步骤(改用 test/host/screenlink_bench.py)、§0 的执行顺序结论一并更新。 --- docs/任务规划/屏板链路.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/任务规划/屏板链路.md b/docs/任务规划/屏板链路.md index d3822ebb..58308632 100644 --- a/docs/任务规划/屏板链路.md +++ b/docs/任务规划/屏板链路.md @@ -23,7 +23,7 @@ **执行顺序因此是**:挪台架线 → 迁日志口 → 才开始写屏板链路。 前两步**已于 2026-10-29 完成**(§3 批 0 的 0-1 / 0-2:台架线接 CN1 的 RS232#1,日志口迁到 UART5, 台架串口由 `COM6` 变成 `COM3`,`.serialtest/config.json` 已同步);第三项 0-3 的接法也已落纸。 -**批 0 到此结束,下一步进批 1。** +**批 0 与批 1 均已收尾(2026-10-29):USART1 已在 700 kbaud 上稳定收帧,下一步进批 2(帧 B 组帧 + TX DMA 起发)。** --- @@ -74,9 +74,11 @@ > `HAL_DMA_IRQHandler(&hdma_usart1_tx)`。在用的是 SPI1(DMA2 Stream0/3)、SPI2(DMA1)、ADC1(Stream4) > ——**与 Stream7 不冲突**,不必重新分配流,`.ioc` 也不用为 DMA 改任何东西(1-2 只改波特率)。 > 主机侧 RX 仍然只要 16 B + 空闲中断(屏板只发 6 B),**不要开 RX DMA**。 -> **三个收尾回调全工程都没人实现**:`HAL_UART_TxCpltCallback` / `HAL_UART_RxCpltCallback` 零实现, -> `HAL_UART_ErrorCallback` 只有一处、且只分发另一条通道的句柄(定位:全库搜这三个名字)→ 本链路要**自写这三个、 -> 按 `huart` 句柄分发**,错误分支里顺手清掉 USART1 的 ORE / NE / FE 再重挂接收(与 1-3 同一手法)。 +> **三个收尾回调的现状(2026-10-29 核实)**:`HAL_UART_TxCpltCallback` / `HAL_UART_RxCpltCallback` +> 零实现(批 2 的 2-4 要写 TxCplt);`HAL_UART_ErrorCallback` 与 `HAL_UARTEx_RxEventCallback` +> **已经有一处实现**——`Drivers/BSP/RS485/rs485.c`,只分发它自己那个句柄。所以本链路**不是"自写这三个"**: +> 全局回调一个工程只能有一份定义,正确做法是**在那两处各加一行转发**,按句柄过滤放在 `sb_link` 里 +> (1-3 已按这个做法落地)。错误分支里清 ORE / NE / FE 再按需重挂。 > **`.ioc` 在仓库根**(`STM32F407-Demo.ioc`,不在 `MDK-ARM/` 下):`Dma.Request0/1` 已列 `USART1_TX` / `USART1_RX`, > `DMA2_Stream7` 的 NVIC 优先级 9——与 `Src/dma.c`、`Src/stm32f4xx_it.c` 三处互相自洽(换流会被打回优先级 0,须回 `Src/dma.c` 核对)。 @@ -91,7 +93,7 @@ | 项 | 结论 | |---|---| | 端口 | **USART1**——唯一一条 MCU 引脚直出 TTL、不经收发器的通道,同时引到 U5 子板连接器 | -| 波特率 | 700000 8N1,`BRR = 0x0078`(APB2 84 MHz → `84 000 000 / (16 × 700 000) = 7.5`,误差 0 %) | +| 波特率 | 700000 8N1,`BRR = 0x0078`(APB2 84 MHz → `84 000 000 / (16 × 700 000) = 7.5`,误差 0 %)。**2026-10-29 开机日志实测就是 `0x0078`**(1-2) | | 方向控制 | **不需要**——TTL 直连是 TX / RX 两根独立线(全双工),没有方向切换 | | 剩余子决策 | **USART1 那端怎么接**:TTL 直连屏板 UART2 侧(PA2/PA3,绕过屏板自己的收发器)。**台架按这条做**;产品形态的线束长度与隔离需求届时单独评估 | @@ -244,12 +246,26 @@ | 序 | 任务 | 改动点 | 判据 | |---|---|---|---| | 1-1 ✅ | **协议层模块(纯逻辑,可主机单测)** | 新建 `Drivers/BSP/ScreenLink/sb_proto.c/h`:CRC-16/MODBUS(与屏板同一多项式,用 16 位合并表)、帧 A 解析、帧 B 组帧(普通图 / LOGO)、帧 B 自校验;双端注册:uvprojx 加 File + IncludePath,模拟器侧走 `scripts/check_registration.py` 的 MCU-only 豁免(纯逻辑,模拟器无消费路径) | **已完成 2026-10-29**:主机单测 **59/59 PASSED**(`test/host/sb_proto/`),覆盖率 74/74 行、6/6 函数;屏板实测向量 `01 03 01 00 F0 48` 与 `01 03 00 09 31 DE` 原样解析通过;组出的两种帧按屏板 `Check_CRC` 口径复核均被接受;Keil 构建 **0 错误 0 警告**(`Code` 不变——尚无调用者,链接器按未用节丢弃) | -| 1-2 | **波特率与格式落地** | `STM32F407-Demo.ioc` 里把 **USART1** 改 **700000 / 8N1 / 16 倍过采样**(改 `.ioc` 而不是只改 `Src/usart.c`,否则 CubeMX 重生成会丢);`Src/usart.c` 只写 USER CODE | 示波器/逻辑分析仪实测位宽误差 **< 1 %**;理论值 `USARTDIV = 7.5`、`BRR = 0x0078`(APB2 84 MHz),误差 0 % | -| 1-3 | **USART1 收中断与全局回调分支** | 开 IDLE 中断接收(缓冲 **16 B**);`HAL_UARTEx_RxEventCallback` 里**加 USART1 分支**(现有回调只把事件分发给另一条在用通道的句柄,不加分支会静默收不到);`HAL_UART_ErrorCallback` 同样要加,且**必须清 ORE/NE/FE 并重挂接收**(`__HAL_UART_CLEAR_OREFLAG` 或读 SR 再读 DR) | 屏板每 280 ms 发来的 6 B 能稳定收到,`Size == 6`,CRC 通过率 100 %;**拔插一次 TX 线制造噪声后能自动恢复**(不清 ORE 会永久锁死) | +| 1-2 ✅ | **波特率与格式落地** | **改 `Src/usart.c` 的 `USER CODE BEGIN USART1_Init 2` 区**(覆盖 `huart1.Init.BaudRate` 后重调一次 `HAL_UART_Init`)——**`.ioc` 这条路不存在**:`.ioc` 不保存任何 UART 的波特率(只有 `USART1.IPParameters=VirtualMode`),GUI 里改也不落盘,与 IWDG 那些"写进 `.ioc` 才不怕重生成"的情形不同 | **已完成 2026-10-29**:开机日志实测 `BRR=0x0078`(APB2 84 MHz / (16 × 700 kHz) = 7.5 → 整数 7、小数 0.5×16 = 8,误差 **0 %**),与理论值逐位相符,不必再架示波器;700 kbaud 下 10 分钟收帧零 CRC 错 | +| 1-3 ✅ | **USART1 收中断与全局回调分支** | 新建 `Drivers/BSP/ScreenLink/sb_link.c/h`:开 IDLE 中断接收(缓冲 **16 B**);`HAL_UARTEx_RxEventCallback` / `HAL_UART_ErrorCallback` 里**各加一行转发**(两个回调是全局单例,唯一实现在 `rs485.c`,按句柄过滤在 `sb_link` 内做);错误分支**清 ORE/NE/FE/PE 并按需重挂** | **已完成 2026-10-29**(PC + USB 转 TTL 顶替屏板,`COM6` @700000):`rx == 发送帧数`、`ok == rx`、`crc=0`、`len=0`、`size=6`,`keys` 随按键帧递增;先用错波特率灌 64 B 垃圾(`len=31`、`bits=0x06`)后**自动恢复**,`rearm=0`。主机单测 **81/81 PASSED**(`test/host/sb_link/`),Keil **0 错误 0 警告** | > ⚠ **`HAL_UARTEx_RxEventCallback` / `HAL_UART_ErrorCallback` 是全局单例**——全工程只有一处实现, > 现在只按它自己那个 `huart` 句柄分发(定位:全库搜这两个回调名就能找到实现处)。 > USART1 必须加自己的分支,否则中断进来了但没人处理——表象是"屏板一直超时",很容易误判成协议错。 +> **分支只能"加一行转发",不能"自写一份"**:再定义一次就是链接期 multiple definition。 + +> ⚠ **错误回调里不能无条件重挂**(2026-10-29 台架实测踩到):HAL 把错误分成两类—— +> **ORE 是阻塞型**(先 `UART_EndRxTransfer` 把 `RxState` 打回 READY,再回调,必须重挂); +> **NE/FE/PE 是非阻塞型**(接收照跑,`RxState` 仍是 `BUSY_RX`,此时再调 `ReceiveToIdle_IT` +> 只会拿到 `HAL_BUSY`)。旧写法无条件重挂,于是台架上出现 `err=160 / rearm=160` 完全同步增长—— +> 看着像"每帧都重挂失败、链路快死了",其实一次都没真失败。判据用 `RxState == HAL_UART_STATE_READY` +> 而不是错误位:它直接回答"现在挂着接收没有"。主机单测 `TC-SBLINK-015` 钉住了这条。 + +> ⚠ **PC + 杜邦线顶替屏板时会有 NE(噪声错)**:实测 `bits=0x02`、每帧约 1.3 次, +> 但 `crc=0` 且 `ok == rx`——数据完好,只是采样点被判噪声(线材无屏蔽、无终端匹配)。 +> 判读口径写进了 `sb_link.h` 的 `err_bits` 注释:**NE/FE 而 `rx_ok` 照涨 = 能用的告警,不是故障**; +> 真出问题的是 ORE(`0x08`)——那是处理不过来,本链路 6 B/280 ms 不该出现。 +> 台架上没造出 ORE(CPU 读 DR 太快),那条路径由主机单测按 RM0090 的清除语义覆盖。 ### 批 2 · 协议实现(5 项) @@ -288,8 +304,9 @@ ``` Drivers/BSP/ScreenLink/ -├── sb_proto.c/h 纯逻辑:CRC-16、帧 A 解析、帧 B 组帧 ← 可主机单测 -└── sb_link.c/h 传输:USART1 中断起发 + TX DMA、组帧入口、状态灯映射表 ← 依赖 HAL,仅 MCU +├── sb_proto.c/h 纯逻辑:CRC-16、帧 A 解析、帧 B 组帧 ← 可主机单测(1-1 ✅) +└── sb_link.c/h 传输:USART1 收中断 + ORE 清除 + 统计上报 ← 依赖 HAL,仅 MCU(1-3 ✅) + (批 2 再往里加:组帧入口、TX DMA 起发、状态灯映射表) ``` **为什么拆两层**:协议层不碰 HAL 才能在 `test/host/` 里跑单测(项目既有做法:原样 include 生产 `.c`, @@ -368,7 +385,7 @@ stub 提供 HAL)。屏板侧 CRC 有查表实现,**用同一张表做交叉 |---|---|---|---| | 0 | **腾位置**:串口线挪到 RS-232 侧、日志迁走 | 改接串口线 + 一个 **USB 转 RS-232 适配器** | USART1 空出来,日志照旧能看(批 0) | | 1 | 协议层单测(`test/host/screenlink/`) | 无硬件 | CRC 与组帧的正确性,可回归 | -| 2 | PC 串口助手**模拟屏板**:手发 6 B 召唤帧,看主机回的 4208 B | **USB 转 TTL 模块**接 USART1(PA9/PA10)+ 主机板 | 主机侧协议 100 % 可验证 | +| 2 | PC **模拟屏板**:按屏板节拍发 6 B 召唤帧(脚本 `test/host/screenlink_bench.py`,`--port COM6 --key 5`;`--glitch-baud 115200` 可灌噪声验自愈) | **USB 转 TTL 模块**接 U5 的 `ST_RX0`/`ST_TX0`(PA10/PA9)+ 主机板 | 批 1 只验**收**(已做:`ok == rx`、`crc=0`、`size=6`、噪声后自愈);批 2 再验主机回的 4208 B | | 3 | 反向:PC 收主机帧,用脚本核对 4208 B / CRC / 显存内容 | 同上 | 帧内容正确性(与 `g_ui_fbuf` 逐字节比) | | 4 | **接真屏板**(TTL 直连屏板 UART2 的 PA2/PA3 一侧) | 屏板实物 + 屏板 TTL 引出 | 出图 + 按键 + 状态灯全链路 | | 5 | 产品形态:按最终线束接法定版 | 线束 | 产品形态验证 |