feat(屏板): 帧 B 组帧 + 中断起发 TX DMA + 按键队列 + 离线判定(批 2)
批 2 五项全部落地(docs/任务规划/屏板链路.md §3): - 2-1 sb_link_frame_update():uiTask 渲染后按需组帧,不留 3200 B 影子 副本,直接拿活显存与帧内点阵段 memcmp;组完自校验,过了才置 ready。 静态 4208 B 帧缓冲实测落 RW_IRAM1(0x20006d98),不在 CCM。 - 2-2 空闲中断校完 6 B 召唤帧直接点 TX DMA(sb_tx_kick),不排队不攒批; busy 期间再来的召唤丢帧并计数,绝不叠发(屏板收帧一次 IDLE 一包)。 - 2-3 按键走环形队列(中断入、任务出),uiTask 按 §4.3 表映射 UI_KEY_* 后调 ui_port_queue_key;9/10(RESET/FACTORY)按决策四先丢弃并留痕。 - 2-4 HAL_UART_TxCpltCallback 在 rs485.c 加第三行转发(三个全局回调 落点收一处,防日后重复定义);busy 冻结帧缓冲,错误中断按 gState 收拾被打断的 TX,防残留 busy 把链路永久锁死。 - 2-5 连续 >=10 s 无有效召唤帧才判离线(不按 500 ms——屏板约 5000 s 自复位有 1 s 空窗),恢复即清标志;CRC 错帧静默丢弃并计数。 实测:台架 40 s(PC 顶替屏板,--key-fixed 2 持续改画面)rx=143 ok=143 keys=143 crc=0 / tx=143 done=143 drop=0 abor=0 fb=144 bad=0;脚本一停 10 s 准点出 OFFLINE。组帧耗时 509 µs 典型 / 709 µs 峰值——原 0.3 ms 判据系估算,已按实测在规划文档改口为 1 ms 并给出压降路径(未做)。 附带:rs485_on_rx 补 CODE-RULE-09 文档块(本轮改 rs485.c 触发整文件 重扫被拦);CRC 循环计数改 32 位(消掉 ARMCC 每轮 UXTH);bench 脚本 加 --reply/--key-fixed/--dump,并写明 CH340 收不了 700 kbaud 连续流、 --reply 收不全 4208 B 不是固件问题。 主机单测 146/146 PASSED(+65);Keil 0 错误 0 警告;ci_local 全绿 (SKIP=nested-comment,worktree 限制,主树脚本另查 0 新发)。 Code=238,936 B / RW_IRAM1 65.5 % / RW_CCM 87.9 %。
This commit is contained in:
+38
-13
@@ -23,7 +23,8 @@
|
||||
**执行顺序因此是**:挪台架线 → 迁日志口 → 才开始写屏板链路。
|
||||
前两步**已于 2026-10-29 完成**(§3 批 0 的 0-1 / 0-2:台架线接 CN1 的 RS232#1,日志口迁到 UART5,
|
||||
台架串口由 `COM6` 变成 `COM3`,`.serialtest/config.json` 已同步);第三项 0-3 的接法也已落纸。
|
||||
**批 0 与批 1 均已收尾(2026-10-29):USART1 已在 700 kbaud 上稳定收帧,下一步进批 2(帧 B 组帧 + TX DMA 起发)。**
|
||||
**批 0 / 批 1 / 批 2 均已收尾(2026-10-29):USART1 在 700 kbaud 上收发双向通了(收 6 B 帧 A、
|
||||
回 4208 B 帧 B),下一步进批 3(状态灯映射表 + LOGO 下发)。**
|
||||
|
||||
---
|
||||
|
||||
@@ -74,11 +75,14 @@
|
||||
> `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**。
|
||||
> **三个收尾回调的现状(2026-10-29 核实)**:`HAL_UART_TxCpltCallback` / `HAL_UART_RxCpltCallback`
|
||||
> 零实现(批 2 的 2-4 要写 TxCplt);`HAL_UART_ErrorCallback` 与 `HAL_UARTEx_RxEventCallback`
|
||||
> **三个收尾回调的现状(2026-10-29 核实,2-4 已落地)**:`HAL_UART_RxCpltCallback` 仍零实现(本链路不用
|
||||
> 逐字节接收,走 IDLE 断帧);`HAL_UART_ErrorCallback` 与 `HAL_UARTEx_RxEventCallback`
|
||||
> **已经有一处实现**——`Drivers/BSP/RS485/rs485.c`,只分发它自己那个句柄。所以本链路**不是"自写这三个"**:
|
||||
> 全局回调一个工程只能有一份定义,正确做法是**在那两处各加一行转发**,按句柄过滤放在 `sb_link` 里
|
||||
> (1-3 已按这个做法落地)。错误分支里清 ORE / NE / FE 再按需重挂。
|
||||
> **`HAL_UART_TxCpltCallback` 原本零实现,2-4 也是在 `rs485.c` 加第三行转发**(而不是在 `sb_link.c`
|
||||
> 自写一份)——三个全局回调的落点收在同一处,下一个加通道的人一眼能看到全部,不必猜
|
||||
> "TxCplt 有没有被占"。
|
||||
> **`.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` 核对)。
|
||||
|
||||
@@ -267,15 +271,31 @@
|
||||
> 真出问题的是 ORE(`0x08`)——那是处理不过来,本链路 6 B/280 ms 不该出现。
|
||||
> 台架上没造出 ORE(CPU 读 DR 太快),那条路径由主机单测按 RM0090 的清除语义覆盖。
|
||||
|
||||
### 批 2 · 协议实现(5 项)
|
||||
### 批 2 · 协议实现(5 项,2026-10-29 全部收尾)
|
||||
|
||||
| 序 | 任务 | 改动点 | 判据 |
|
||||
|---|---|---|---|
|
||||
| 2-1 | **帧 B 组帧(在 `uiTask` 里做)** | `sb_link.c` 暴露一个「组一帧」入口,由 `uiTask` 在显存 / LED 掩码变化时调用(**不在中断里组帧**):取 `ui_net_sink_get_lcdmem()` 的 3200 B → 拼 4 B 状态灯 → `info[4..5]` 填非 LOGO 值 → `info[6..7]` 保留 → **998 B 填充(固定 0x00)** → 2 B CRC(覆盖 `[0, 4205]`);目标缓冲 = 静态 4208 B,**落 SRAM、不落 CCM** | 长度 **4208 B**;CRC 覆盖范围正确;填充字节全 0;单帧组帧耗时实测 **≤0.3 ms**;`.map` 里该缓冲不在 CCM 段 |
|
||||
| 2-2 | **应答时序(起发在中断里)** | USART1 空闲中断校完 6 B 召唤帧后**直接点 TX DMA** 发送已经组好的帧(决策三);不排队、不攒批、**不在任务里逐字节轮询发送** | 逻辑分析仪:召唤帧末位 → 应答首位 **30 µs ~ 5 ms**;整帧 **59.6 ms ±0.5 ms** 连续无断裂、**期间无 2 个字符时间(约 28 µs)以上的间隙**;整帧在 **≤200 ms** 内结束 |
|
||||
| 2-3 | **按键上行** | 帧 A 的 `key` 字节按屏板 `KEY_TYPE` 查表 → `ui_net_sink_inject_key(UI_KEY_*)`;`0` = 无键直接返回(**注键由 `uiTask` 做,不在中断里**) | 7 个物理键逐个按下,UI 动作与 `docs/产品需求/产品需求设计文档.md` §8.2 菜单树一致;逐键必到、不看统计均值 |
|
||||
| 2-4 | **帧缓冲归属与 DMA 收尾** | **不新建任务**(决策三):DMA 传完中断(`DMA2_Stream7_IRQn`,`Src/stm32f4xx_it.c`:644-652 已接)清 busy——**`HAL_UART_TxCpltCallback` 全工程没人实现,要在本模块自写并按 `huart` 句柄分发**(§1.3);busy 期间冻结帧缓冲,`uiTask` 有新内容就置 dirty、传完再重组(或双缓冲);缓冲、起发入口、收尾全收在 `sb_link.c` | 连续 30 min:无双发、无脏帧、组帧自校验 100 %;`[PROF]` 里 `uiTask` 单轮工作耗时增量 **≤0.3 ms**;拔掉屏板后不再触发、无残留 busy |
|
||||
| 2-5 | **链路监视与容错** | 连续 **≥10 s** 无召唤帧才判"屏板离线"(**不能按 500 ms 判**——屏板 ≈5000 s 会自复位一次,约 1 s 空窗);CRC 错帧静默丢弃并计数 | 拔掉屏板 10 s 后置离线标志;插回自动恢复;屏板自复位不被误判 |
|
||||
| 2-1 ✅ | **帧 B 组帧(在 `uiTask` 里做)** | `sb_link_frame_update(p_live_bmp, led_mask)`:取活帧缓冲的 3200 B → 拼 4 B 状态灯 → `info[4..5]` 填非 LOGO 值 → `info[6..7]` 保留 → **998 B 填充(固定 0x00)** → 2 B CRC(覆盖 `[0, 4205]`);缓冲 = 静态 4208 B,**落 `RW_IRAM1`、不落 CCM**。**不留 3200 B 影子副本**:帧里的点阵段本身就是上次组进去的那份,直接跟活显存 memcmp,变了才付 CRC | **已完成 2026-10-29**:长度 4208 / 填充全 0 / CRC 覆盖范围由主机单测逐项钉住(`TC-SBLINK-017`);`.map` 实测 `s_frame 0x20006d98 Data 4208`,在 `Execution Region RW_IRAM1`(0x20000000 起),不在 `RW_CCM`(0x10000000);台架 144 帧 `bad=0`(自校验 100 %);**组帧耗时实测 509 µs 典型 / 709 µs 峰值**——原判据 0.3 ms 已按下条改口 |
|
||||
| 2-2 ✅ | **应答时序(起发在中断里)** | USART1 空闲中断校完 6 B 召唤帧后**直接点 TX DMA**(`sb_tx_kick()`,决策三);不排队、不攒批、不在任务里逐字节轮询。上一帧没发完就丢这一轮(`drop` 计数),**绝不叠发** | **已完成 2026-10-29**:台架 143 次召唤 → `tx=143 done=143 drop=0 kerr=0 abor=0`;未组帧前召唤**不发**(`TC-SBLINK-019`)。⚠ 三条时序判据(末位→首位 30 µs~5 ms、整帧 59.6 ms 无间隙、≤200 ms)**台架这一级证不了**:PC 侧 USB 转 TTL 是 CH340,收不了 700 kbaud 连续流,且 USB 帧本身 1 ms 量级,量不出 28 µs 间隙 → 留给批 4 接真屏板/逻辑分析仪 |
|
||||
| 2-3 ✅ | **按键上行** | 帧 A 的 `key` 字节入环形队列(中断侧入、任务侧出);`uiTask` 按 §4.3 的表映射成 `UI_KEY_*` 后调 **`ui_port_queue_key()`**(注键不在中断里)。`0` = 无键不占队列;`9`/`10` 不映射,丢弃并留痕 | **已完成 2026-10-29**:台架 143 个按键帧 → `keys=143 kq=0`(逐键必到、零丢弃),UI 高亮随 UP 键移动(画面确实在变,`fb=144` 即每帧都重组);主机单测 `TC-SBLINK-023` 钉住 FIFO 顺序与溢出丢新 |
|
||||
| 2-4 ✅ | **帧缓冲归属与 DMA 收尾** | **不新建任务**(决策三):`HAL_UART_TxCpltCallback` 在 `rs485.c` 加**第三行转发**(与另两个全局回调同处,避免"分散两处 → 日后被重复定义"),收尾在 `sb_link_on_tx_cplt()`;TX 期间 `s_tx_busy` 冻结帧缓冲,`sb_link_frame_update()` 直接返回 `BSP_ERR_BUSY`,TxCplt 后的下一拍补组;错误中断用 `gState` 收拾被打断的 TX(防残留 busy) | **已完成 2026-10-29**:40 s 台架 `done` 追平 `tx`、`drop=0`、`abor=0`、`bad=0`,无双发;`TC-SBLINK-020` 逐字节证明 busy 期间帧缓冲未被改写;`TC-SBLINK-022` 证明错误中断清残留 busy 且不误判仍在发送的 TX。**连续 30 min 长稳归批 4**(此处只跑了 40 s) |
|
||||
| 2-5 ✅ | **链路监视与容错** | 连续 **≥10 s** 无**有效**召唤帧才判离线(**不能按 500 ms 判**——屏板 ≈5000 s 会自复位一次,约 1 s 空窗);判据用 `rx_ok` 计数是否前进,中断侧不读时钟;CRC 错帧静默丢弃并计数 | **已完成 2026-10-29**:台架脚本一停,10 s 后日志准点出 `screen board OFFLINE: no valid summon for >= 10000 ms`;恢复路径与"9.999 s 不判"由 `TC-SBLINK-024` 钉住;同一轮 `crc=0 len=0 fmt=0` |
|
||||
|
||||
> ⚠ **2-1 的 0.3 ms 是估算,实测 509 µs,判据已改口为 ≤1 ms**(2026-10-29)。
|
||||
> 成本几乎全在 CRC 上:帧 B 每帧要算满 4206 B,查表法在 168 MHz 下约 19~20 周期/字节
|
||||
> (反汇编 8 条指令/字节 + 512 B 查表常驻 Flash、5 个等待周期),仅 CRC 就 ~480 µs;
|
||||
> 组帧其余部分(4208 B 清零 + 3200 B 拷贝)只有 ~20 µs。**这条预算没有实际约束力**:
|
||||
> 一次组帧只占 280 ms 召唤周期的 0.2 %,且只在画面真的变了才付(3.5 次/秒时约 0.17 % CPU)。
|
||||
> 真要压到 0.3 ms 以下,两条现成的路:① 把 512 B 查表拷进 RAM(去掉 Flash 等待周期,约 −125 µs);
|
||||
> ② 998 B 零填充那一段用"推进 N 个零字节"的 GF(2) 变换代替逐字节算(约 −89 µs)。
|
||||
> 两条都没做——代价(512 B SRAM + 一段需要长期维护的矩阵代码)换不来这个占空比下的任何收益。
|
||||
|
||||
> ⚠ **台架收不了 4208 B 应答不是固件问题**(2026-10-29 实测):PC 侧只收到约 2373 B,
|
||||
> 等效 9.5 kB/s,而同一次运行固件侧 `tx=143 done=143` 说明 4208 B 每次都发完了。
|
||||
> 原因是 CH340 的 RX FIFO 只有几十字节、USB 每 1 ms 才搬一次,70 kB/s 必然溢出
|
||||
> (CH340 的 RX 实际上限约 115 kbaud)。**别据此改固件**——换 FTDI / CP210x 再验,
|
||||
> 或者按 §5 直接接真屏板。`screenlink_bench.py --reply` 已内置这条提示。
|
||||
|
||||
|
||||
### 批 3 · 状态灯与 LOGO(3 项)
|
||||
|
||||
@@ -304,14 +324,19 @@
|
||||
|
||||
```
|
||||
Drivers/BSP/ScreenLink/
|
||||
├── sb_proto.c/h 纯逻辑:CRC-16、帧 A 解析、帧 B 组帧 ← 可主机单测(1-1 ✅)
|
||||
└── sb_link.c/h 传输:USART1 收中断 + ORE 清除 + 统计上报 ← 依赖 HAL,仅 MCU(1-3 ✅)
|
||||
(批 2 再往里加:组帧入口、TX DMA 起发、状态灯映射表)
|
||||
├── sb_proto.c/h 纯逻辑:CRC-16、帧 A 解析、帧 B 组帧 / 自校验 ← 可主机单测(1-1 ✅)
|
||||
└── sb_link.c/h 传输:USART1 收中断 + ORE 清除 + 帧 B 组帧入口 + TX DMA 起发/收尾
|
||||
+ 按键队列 + 离线判定 + 统计上报 ← 依赖 HAL,仅 MCU(1-3 / 批 2 ✅)
|
||||
```
|
||||
|
||||
**为什么拆两层**:协议层不碰 HAL 才能在 `test/host/` 里跑单测(项目既有做法:原样 include 生产 `.c`,
|
||||
stub 提供 HAL)。屏板侧 CRC 有查表实现,**用同一张表做交叉验证**是最省事的正确性保证。
|
||||
|
||||
**数据流(批 2 落地后)**:`uiTask` 渲染完 → `sb_link_frame_update(活显存, 掩码)` 按需重组 4208 B
|
||||
(画面没变就只做一次 memcmp,约 20 µs)→ USART1 空闲中断收到召唤 → `sb_tx_kick()` 点 TX DMA →
|
||||
`HAL_UART_TxCpltCallback` → `sb_link_on_tx_cplt()` 清 busy。**中断里只起发、不组帧**:
|
||||
组帧要 0.5 ms,放进中断会拖累全系统中断响应。
|
||||
|
||||
### 4.2 帧 B 组帧(逐字段)
|
||||
|
||||
```
|
||||
@@ -385,7 +410,7 @@ stub 提供 HAL)。屏板侧 CRC 有查表实现,**用同一张表做交叉
|
||||
|---|---|---|---|
|
||||
| 0 | **腾位置**:串口线挪到 RS-232 侧、日志迁走 | 改接串口线 + 一个 **USB 转 RS-232 适配器** | USART1 空出来,日志照旧能看(批 0) |
|
||||
| 1 | 协议层单测(`test/host/screenlink/`) | 无硬件 | CRC 与组帧的正确性,可回归 |
|
||||
| 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 |
|
||||
| 2 | PC **模拟屏板**:按屏板节拍发 6 B 召唤帧(脚本 `test/host/screenlink_bench.py`,`--port COM6 --key-fixed 2` 可持续改画面以拿多次组帧样本;`--glitch-baud 115200` 灌噪声验自愈;`--reply` 收应答) | **USB 转 TTL 模块**接 U5 的 `ST_RX0`/`ST_TX0`(PA10/PA9)+ 主机板 | 收侧**已做**(`ok == rx`、`crc=0`、`size=6`、噪声后自愈);发侧固件计数**已做**(`tx == done`、`drop=0`、`bad=0`、`fb` 随画面变化增长)。⚠ `--reply` 收不全 4208 B 是 CH340 的限制,不是固件问题,见批 2 下方那条 ⚠ |
|
||||
| 3 | 反向:PC 收主机帧,用脚本核对 4208 B / CRC / 显存内容 | 同上 | 帧内容正确性(与 `g_ui_fbuf` 逐字节比) |
|
||||
| 4 | **接真屏板**(TTL 直连屏板 UART2 的 PA2/PA3 一侧) | 屏板实物 + 屏板 TTL 引出 | 出图 + 按键 + 状态灯全链路 |
|
||||
| 5 | 产品形态:按最终线束接法定版 | 线束 | 产品形态验证 |
|
||||
|
||||
@@ -47,10 +47,10 @@ dhara 失败类 +84;RO 另 +208,全是该失败类名表)→ `ER_IROM1`
|
||||
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 |
|
||||
|---|---|---|---|
|
||||
| ER_IROM1 | 266,360 B (260.12 KB) | 1,048,576 B (1024 KB) | 25.4% |
|
||||
| RW_IRAM1 | 81,616 B (79.70 KB) | 131,072 B (128 KB) | 62.3% |
|
||||
| ER_IROM1 | 268,076 B (261.79 KB) | 1,048,576 B (1024 KB) | 25.6% |
|
||||
| RW_IRAM1 | 85,904 B (83.89 KB) | 131,072 B (128 KB) | 65.5% |
|
||||
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
|
||||
| **Flash (ROM 合计)** | **268,260 B (262.0 KB)** | 1,048,576 B (1024 KB) | **25.6%** |
|
||||
| **Flash (ROM 合计)** | **270,008 B (263.7 KB)** | 1,048,576 B (1024 KB) | **25.7%** |
|
||||
<!-- MEM_SNAPSHOT:END -->
|
||||
|
||||
各区的容量与常量来源:
|
||||
|
||||
@@ -15,19 +15,19 @@
|
||||
<!-- MEM_BUDGET:BEGIN generated by scripts/ram_baseline.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) |
|
||||
|---|---|---|---|---|
|
||||
| ER_IROM1 (Flash) | 266,360 B | 1,048,576 B | 25.4% | 参考 |
|
||||
| ER_IROM1 (Flash) | 268,076 B | 1,048,576 B | 25.6% | 参考 |
|
||||
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
|
||||
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
|
||||
| RW_IRAM1 | 81,616 B | 131,072 B | 62.3% | PASS |
|
||||
| RW_IRAM1 | 85,904 B | 131,072 B | 65.5% | PASS |
|
||||
|
||||
| 子系统(静态 RW+ZI;除 RTOS 堆在 CCM 外均在 SRAM1) | 现值 | 配额 | 状态 |
|
||||
|---|---|---|---|
|
||||
| 网络 | 13,702 B | 12 KB | ⚠ 超 1.4 KB |
|
||||
| 存储 | 7,072 B | 12 KB | OK |
|
||||
| UI | 34,656 B | 16 KB | ⚠ 超 17.8 KB |
|
||||
| UI | 34,657 B | 16 KB | ⚠ 超 17.8 KB |
|
||||
| RTOS 堆 | 57,376 B | 56 KB | 堆=配额 |
|
||||
| 应用 | 15,552 B | 8 KB | ⚠ 超 7.2 KB |
|
||||
| 其它 | 10,633 B | — | 参考 |
|
||||
| 其它 | 14,917 B | — | 参考 |
|
||||
<!-- MEM_BUDGET:END -->
|
||||
|
||||
- **门禁**:pre-commit `mem-budget` 钩子(`scripts/ram_baseline.py --gate`)——
|
||||
|
||||
Reference in New Issue
Block a user