docs(屏板链路): 规划文档收编为设计说明事实源,全仓引用与端口口径同步
- 重写 docs/子系统设计/屏板链路设计说明.md:链路角色、串口与端口配置、 物理接线、协议实现口径、键值与状态灯映射、资源与耗时、对外接口、 已验证/未闭环、坑账本——只写现状事实,演进过程查 git 历史 - 删除 docs/任务规划/屏板链路.md(内容已全部沉淀进上述设计说明, 登记簿「完成即销项」;docs/任务规划/README.md 相应两行一并移除) - 代码与主机侧测试的文档指针改指设计说明对应章节: sb_link.h/.c、sb_proto.h/.c、sb_led_map.h、console_log.c、ui_task.c、 test/host 的 sb_link/sb_proto/sb_led_map 三套与两个台架脚本 - docs/README.md:设计文档索引行与设计说明描述更新,任务规划目录 7→6 文件 - docs/产品需求/产品需求设计文档.md:串口通道现状口径同步——第 11/12/13 条 已交付(4208 B 应答/状态灯掩码/键上行/双出口并发)、LOGO 下发出局不实现、 帧长实测固化、调试日志口 UART5/RS232#1(§4.2/§5.16/§6.6/§9.12/§9.13) - docs/子系统设计/控制台单写者日志设计说明.md v1.8:全文端口口径由 USART1 同步为 UART5/RS232#1(CON_UART* 单点宏),ORE 机理标注跨端口 同因,时间线补端口迁移行;头部修订指针改为实际章节名 说明:本任务经用户显式授权在任务树内编辑 docs/任务规划/ 与产品需求基线 (铁律「单写者」豁免一次);主树合并后统一跑全量 check_doc_links --all。
This commit is contained in:
+207
-182
@@ -1,201 +1,226 @@
|
||||
# 屏板链路设计说明(主机侧实现记录)
|
||||
# 屏板链路设计说明(主机侧)
|
||||
|
||||
> 模块:`Drivers/BSP/ScreenLink/`(`sb_proto` + `sb_link`),任务 `screenlink`。
|
||||
> 本文档记**已实现的内容与实测证据**;任务分解、协议事实源、四个决策的来龙去脉在
|
||||
> `docs/任务规划/屏板链路.md`(下称规划文档),两边不重复维护同一张表。
|
||||
> 创建:2026-10-29(真屏板首通当晚落笔)。
|
||||
> **模块**:`Drivers/BSP/ScreenLink/`(`sb_proto` 协议层 + `sb_link` 传输层)· `App/algo/sb_led_map`(状态灯折算)· `App/task/ui_task.c`(宿主)。
|
||||
> **需求与屏板侧契约**(这条链路要做什么、屏板行为逐字节)在 `docs/产品需求/产品需求设计文档.md` §5.16,本文不重抄其表;
|
||||
> 本文是**主机侧实现事实**:接线、串口配置、协议口径、键/灯映射、资源、验证状态与未闭环项。
|
||||
> **屏板固件事实源**:外部工程 `DTU-LCD`(不在本仓,本链路不改它)。
|
||||
|
||||
---
|
||||
## 1. 链路与角色
|
||||
|
||||
## 1. 一句话架构
|
||||
- 屏板是 **Modbus 主站**:每 280 ms 发一帧 6 B 召唤(帧 A,`RT_CMD`),有键待报时插一帧带 key 的立即召唤(`KEY_CMD`)。
|
||||
主机是**从站 `0x01`**:校验收下帧 A,回一帧 4208 B 图像应答(帧 B)。**主机从不主动推送。**
|
||||
- **严格一召唤 → 一答 4208 B**:不提前发、不超发、不叠发。屏板接收溢出即**永久静默**且不可自恢复(它错误回调里的
|
||||
串口重启被注释掉),只能断电重上或等 ≈83 min 自复位——多发是唯一风险源,busy 冻结在主机侧挡死。
|
||||
- **一份显存、两个出口**:串口(本链路)为主,TCP 7003 网络为镜像;两出口读**同一份活显存**
|
||||
(`ui_port_get_framebuffer()`),无第二份账可漂。
|
||||
- **宿主 = `uiTask`(组帧 + 注键),唯一起发者 = USART1 空闲中断(µs 级)**。不新建任务、不占堆、
|
||||
不加看门狗位(产品 `required_mask` 维持 `0x1F7`,起发在中断、与任务心跳无关)。
|
||||
|
||||
屏板是 Modbus 主站、每 280 ms 发一帧 6 B 召唤(帧 A),主机是从站 `0x01`:
|
||||
**任务侧组好一帧 4208 B(帧 B)挂在手上,空闲中断校完召唤直接点 TX DMA 回一帧**;
|
||||
屏板按键骑在召唤帧里上来,主机排给 UI。串口为主出口,7003 网络是同一份显存的镜像。
|
||||
|
||||
```
|
||||
uiTask(10 ms 节拍) USART1 空闲中断(屏板召唤到达)
|
||||
ui_port_step() 渲染 sb_link_on_rx_event()
|
||||
sb_link_frame_update(活显存, 掩码) ├─ sb_frame_a_parse 校验 6 B
|
||||
├─ 点阵 memcmp 没变 → 立刻返回(~20 µs) ├─ 按键 ≠ 0 → 入环形队列
|
||||
├─ TX busy → BSP_ERR_BUSY,下拍再来 ├─ 帧有效 → sb_tx_kick() 点 TX DMA
|
||||
└─ 变了 → sb_frame_b_build_image 组整帧 └─ 无条件重挂 ReceiveToIdle_IT
|
||||
+ sb_frame_b_verify 自校验
|
||||
→ s_frame_ready = 1 DMA2_Stream7 TC 中断
|
||||
sb_link_key_take() 出队 sb_link_on_tx_cplt() 清 busy
|
||||
→ ui_task_board_key_map() 映射 UI_KEY_*
|
||||
→ ui_port_queue_key()
|
||||
sb_link_poll(tick) 统计上报 + ≥10 s 无召唤判离线
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph UITASK["uiTask(10 ms 一拍)"]
|
||||
R["ui_port_step 渲染"] --> F["sb_link_frame_update(活显存, 灯掩码)"]
|
||||
F -->|"点阵/掩码 memcmp 没变(~20 µs)"| R
|
||||
F -->|TX busy → BSP_ERR_BUSY| R
|
||||
F -->|变了 → 组帧 + 自校验| S["s_frame 就绪"]
|
||||
K["sb_link_key_take 出队"] --> M["ui_task_board_key_map 映射"] --> Q["ui_port_queue_key 注键"]
|
||||
P["sb_link_poll 统计 + 离线判定"]
|
||||
end
|
||||
subgraph ISRCTX["USART1 空闲中断(屏板召唤到达)"]
|
||||
A["sb_link_on_rx_event:校 6 B 帧 A"] -->|"key ≠ 0"| KQ["按键环形队列(入队)"]
|
||||
A -->|帧有效 且 s_frame 就绪| D["sb_tx_kick 点 TX DMA"]
|
||||
A -->|无条件| A2["重挂 ReceiveToIdle_IT"]
|
||||
end
|
||||
S -.-> D
|
||||
KQ -.-> K
|
||||
D --> TC["DMA2_Stream7 传完中断"] --> C["清 busy(攒了 dirty → 下一拍补组)"]
|
||||
```
|
||||
|
||||
分层铁律:`sb_proto` 纯逻辑(不碰 HAL/寄存器/任务),原样 include 进 `test/host/` 跑单测;
|
||||
`sb_link` 依赖 HAL 与中断上下文,仅 MCU。
|
||||
分层铁律:`sb_proto` 纯逻辑(不碰 HAL / 寄存器 / 任务),原样 include 进 `test/host/` 跑单测;
|
||||
`sb_link` 依赖 HAL 与中断上下文,仅 MCU(`new-source-file` 双端注册时走 MCU-only 豁免)。
|
||||
|
||||
## 2. 对外接口(单一出处在头文件,这里只列形状)
|
||||
## 2. 串口与端口配置
|
||||
|
||||
| 项 | 值 | 说明 |
|
||||
|---|---|---|
|
||||
| 端口 | **USART1** PA9 / PA10(`U5` 子板连接器网络 `ST_TX0` / `ST_RX0`) | MCU 引脚直出 TTL,**不经任何收发器**,无 DE、全双工两根独立线 |
|
||||
| 波特率 | 700000 8N1,实测 `BRR = 0x0078`(APB2 84 MHz → 7.5,误差 0 %) | 开机日志自证(`sb_link_init` 交叉核对,对不上报 `BSP_ERR_STATE`) |
|
||||
| 波特率落点 | `Src/usart.c` 的 `USER CODE BEGIN USART1_Init 2` 覆盖 `huart1.Init` 后重调 `HAL_UART_Init` | **`.ioc` 不保存任何 UART 的波特率**,GUI 里改也不落盘 |
|
||||
| RX | **16 B 缓冲 + IDLE 中断**(`HAL_UARTEx_ReceiveToIdle_IT`) | 屏板只发 6 B,别照抄它侧的 4208 B;**一次性 API,每次回调必须重挂**,漏一次 = 永久静默;**不开 RX DMA** |
|
||||
| TX | **整帧交 TX DMA**:`DMA2_Stream7` CH4(NVIC 优先级 9,`Src/dma.c` 已配、`Src/stm32f4xx_it.c` 已接 IRQ) | CubeMX 预置、全工程原本没人用;`.ioc` 无需为此改动;帧缓冲必须在 SRAM(**CCM 对 DMA1/2 物理不可达**,放错段 = 静默发旧数据) |
|
||||
| 全局 UART 回调 | `HAL_UARTEx_RxEventCallback` / `HAL_UART_ErrorCallback` / `HAL_UART_TxCpltCallback` 的**唯一实现处在 `Drivers/BSP/RS485/rs485.c`**,各含一行 `sb_link_on_*` 转发;按句柄过滤在 `sb_link` 内做 | 三个都是全局单例:再定义一份就是链接期 multiple definition;收在同一文件,下一个加通道的人一眼看到全部 |
|
||||
| 错误处理 | 按 **SR → DR 顺序**清 ORE/NE/FE/PE;**重挂判据用 `RxState == READY` 而非错误位**(ORE 阻塞型须重挂,NE/FE 非阻塞、接收还在跑,再挂只拿 `HAL_BUSY`);TX 被错误打断时按 `gState != BUSY_TX` 清残留 busy | ORE 锁存 = 接收再也进不来;残留 busy = 之后每次召唤都被判"上一帧还在发"→ rx 涨 tx 冻结 |
|
||||
| 链路监视 | `rx_ok` 计数 **≥10 s 不前进 → 判离线**(`SB_LINK_OFFLINE_MS`),恢复由下一次有效召唤;5 s 统计行只在计数有变化时打 | 屏板 ≈5000 s 自复位有 ≈1 s 空窗,按 500 ms 判会把"歇口气"判成离线反复跳;屏板无背光 100 s 熄灭也**不是**掉线(按键即复亮) |
|
||||
| 日志口联动 | 控制台已迁 **UART5(RS232#1)**,USART1 专用屏板;收口在 `App/log/console_log.c` 的 `CON_UART*` 单点宏 | 两条流共用同一个 `USART1->DR` 会互相插字节(症状 = 屏板偶发花屏 + 日志乱码);台架端子见 §3.2 |
|
||||
|
||||
## 3. 物理接线
|
||||
|
||||
### 3.1 主机 ↔ 屏板(TTL 直连,PA10 已接线、无需飞线)
|
||||
|
||||
| 主机侧(V0.1) | 屏板侧 | 方向 |
|
||||
|---|---|---|
|
||||
| `U5.A10`(网络 `ST_TX0`,PA9) | 屏板排针**丝印 `RX`** | 主机发 → 屏板收 |
|
||||
| `U5.B10`(网络 `ST_RX0`,PA10) | 屏板排针**丝印 `TX`** | 屏板发 → 主机收 |
|
||||
| 板地 `CGND`(或任一 GND 脚) | 屏板 `GND` | **两板各自供电必须接第三根共地线**,浮地的形态同样是彻底收不到 |
|
||||
|
||||
- 丝印 `TX/RX` 是**屏板自己发出/接收的视角**,所以主机侧**交叉同名接**:主机 TX 接丝印 `RX`、主机 RX 接丝印 `TX`。
|
||||
- 屏板排针收发脚与屏板网表(PA2=发 / PA3=收)的对应关系未经屏板方确认(实测按网表接不通、对调才通)。
|
||||
**唯一实操验收口径 = 接完看主机 `rx` 计数涨不涨**(涨 = 方向对,与帧内容无关),不涨当场对调两根信号线;
|
||||
产品线束定版前向屏板方核实实际收发脚。**不要按屏板 readme 与"TX 对 TX"直连**——同边对接两端都静默且无任何报错可看。
|
||||
- **别去 CN1 找 USART1 端子**:`ST_TX0` / `ST_RX0` 在网表里只连 `U5.A10` / `U5.B10` 与 MCU 101/102 脚;
|
||||
CN1 上的 `RS232_*` 是 UART5 / USART2 经收发器之后的电平。同一个 `U5` 上还挂着 USART3 的 `ST_TX2` / `ST_RX2`
|
||||
(`U5.C7` / `U5.C6`),别抓错针。3.3 V TTL 直连,不串电阻,没有方向控制脚可接。
|
||||
|
||||
### 3.2 台架日志口(RS232#1)
|
||||
|
||||
- 台架须用 **USB 转 RS-232 适配器**(TTL 的 CH340 直连不了,且 RS-232 的 ±电平可能打坏 `U13` TPT3232E 或适配器):
|
||||
适配器 `TXD` → `CN1.10`(板子 `RS232_RX1`)、`RXD` → `CN1.12`(板子 `RS232_TX1`)、`GND` → `CN1.7` 或 `CN1.8`(`CGND`)。
|
||||
- 台架串口因此是 **`COM3`**,`.serialtest/config.json` 的 `portName` 已按此配置,`@test` 与 `serial-assistant` 不必手工指端口。
|
||||
- RS232#1 **没有 TTL 出口**(`ST_TX1`/`ST_RX1` 只连 `U9` 隔离器与 MCU),唯一外部出口是 CN1 的 RS-232 端子;
|
||||
`U14` 是 RS232#3 的网口座,本通道用不上。
|
||||
|
||||
### 3.3 PC 顶替屏板(无屏板实物的台架验证)
|
||||
|
||||
- USB 转 TTL 适配器:`TXD` → `U5.B10`、`RXD` → `U5.A10`、`GND` → 板地;700000 8N1;
|
||||
激励脚本 `test/host/screenlink_bench.py` 按屏板节拍发 6 B 帧 A(`--key-fixed` 持续改画面取组帧样本、
|
||||
`--glitch-baud` 灌噪声验自愈、`--reply` 收应答)。
|
||||
- ⚠ **CH340 收不了 700 kbaud 连续流**(RX FIFO 几十字节 + USB 每 1 ms 才搬一次,实测只收到 ≈2373 B / 9.5 kB/s):
|
||||
**收不全 ≠ 固件没发**,同轮 `tx == done` 即自证;要收全 4208 B 帧验内容须换 FTDI / CP210x。
|
||||
|
||||
## 4. 协议实现口径
|
||||
|
||||
帧 A / 帧 B 的逐字段表与屏板时序常量在 `docs/产品需求/产品需求设计文档.md` §5.16.2,此处只记主机侧实现事实:
|
||||
|
||||
- **CRC-16/MODBUS**(`0xA001` 反射、初值 `0xFFFF`、16 位合并表):帧 A 覆盖前 4 B、帧 B 覆盖 `[0, 4205]`,
|
||||
**一律低字节在前**(标准 Modbus RTU 线上序)。屏板源码 `Calculate_CRC()` 的返回值与写入处注释把高低**都写反了**
|
||||
(`123456789` 它返回 `0x374B`,标准值 `0x4B37`;两处名字反、线上字节对)——照字面实现成"高字节在前",
|
||||
屏板会把每一帧判成 CRC 错,而它超时判定只看"收没收到一整包"、坏帧也清超时计数,现场只表现为"画面不刷新"。
|
||||
实测向量钉在 `test/host/sb_proto/sb_proto_test.c`(`01 03 01 00 F0 48`、`01 03 00 09 31 DE`)。
|
||||
- **帧 B 正好 4208 B**:3200 显存 + 4 B 灯掩码(小端)+ `info[4] = info[5] = 0x00`(普通图分支)+ `info[6..7]` 保留 +
|
||||
**998 B 填充固定 `0x00`(屏板不读但参与 CRC)** + 2 B CRC。短一字节屏板就在 `DR[4206]` 读到陈旧字节 → CRC 必失败
|
||||
静默丢弃;多发会把它正在处理的那一帧撕裂(收满即回调、缓冲从头再填)。
|
||||
- **LOGO 形态不实现、不下发**(出局):总线上不会出现 `info[4..5] = 0x89 0x45` 的帧;`sb_proto` 里既有的
|
||||
`sb_frame_b_build_logo()` 与魔数常量为待删除遗留(§9.2),勿新增调用。
|
||||
- **组帧**(`sb_link_frame_update`,uiTask 每拍):拿帧缓冲里的点阵段与活显存 memcmp,变了才组 + `sb_frame_b_verify`
|
||||
自校验,不过不置 ready(宁可不发旧帧)。**不留显存影子副本**(省 3200 B SRAM 与每拍重算 CRC,代价只是每拍一次
|
||||
~20 µs 的 memcmp)。**TX DMA busy 期间冻结帧缓冲**:DMA 读的就是 `s_frame` 本体,中途改写会发出半新半旧的"脏帧"
|
||||
——CRC 仍对、屏板照收、画面撕裂,极难归因;忙就 `BSP_ERR_BUSY` 下一拍补组(双缓冲被否:最坏慢 10 ms,
|
||||
280 ms 周期无感,不值再花 4208 B)。
|
||||
- **时序事实**(屏板实跑 **705 882 bps**——它自己 36 MHz 时钟比标称高 0.84 %,判据换算按它、不按 700 000):
|
||||
1 字符 ≈14.17 µs,整帧 4208 × 10 bit ≈ **59.6 ms**(8N1)。
|
||||
- **断帧窗口**:屏板按"总线空闲 1 个字符时间"断帧且无接收超时寄存器可调——**间隙 ≥2 字符(≈28 µs)必把一帧
|
||||
切成两半**,两半都过不了 CRC。这是"必须整帧一次交 DMA"的全部理由:任务里逐字节轮询 `DR`,一次几十 µs 的
|
||||
ISR 抢占(TIM3 采样、SysTick、SPI/CH395F DMA)就毁帧,而屏板不报超时、不可归因。
|
||||
- **起发留量**:距召唤帧末位 ≥30 µs(2 个字符时间,等屏板发完放开它自己的 DE 脚)再起发,无上限要求。
|
||||
- **屏板的 205 ms 不是帧作废线**:到点只置"可以重发"标志,不清缓冲、不重启串口,**迟到的完整帧照样显示**
|
||||
(帧里没序号);真硬的是下一轮召唤——无键 280 ms、**有键待报最早 ≈205 ms 就再来一帧**,上一答还在总线上
|
||||
就是双向对撞。所以"≤200 ms 内发完"仍要做,理由是**防撞总线 + 不多一轮画面滞后**。
|
||||
- **按键只发一次不重试**(屏板发完即清备份):错过那一轮 = 丢键——这是起发必须留在中断(µs 级)、
|
||||
不能挂在任何任务态的直接原因。`uiTask` 属"数秒量级等待"那一族(netTask 忙等推导上界 ≈4 s、
|
||||
对时 relisten 突发实测单轮 800 ms 以上缺席),屏板只给 205/280 ms,任务态起发救不了。
|
||||
- **按键上行**:中断校验帧 A 后 `key ≠ 0` 入环形队列(可存 3 个,满了丢**最**新的一帧并计数——旧键已在路上),
|
||||
`uiTask` 出队 → 映射 → 注键;**注键不在中断里**。
|
||||
|
||||
## 5. 键值映射(帧 A `key` → `UI_KEY_*`)
|
||||
|
||||
| 屏板 `key` | 语义 | 映射 | 备注 |
|
||||
|---|---|---|---|
|
||||
| 0 | 无键 | — | 不产生事件、不占队列 |
|
||||
| 1 / 2 / 3 / 4 | ENTER / UP / DOWN / ESC | `0x20` / `0x02` / `0x40` / `0x01` | |
|
||||
| 7 / 8 | LEFT / RIGHT | `0x10` / `0x08` | |
|
||||
| 9 / 10 | RESET / FACTORY | **丢弃 + 一行日志**(`unmapped`) | 语义待产品裁定(映射复位 / 映射 ESC / 不映射);10 无物理入口理论收不到 |
|
||||
| 5 / 6 | ADD / DEC | — | 屏板 `key_configs[]` 仅 7 项,**无物理入口**,不会出现——别按屏板 readme 的 8 键表实现 |
|
||||
|
||||
- 映射表落点在 `ui_task.c` 的 `ui_task_board_key_map()`;`sb_link_key_take()` 只吐**屏板原始键号**——键号是协议
|
||||
语义、UI 键码是 UI 语义,汇合归 uiTask(那一层才知道当前哪个页面在接键)。
|
||||
- 与 7003 网络的注入(`ui_net_sink_inject_key()`)**同一个输入队列**(都走 `ui_port_queue_key`),两路无需互斥。
|
||||
|
||||
## 6. 状态灯映射(帧 B `info[0..3]`,32 位小端)
|
||||
|
||||
权威定义 = `App/algo/sb_led_map.h` 位宏 + `sb_led_map_eval()`(**映射的唯一出处**,折算不散落调用点)。
|
||||
`uiTask` 每拍现采四路状态源 → 掩码进 `sb_link_frame_update()`;掩码变了帧就重组,走 busy/dirty 既有通路,无新机制。
|
||||
|
||||
| 位 | 语义 | 来源 |
|
||||
|---|---|---|
|
||||
| bit1 | 运行心跳 | 500 ms 半周期翻转,与主板 LED1~3 同拍 |
|
||||
| bit2 | 告警 | `prot_elm_active_mask() != 0` |
|
||||
| bit3 | 通信(104 RUN) | `iec104_serv_link_run()` |
|
||||
| bit4 | 采样故障 | `adc_health_flags()` 电平位(不选 sticky——灯要能灭) |
|
||||
| bit5 | 保护启动 | `prot_elm_pick_mask() != 0` |
|
||||
| bit0 | 恒 0 | ID 0 电源灯硬件常亮,屏板不看 bit0 |
|
||||
| bit6~31 | 恒 0 | 保留,扩展从 ID 6 顺序取 |
|
||||
|
||||
- 折算做成**纯函数分支**而不是 const 表 + 谓词回调:5 路输入判据形态各异(电平 4 路 + 闪烁 1 路),
|
||||
纯函数直白且可单测;主机单测 `test/host/sb_led_map/` 钉「掩码 & ~定义集 == 0」,改表漏测会被套件拦住。
|
||||
- ⚠ 产品构建下 bit2 / bit5 **恒灭**(保护判据未标定闭锁,`ADC_CAL_F_VALID` 未置位)——验证这两路要等标准源
|
||||
标定后或临时构建(与屏上「保护动作 N」同一已知口径)。
|
||||
|
||||
## 7. 资源与耗时(`.map` / 日志可复核)
|
||||
|
||||
| 项 | 值 | 说明 |
|
||||
|---|---|---|
|
||||
| `s_frame` | 4208 B @ `0x20006d98` | `Execution Region RW_IRAM1`(`0x20000000` 起),**不在 `RW_CCM`** |
|
||||
| `s_rx_buf` / `s_stats` 等 | 16 + 84 B | 同段 |
|
||||
| 整模块增量 | Code ≈ +1.5 KB(237,464 → 238,936 B) | `.map` |
|
||||
| 全镜像 | RW_IRAM1 85,904 B(65.5 %)/ RW_CCM 57,376 B(87.9 %) | 凭证 `scripts/size_attestation.json`(门禁红线 92 %) |
|
||||
| 任务 / 堆 / 看门狗 | **零新增** | uiTask 宿主、静态缓冲、`required_mask` 不变 |
|
||||
| 组帧耗时 | **509 µs 典型 / 812 µs 峰值,预算 ≤1 ms**;没变只付 memcmp ≈20 µs | 台架 `us=` / `max=` 字段 |
|
||||
| 耗时结构 | ≈480 µs 全在 CRC:每帧算满 4206 B,168 MHz 查表 ≈19~20 周期/字节,512 B 表驻 Flash(5 等待周期);清零+拷贝只 ≈20 µs。**占空比无约束**:一帧 280 ms ≈0.2 %,3.5 帧/s ≈0.17 % CPU | 压降两条路已评估**未实施**:表搬 SRAM(≈ −125 µs,+512 B SRAM)、998 B 零段 GF(2) 推进变换(≈ −89 µs,需长期维护矩阵代码)——这个占空比换不来收益;另循环变量用 `uint32_t` 防 ARMCC 每轮多一条 `UXTH`(`sb_crc16`) |
|
||||
|
||||
## 8. 对外接口(单一出处在各头文件,这里只列形状)
|
||||
|
||||
| 接口 | 上下文 | 作用 |
|
||||
|---|---|---|
|
||||
| `sb_link_init(&huart1)` | 启动,`app_main.c` 步骤 2b | 交叉核对波特率(对不上报 ERR_STATE)+ 挂首轮接收 + 使能 DWT |
|
||||
| `sb_link_receive_start()` | 中断/任务 | 重挂 IDLE 接收(一次性 API,漏一次链路永久静默) |
|
||||
| `sb_link_init(&huart1)` | 启动,`app_main.c` 步骤 2b | 交叉核对波特率(对不上 `BSP_ERR_STATE`)+ 挂首轮接收 + 使能 DWT |
|
||||
| `sb_link_receive_start()` | 中断/任务 | 重挂 IDLE 接收(一次性 API,漏一次永久静默) |
|
||||
| `sb_link_on_rx_event(huart,size)` | 中断(rs485.c 转发) | 帧 A 校验、计数、按键入队、**点 TX DMA 应答**、重挂 |
|
||||
| `sb_link_on_error(huart)` | 中断(rs485.c 转发) | 按 SR→DR 顺序清错、按需重挂 RX、按 `gState` 清被打断 TX 的残留 busy |
|
||||
| `sb_link_on_error(huart)` | 中断(rs485.c 转发) | 按 SR→DR 清错、按 `RxState` 决定重挂 RX、按 `gState` 清被打断 TX 的残留 busy |
|
||||
| `sb_link_on_tx_cplt(huart)` | 中断(rs485.c 转发) | 清 busy、计数;**不在中断里组帧** |
|
||||
| `sb_link_frame_update(bmp,mask)` | uiTask | 按需组帧(memcmp 判变化),busy 返 BUSY,自校验不过不发 |
|
||||
| `sb_link_key_take()` | uiTask | 出队屏板原始键号(0=空);映射归 uiTask |
|
||||
| `sb_link_poll(now_ms)` | uiTask | 离线判定(每拍)+ 5 s 变化才打的 RX/TX 两行统计 |
|
||||
| `sb_link_get_stats(&st)` | 任意 | 计数快照(rx/ok/crc/keys/tx/done/drop/fb/us/offline…) |
|
||||
| `sb_crc16` / `sb_frame_a_parse` / `sb_frame_b_build_image` / `sb_frame_b_build_logo`(⚠ 待删除,LOGO 出局见批 3 的 3-3)/ `sb_frame_b_verify` | 纯逻辑 | 协议层五件套,主机单测 59/59 |
|
||||
| `sb_link_frame_update(bmp,mask)` | uiTask | 按需组帧(memcmp 判变化),busy 返 `BSP_ERR_BUSY`,自校验不过不发 |
|
||||
| `sb_link_key_take()` | uiTask | 出队屏板原始键号(0 = 空);映射归 uiTask |
|
||||
| `sb_link_poll(now_ms)` | uiTask | 离线判定(每拍)+ 5 s 变化才打的统计行(RX/TX 两行自分频) |
|
||||
| `sb_link_get_stats(&st)` | 任意上下文 | 计数快照(rx/ok/crc/len/fmt/keys/tx/done/drop/fb/bad/us/off…) |
|
||||
| `sb_crc16` / `sb_frame_a_parse` / `sb_frame_b_build_image` / `sb_frame_b_verify` | 纯逻辑 | 协议层四件套,主机单测可回归 |
|
||||
| `sb_frame_b_build_logo` | 纯逻辑 | **待删除**(LOGO 出局,勿新增调用,§9.2) |
|
||||
|
||||
全局回调落点:`HAL_UARTEx_RxEventCallback` / `HAL_UART_ErrorCallback` /
|
||||
`HAL_UART_TxCpltCallback` 三个的**唯一实现处都在 `rs485.c`**,各含一行
|
||||
`sb_link_on_*` 转发;按句柄过滤在 `sb_link` 内部做。三个必须收在一处见全——
|
||||
分开写,下一个加通道的人会以为 TxCplt 没人用而再定义一份(multiple definition)。
|
||||
## 9. 验证状态
|
||||
|
||||
## 3. 关键实现决定(为什么是现在这个样子)
|
||||
### 9.1 已验证
|
||||
|
||||
1. **不留 3200 B 影子缓冲**:帧里的点阵段就是"上次组进去的那一份",直接拿它跟活显存
|
||||
memcmp。省下 3200 B SRAM 与每拍 0.15 ms 的 CRC,代价只是每拍一次 memcmp(~20 µs)。
|
||||
2. **busy 期间冻结帧缓冲**:DMA 读的就是 `s_frame` 本体,中途改写 = 半新半旧的脏帧
|
||||
(CRC 仍对、屏板照收、画面撕裂,极难归因)。宁可 `BSP_ERR_BUSY` 下一拍再组。
|
||||
双缓冲被否:一召唤一答的节奏下"下一拍补组"最坏慢 10 ms,屏板 280 ms 周期无感,
|
||||
不值再花 4208 B。
|
||||
3. **起发在中断里**(规划文档的决策三):4208 B 线上 59.6 ms,任务里轮询 DR 一次几十 µs
|
||||
的 ISR 抢占就把帧切断(屏板按 1 字符时间断帧、且坏帧也清超时计数——现场只剩"画面不刷新")。
|
||||
4. **注键走 `ui_port_queue_key`、不绕 `ui_net_sink_inject_key`**:后者只是 7003 后端对
|
||||
同一函数的转发,绕过去要多一次 include 且语义不变。两条入口进同一个输入队列。
|
||||
5. **错误中断顺手收拾 TX**:HAL 的 TX 被错误打断时不调 TxCplt,busy 会永久挂着,
|
||||
之后每次召唤都被判"上一帧还在发"→ rx 涨 tx 冻结。判据用 `huart->gState != BUSY_TX`。
|
||||
6. **离线判 10 s 不判 500 ms**:屏板 ≈5000 s 自复位有 ≈1 s 空窗,短窗会把"歇了口气"
|
||||
判成离线再"恢复",标志反复跳。
|
||||
7. **LED 掩码走独立纯逻辑模块折算**(批 3 落地):位定义 + 求值收在 `App/algo/sb_led_map.c/h`
|
||||
(映射表唯一出处),`uiTask` 每拍现采四路状态源(`prot_elm_active_mask` / `prot_elm_pick_mask` /
|
||||
`adc_health_flags` / `iec104_serv_link_run`)喂进去,掩码变了帧就重组——复用批 2 的 busy/dirty
|
||||
机制,无新通路。折算不做成 const 表:5 路输入判据形态各异(电平 4 路 + 心跳 1 路),
|
||||
纯函数分支比"表 + 谓词回调"直白,且主机单测能钉「掩码 & ~定义集 == 0」。
|
||||
8. **键 9/10 丢弃留痕**:决策四未裁,先按 `docs/任务规划/屏板链路.md` §4.3 的表把
|
||||
1/2/3/4/7/8 映射到 `UI_KEY_*`,9/10 出日志不注键。
|
||||
9. **LOGO 出局**(用户裁定 2026-10-29):不做下发入口。`sb_proto` 的 `sb_frame_b_build_logo()`
|
||||
与魔数常量**列为待删除**(头文件已挂 ⚠ 勿新增调用);常规图像路径 `info[4..5]` 恒 0,
|
||||
屏板按普通图分支处理,总线上不会再出现 LOGO 形态——「屏板接收溢出」风险表随之只剩叠发一源。
|
||||
| 层级 | 证据(计数名 = 台架/真机日志 `[SBLINK]` 行字段) |
|
||||
|---|---|
|
||||
| 主机单测 | `test/host/sb_proto/` **59/59**(屏板实测向量、CRC 表与屏板查表逐字节交叉验证);`test/host/sb_link/` **146/146**(ORE 的 SR→DR 顺序在桩里真执行、非阻塞错误不得重挂、busy 期间帧缓冲逐字节冻结、帧 B 逐字段、周期→µs 换算、离线 9.999 s/10 s 边界与恢复、按键 FIFO 与溢出丢新);`test/host/sb_led_map/` **17/17** |
|
||||
| 台架(PC + USB 转 TTL 顶替屏板) | `rx == 发送帧数`、`ok == rx`、`crc=0 len=0 size=6`;错波特率灌 64 B 垃圾后**自动恢复**(`rearm=0`,一次都没真失败);杜邦线噪声形态 `bits=0x02`(NE)每帧 ~1.3 次但数据零错;激励停止 10 s 准点 OFFLINE |
|
||||
| 真屏板冒烟(2026-10-29) | 5 s ≈18 帧 → 278 ms/帧,与屏板 280 ms 节拍吻合;`tx==done`、一召一答无双发;**屏板出图(用户目视确认)**——屏板收帧只有"CRC 过 → 刷屏 / 不过 → 静默丢弃"两种结局且无报错通道,**出图 = 屏板亲手端到端验收了帧长 4208、CRC 覆盖 `[0,4205]`、低字节在前、1 bpp 20 B/行 MSB 在前的布局**,这是任何主机侧计数都给不出的一条证据 |
|
||||
| 真屏板 10 min 连续(2026-10-29) | `rx=4258 ok=4258 crc=0 len=0 fmt=0 err=0 bits=0x00 rearm=0`(**连错误中断都没进过**);`tx=4249 done=4248`(`drop=9` 全在开机头两秒帧未就绪期,窗口内零新增);`keys=53 kq=0`(逐键必到、零丢弃);`fb=902 bad=0`(每帧自校验通过);组帧峰值 `us=687 max=812` < 1 ms 预算 |
|
||||
| 双通道并发(2026-10-30,真屏板在线) | 7003 侧 `test/host/remolcd_pull_test.py` INIT→LCDMEM 10 Hz 重压两轮 **1500/1500 + 700/700**、CRC 错 0、超时 0,往返 min 35 / avg 63 / max 131 ms;屏板侧 60 s 隔离窗口 rx +233 全收、**每召必答**、`drop` 零新增、`off=0`;画面一致性由"两出口同一份活显存"机制保证 |
|
||||
| 离线与恢复 | 断接期间 `off=1`,恢复后 `off=0`——`offline` 标志**只有**恢复分支会清,1→0 的转变即恢复路径执行过的证明(全程未重启主机) |
|
||||
|
||||
## 4. 资源占用(实测,`.map` 可复核)
|
||||
### 9.2 未闭环
|
||||
|
||||
| 项 | 值 | 备注 |
|
||||
|---|---|---|
|
||||
| `s_frame` | 4208 B @ `0x20006d98` | `Execution Region RW_IRAM1`——**不在 CCM**(DMA1/2 物理不可达 CCM,放错段=静默发旧数据) |
|
||||
| `s_rx_buf` / `s_stats` 等 | 16 + 84 B | 同段 |
|
||||
| 整模块增量 | Code 约 +1.5 KB | 批 2 前后 237,464 → 238,936 B |
|
||||
| 全镜像 | RW_IRAM1 85,904 B (65.5 %) / RW_CCM 57,376 B (87.9 %) | 凭证 `scripts/size_attestation.json` |
|
||||
| TX DMA | `DMA2_Stream7` CH4,NVIC 优先级 9 | CubeMX 已配好、此前全工程没人用;无需动 `.ioc` |
|
||||
| 波特率 | USART1 = 700000,BRR=0x0078(误差 0 %) | 写在 `Src/usart.c` 的 `USER CODE BEGIN USART1_Init 2`(`.ioc` 不保存波特率),开机日志自证 |
|
||||
| RX | 16 B + IDLE 中断 | 不上 RX DMA(屏板只发 6 B) |
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
| 2 h 长稳 | 跨一次屏板自复位(≈5000 s + 余量):零误判 OFFLINE、零 kerr/abort、自动续上 |
|
||||
| 仪器项 | 逻辑分析仪:整帧期间无 ≥28 µs 间隙、召唤末位→应答首位 ≥30 µs、59.6 ms 整帧时长;FTDI/CP210x 收满 4208 B 后与 `g_ui_fbuf` 逐字节比对 |
|
||||
| uiTask 拥塞面 | 制造 7003/netTask 拥塞(客户端不读、对时 relisten 突发),屏板侧应零超时、画面停最后一帧——"起发不在任务态"的直接证据 |
|
||||
| 目视项 | 7 个物理键逐个按、动作对照 `docs/产品需求/产品需求设计文档.md` §8.2 菜单树 + 键 9 出 `unmapped` 日志;状态灯逐位(bit1 肉眼即见;bit3 挂 104 主站看亮灭;bit4 拔采样前端;bit2/bit5 见 §6 闭锁提示);主动拔插回归(含 7003 并发面) |
|
||||
| 剖析对照 | 画面静止 1 min,开屏板链路前后对比 `[PROF] uiw`(memcmp ~20 µs 成本判据) |
|
||||
| 待删除 | `sb_proto` 的 `sb_frame_b_build_logo()` + 魔数常量与对应单测用例(LOGO 出局遗留,现仅"勿新增调用") |
|
||||
| 待确认 | 屏板排针实际收发脚方向(向屏板方,线束定版前);键 9/10 语义(产品侧) |
|
||||
| 观察项 | 一次上电累计 `len=7` 共 7 次(IDLE 断帧尺寸 ≠ 6),出现窗口夹在"烧录后冒烟"与"首轮 150 s 重压结束"之间、不能排除与重压相关;但同固件上一上电周期 10 min 窗 `len=0`,第二轮 60 s 隔离窗口**零新增**,全程 `crc=0 fmt=0`、`ok` 增速与 `rx` 同频——功能影响为零。若再现首查:那包是否伴随 `err` 位(NE 噪声尾巴粘进下一包 = 无屏蔽杜邦线的正常形态) |
|
||||
|
||||
## 5. 实测记录
|
||||
## 10. 坑账本
|
||||
|
||||
### 5.1 主机单测(可回归)
|
||||
|
||||
- `test/host/sb_proto/`:**59/59**,含屏板实测向量与 CRC 表逐字节交叉验证。
|
||||
- `test/host/sb_link/`:**146/146**(批 2 新增 65 条)。桩把 `SR/DR/CYCCNT` 做成函数指针,
|
||||
能在主机上钉住:ORE 清除的 SR→DR 顺序(RM0090 语义在桩里真执行)、非阻塞错误**不得**重挂、
|
||||
busy 期间帧缓冲逐字节冻结、错误中断清残留 busy 但不误杀在发的 TX、帧 B 每个字段、
|
||||
周期→µs 换算除数、离线 9.999 s/10 s 边界与恢复、按键 FIFO 与溢出丢新。
|
||||
|
||||
### 5.2 台架(PC + USB 转 TTL 顶替屏板,2026-10-29 白天)
|
||||
|
||||
40 s `--key-fixed 2`:`rx=143 ok=143 keys=143 crc=0` / `tx=143 done=143 drop=0 abor=0
|
||||
fb=144 bad=0`;杜邦线噪声形态 `bits=0x02`(NE)每帧 ~1.3 次但数据零错——已定口径
|
||||
"NE/FE 而 rx_ok 照涨 = 能用的告警";错波特率灌 64 B 垃圾后自动恢复;激励停止 10 s
|
||||
准点 OFFLINE。组帧耗时 **509 µs 典型 / 709 µs 峰值**(成本全在 4206 B 查表 CRC、512 B
|
||||
表驻 Flash)——原 0.3 ms 估算判据已改口 1 ms,压降路径记录未实施,见规划文档批 2 的 ⚠。
|
||||
|
||||
### 5.3 真屏板冒烟首通(2026-10-29 21:24,接 §2 决策一的 TTL 线)
|
||||
|
||||
| 证据 | 读数 | 结论 |
|
||||
|---|---|---|
|
||||
| `rx=125 ok=125 crc=0 len=0 fmt=0 err=0 bits=0x00 rearm=0` | 一字节没错、**连错误中断都没进过** | 真屏板线路质量优于台架杜邦线(台架每帧 ~1.3 次 NE) |
|
||||
| 5 s ≈ 18 帧 → 278 ms | 与屏板固件 280 ms(第 56 拍 × 5 ms)吻合 | 对端确认是屏板本尊的周期召唤 |
|
||||
| `tx=116 done=116 kerr=0 abor=0`,`drop=9` 集中在开机头两秒 | 一召一答、无双发、无失败 | 2-2/2-4 的时序骨架在真硬件上成立 |
|
||||
| 错接期间 `off=1` → 对调线后连打三轮 `off=0` | 离线判定与自动恢复走通 | 2-5 判据闭环(全程未重启主机)。⚠ `back online` 那一行本身没取到证据——恢复发生在 21:24 抓日志的 `clear` 之前 ~10 s,行被抹掉了;但 `offline` 字段**只有**恢复分支会清,1→0 的转变即该分支执行过的证明 |
|
||||
| **屏板出图,用户目视确认"非常完美"** | 见 §5.4 的推论 | 帧 B 的 4208/CRC 口径端到端正确 |
|
||||
|
||||
### 5.4 "出图"这两个字的证明力(别低估它)
|
||||
|
||||
屏板收帧**只有两种结局**:整包 CRC 过 → 刷屏;不过 → 静默丢弃,且它超时计数照清、
|
||||
没有任何报错通道(`docs/任务规划/屏板链路.md` §4.4 预言过的"只表现为画面不刷新")。所以肉眼看到正确画面,等于
|
||||
**屏板亲手验收了**:帧长 4208、CRC 覆盖 [0,4205]、**低字节在前**(两处命名相反的屏板
|
||||
源码口径)、1 bpp 20 B/行 MSB 在前的显存布局、info 字段布局——全部端到端正确。
|
||||
这是主机侧任何计数都给不出的一条证据,台架阶段(CH340 收不全)也补不了。
|
||||
|
||||
### 5.5 真屏板 10 min 连续零错(2026-10-29 21:30~21:44,验收清单第 1 条的主机侧半边)
|
||||
|
||||
挂机窗口内(日志清零后累计)末轮计数:
|
||||
|
||||
| 计数 | 读数 | 判据 |
|
||||
|---|---|---|
|
||||
| `rx=4258 ok=4258 crc=0 len=0 fmt=0 err=0 bits=0x00 rearm=0` | **连错误中断都没进过一次** | 10 min 零超时、零 CRC 错帧 ✓ |
|
||||
| 末段 5 s 采样 4240→4258 = 18 帧 | 277.8 ms/帧 | 与屏板 280 ms 节拍吻合 ✓ |
|
||||
| `tx=4249 done=4248 drop=9 kerr=0 abor=0` | `drop=9` 全部是开机头两秒(帧未就绪),窗口内零新增;`done` 差 1 是采样瞬间最后一帧还在发(59.6 ms 窗) | 一召一答、无双发、无失败 ✓ |
|
||||
| `keys=53 kq=0` | 按压期间 53 个按键帧全部入队出队、**零丢弃** | 按键"逐键必到"的主机侧半边 ✓(动作语义合不合菜单树需目视,见 §7-1) |
|
||||
| `fb=902 bad=0`,`us=687 max=812` | 10 min 组帧 902 次(待机时钟秒跳等 LVGL 自刷新驱动,≈1.5 次/s),自校验全程通过;峰值 812 µs < 1 ms 改口判据 | 常态组帧占空比对 10 ms 节拍 <7 % |
|
||||
| 窗口内无 `OFFLINE`/`back online` 行 | 连接全程稳定 | — |
|
||||
|
||||
### 5.6 接线实测教训(决策一的接法表照接后**不通**,对调才通)
|
||||
|
||||
- 按规划文档决策一的表接"主机 A10(PA9)→屏板 PA3、B10(PA10)→屏板 PA2"**rx=0 彻底静默**;
|
||||
**把两根信号线对调后立即通**。表本身的信号方向逻辑没错(TX 对 RX),错在**屏板侧引脚的
|
||||
认定**——排针丝印 TX/RX 是"屏板自己发出/接收"的视角,与网表里 PA2=发送/PA3=接收的对应
|
||||
关系未经屏板方确认。实操判据比丝印可靠:**接完看主机 `rx` 有没有计数**(rx 一动=方向对,
|
||||
与帧内容无关),不动就当场对调。产品形态线束定版前,以"rx 计数"为唯一验收口径。
|
||||
- 两块板各自供电必须接第三根线 GND;只连两根信号线时是浮地,形态同样是彻底收不到。
|
||||
|
||||
### 5.7 双通道并发回归(批 4 的 4-1,2026-10-30 00:10~00:18,真屏板在线)
|
||||
|
||||
激励脚本 `test/host/remolcd_pull_test.py`(新):INIT(8 B:宽/高/帧字节数,大端)→ LCDMEM
|
||||
偏移 0 以 **10 Hz** 重压拉整帧 3200 B,逐帧 XOR 校验 + 长度核对。每轮 LCDMEM 在固件侧都是
|
||||
「uiTask 入队 + 等 netTask 回执」的真往返——这就是"7003 在真实占用 uiTask"的压力形态。
|
||||
|
||||
| 侧 | 读数 | 判据 |
|
||||
|---|---|---|
|
||||
| 网络侧·第一轮 | 150 s:请求 1500 / 应答 1500、CRC 错 0、超时 0,往返 min 35 / avg 63 / max 88 ms | **串口发送期间 7003 不超时** ✓(4208 B 线上 59.6 ms 与拉帧并行,应答照样 100 % 到手) |
|
||||
| 网络侧·第二轮 | 60 s 隔离窗口:700 / 700、零错零超时,max 131 ms | 同上,重复性 ✓ |
|
||||
| 屏板侧·60 s 隔离窗口(日志 clear 后开窗,计数只读窗口内) | rx +233 帧(278 ms/帧节拍精确)、**`drop` 零新增、`kerr=0 abor=0 bad=0`、`off=0`、全程无 OFFLINE**,`tx` 追平 `rx`(末帧 59.6 ms 采样窗差 1) | **7003 拉帧期间屏板每召必答** ✓(屏板 205 ms 超时计数在主机侧看不到,每召必答是其不超时的充分证据) |
|
||||
| 画面一致性 | 机制证明:两出口读**同一份活显存**(`ui_port_get_framebuffer()` 单点),无第二份账可漂;7003 末帧已 `--dump` 成 PBM/PNG 留档(`ci-artifacts/`,不入库) | 目视对图为机会性复核 |
|
||||
|
||||
**观察项(不属 4-1 判据,如实记)**:本轮上电累计 `len=7`(7 次 IDLE 断帧尺寸 ≠ 6)。
|
||||
出现窗口夹在"烧录后冒烟 23:49(未截 len 字段)"与"首轮重压结束 00:15(已 len=7)"之间,
|
||||
**不能排除与首轮 150 s 重压相关**;但上一上电周期 10 min 窗(§5.5)`len=0`,同一份固件,
|
||||
说明非确定性、疑瞬态;第二轮 60 s 隔离窗口**零新增**,且全程 `crc=0 fmt=0`、`ok` 增速与
|
||||
`rx` 同频——有效帧一帧没少,功能影响为零。若后续再现再查(首查方向:断帧尺寸 7 的那一包
|
||||
是否伴随 `err` 位——若是 NE 噪声尾巴粘进下一包,属杜邦级线噪的正常形态)。
|
||||
|
||||
## 6. 坑账本(都已在代码注释/规划文档留痕,这里只汇总)
|
||||
|
||||
1. 中文字面量 → ARMCC `#870-D`(源码无 BOM 按本地代码页解)。日志字面量纯 ASCII。
|
||||
2. `@test` 的 forbidden `FAIL;ERROR;HardFault;[ERR]` **大小写不敏感**——日志字段名带
|
||||
fail/error/abort 会把启动自检判红(`fail=0` 实测中招,已改 `kerr=`/`abor=`)。
|
||||
3. `ReceiveToIdle_IT` 一次性:漏重挂 = 链路永久静默;ORE 不自己清 = 一次噪声打死接收。
|
||||
4. NE/FE 非阻塞、ORE 阻塞:重挂判据用 `RxState==READY`,不看错误位。
|
||||
5. CH340 收不了 700 kbaud 连续流(4208 B 只到 ~2373 B)——**收不全 ≠ 固件没发**,
|
||||
同轮 `tx==done` 即自证。
|
||||
6. `.ioc` 不保存 UART 波特率;改波特率只能在 `USER CODE` 覆盖后重调 `HAL_UART_Init`。
|
||||
7. 纯注释改动也会让体积凭证报"不同码":先重建、再 `ram_baseline --update`。
|
||||
|
||||
## 7. 尚未实测项(按优先级)
|
||||
|
||||
| # | 项 | 怎么测 | 状态(2026-10-29 夜) |
|
||||
|---|---|---|---|
|
||||
| 1 | **7 个物理键逐个按** | 真屏板已就位:每按一键,日志 `keys=` 应 +1、`kq` 恒 0,画面动作对照 `docs/产品需求/产品需求设计文档.md` §8.2 菜单树;键 9 应只出 `unmapped` 日志 | **半闭环**:10 min 窗口 `keys=53 kq=0`(必到、零丢弃)已实测(§5.5);剩"逐键动作合菜单树 + 键 9 出 unmapped 日志"需边按边看 |
|
||||
| 2 | **10 min 连续零错**(屏板在线) | 挂机后 `grep SBLINK` 抽末行:`crc=len=fmt=err=rearm=bad=kerr=abor=0`、`tx≈rx`、`done=tx` | ✅ **2026-10-29 实测通过**(§5.5,rx=4258 零错) |
|
||||
| 3 | **拔插回归**(正式版) | 拔屏板线 ≥10 s → 一条 OFFLINE;插回 ≤2 s 续图、一条 back online;期间 7003 与日志口不受影响 | 冒烟时被动验过一半(对调线前 off=1、对调后自动恢复);主动拔插 + 7003 并发面未测 |
|
||||
| 4 | **uiTask 单轮增量**(判据"画面静止时 memcmp 成本 ~20 µs") | 画面静止 1 min,对比开屏板前后的 `[PROF] uiw`;按键刷屏时段观察 `uiw` 尖峰 ≈ 组帧 0.7 ms 以内 | 未测(§5.5 已给出组帧侧成本上限 `max=812 µs`,`[PROF]` 对照仍缺) |
|
||||
| 5 | **2 h 长稳跨自复位** | 挂机 ≥5000 s + 余量:零误判离线(自复位 ≈1 s 空窗不该出 OFFLINE)、零 kerr/abort、恢复自动 | 无 |
|
||||
| 6 | **召唤→应答延迟 30 µs~5 ms / 帧内无 ≥28 µs 间隙 / 59.6 ms** | 逻辑分析仪挂 `U5.A10/B10`;或 FTDI/CP210x 适配器 + `screenlink_bench.py --reply` 只能验整帧时长与内容(USB 1 ms 粒度量不了间隙) | 仪器 |
|
||||
| 7 | **帧 B 与 `g_ui_fbuf` 逐字节比** | FTDI/CP210x 收满 4208 后 `--dump` 出 PBM 对 UI 目检;严格逐字节需 PC 收全帧脚本比对(CH340 做不了) | 仪器 |
|
||||
| 8 | **双通道并发**(批 4 的 4-1) | 挂 DTU-RemoteLCD 持续拉帧 + 屏板同时召唤:两侧画面一致、串口发送期 7003 不超时、7003 拉帧期屏板不超时 | ✅ **2026-10-30 实测通过**(§5.7:7003 侧 2200 拉帧零错零超时;屏板侧隔离窗口每召必答、零新增丢弃;画面一致由"同一份活显存"机制保证) |
|
||||
| 9 | **7003 拥塞时串口照常应答** | 制造 netTask 往返卡顿(对时风暴/客户端不读),屏板侧应零超时、画面停最后一帧——"起发不在任务态"的直接证据 | 无 |
|
||||
| 10 | **状态灯逐位**(批 3 已实现,待目视) | ID 1 运行灯 1 Hz 闪(肉眼即见);ID 3 = 挂 104 主站(`test/host/iec104_master.py`)后亮、拆线灭;ID 4 = 拔采样前端后亮、插回灭;**ID 2 / ID 5 在产品构建恒灭**(未标定闭锁,`protect_task.c` 的 VALID 门禁——与屏上"保护动作 N"同一已知口径),要验证这两路得等标准源标定后或临时构建 | 真屏板已就位 |
|
||||
| 11 | ~~LOGO 断电留存~~ | **随 LOGO 出局划掉**(用户裁定 2026-10-29):不下发 LOGO,`sb_proto` 的 LOGO 组帧函数列为待删除 | — |
|
||||
| 12 | **键 9/10 语义裁定**(决策四) | 需要产品侧定 RESET/FACTORY 行为,代码已留 unmapped 分支 | 人 |
|
||||
| 13 | 真屏板 ORE 路径 | 主机侧只能被动等:若某天 `bits` 出现 0x08,看 `rearm` 是否跟涨;台架造不出(CPU 读 DR 太快),已由主机单测按 RM0090 覆盖 | 机会性 |
|
||||
|
||||
第 2 项已代跑通过(§5.5);第 3、4 项接线不动就能跑;第 1 项剩下半边需要你手按。
|
||||
1. **`@test` 的 forbidden 标记(`FAIL;ERROR;HardFault;[ERR]`)大小写不敏感**——日志字段名带 fail / error / abort
|
||||
会把启动自检判红,本模块统计行用 `kerr=` / `abor=` / `off=` 命名。
|
||||
2. **`ReceiveToIdle_IT` 是一次性的**:漏重挂 = 链路永久静默,且没有任何报错;ORE 不自己清,一次噪声打死接收。
|
||||
3. **NE/FE 非阻塞、ORE 阻塞**:重挂判据用 `RxState == READY` 而不是错误位;台架"NE 每帧出现 + `rx_ok` 照涨"
|
||||
= 能用的告警,**不是**故障(无屏蔽杜邦线常态);真故障形态是 ORE(`bits=0x08`),本链路 6 B/280 ms 不该出现。
|
||||
4. **CH340 收不了 700 kbaud 全帧**(§3.3)——收不全 ≠ 固件没发,`tx == done` 自证,别据此改固件。
|
||||
5. **`.ioc` 不保存 UART 波特率**:改波特率只能在 `USER CODE` 区覆盖 `Init` 后重调 `HAL_UART_Init`(§2)。
|
||||
6. **全局 UART 回调是工程单例**:新通道只在 `rs485.c` 加转发行、不自写一份(§2)——"中断进来了但没人处理"
|
||||
的表象是"屏板一直超时",极易误判成协议错。
|
||||
7. **帧缓冲不可上栈、不可进 CCM**(§7)——放错段不报错,只是 DMA 读不到、发出去的是旧数据。
|
||||
8. **`info[0..3]` 双解释**:先判 `info[4..5]` 魔数再解释这 4 字节(普通图 = LED 掩码、LOGO = 宽高),
|
||||
顺序反了会把 LED 掩码当宽高。
|
||||
9. **纯注释改动也会让体积凭证报"不同码"**:先重建、再 `ram_baseline.py --update`。
|
||||
|
||||
Reference in New Issue
Block a user