diff --git a/docs/README.md b/docs/README.md index 49f982f4..22f4b705 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,7 +11,7 @@ ``` docs/ ├── README.md 本索引(阅读顺序 + 全量清单) -├── 任务规划/ 统一待办(README 单一入口 + 5 个职责文件) +├── 任务规划/ 统一待办(README 单一入口 + 6 个职责文件) ├── 产品需求/ 产品需求设计文档(面向产品的需求基线) ├── 架构设计/ 全局架构与设计方法论 ├── 子系统设计/ 各功能子系统的设计说明 @@ -241,7 +241,7 @@ docs/ | 位置 | 内容 | |---|---| -| [`任务规划/`](任务规划/README.md) | **统一待办入口(6 个文件)**:`README.md` = 现在做什么与全项目顺序;`UI补齐.md` = 当前唯一执行清单;`登记簿.md` = 后续任务、外部窗口与长期观察;`数据可信.md` = 两项阻塞任务及代码引用的设计约束;`暂缓-RS485.md` 与 `测试与架构专项.md` = 两个专项。完成即删,重复内容只保留一个归属 | +| [`任务规划/`](任务规划/README.md) | **统一待办入口(7 个文件)**:`README.md` = 现在做什么与全项目顺序;`UI补齐.md` = 当前唯一执行清单;`登记簿.md` = 后续任务、外部窗口与长期观察;`数据可信.md` = 两项阻塞任务及代码引用的设计约束;`暂缓-RS485.md`、`测试与架构专项.md`、`屏板链路.md` = 三个专项。完成即删,重复内容只保留一个归属 | | `硬件平台/` | 网表详细分析报告与语义图(V0.0 / V0.1);**[`ADC通道映射说明.md`](硬件平台/ADC通道映射说明.md)——V0.1 十七路 ADC 语义/网名/通道号权威映射表(固件取数、UI 显示、自检注入以此为准)** | | `芯片手册/` | 外部芯片数据手册转录(CH395F / GD5F2GQ5UE / SD2506 / TPAFE5160 / RTL8305NBI 等) | | `images/` | 文档技术配图(`.svg` 产物,由 `tools/arch_diagram/`、`tools/refgraph/` 生成) | diff --git a/docs/产品需求/产品需求设计文档.md b/docs/产品需求/产品需求设计文档.md index 2c50f521..d4af3f59 100644 --- a/docs/产品需求/产品需求设计文档.md +++ b/docs/产品需求/产品需求设计文档.md @@ -1061,6 +1061,13 @@ sequenceDiagram > 见 `Src/main.c` 时钟配置),`42 000 000 / 700 000 = 60` 整除 → `USARTDIV = 60.0`、`BRR = 0x3C0`, > 误差 **0 %**。若改时钟树或改用 HSI,必须重算——700 kbaud 下 2 % 误差已足以丢帧。 > 收发器须选 **≥ 1 Mbps** 档(3485 / 自动方向类),700 kbaud 不是普通 485 的常规工况。 +> +> ⚠ **700 kbaud 在本板现有 485 通道上物理做不到**(2026-10-28 核查):唯一的板侧收发器 +> `U15 = TP8485E-SR` 速率上限 **250 kbps**(`docs/芯片手册/TP8485E-SR.md`),差 2.8 倍。 +> 而 4208 B 在 250 kbps 下需 **168.3 ms**,已占满屏板 200 ms 应答窗口的 84 %,无实用余量。 +> 出路只有两条:**换 ≥1 Mbps 收发器**,或**屏板降速并同步放大 `RS485_ACK_COUNT_MAX`**。 +> 这是本项**唯一的硬件阻塞点**,处置方案与代价见 `docs/任务规划/屏板链路.md` §2 决策一。 +> 软件侧不受影响——可先用 TTL 直连在台架跑通全链路,再等硬件定版。 **帧 A:召唤 / 按键(屏板 → 主机,固定 6 B)** @@ -2506,6 +2513,9 @@ pre-commit run --all-files > **方向相反,别写错**:网络通道是"**客户端召唤、设备应答**";串口通道是"**屏板召唤、设备应答**"。 > 两条通道共用同一份显存快照,但**帧格式、校验、时序完全不同**(网络用 `0xAA/0xBB` + XOR, > 串口用裸二进制 + CRC-16/MODBUS),不能共用组帧代码。 +> +> **执行细则另立**:逐项任务的改动点、判据、代码骨架、台架联调步骤与风险清单在 +> `docs/任务规划/屏板链路.md`;本节只保留**需求视角**的缺口与顺序,不复制执行细节。 | 序 | 任务 | 改动点 | 判据 | 跨端 | |---|---|---|---|---| diff --git a/docs/任务规划/README.md b/docs/任务规划/README.md index ef880d5d..7a72642e 100644 --- a/docs/任务规划/README.md +++ b/docs/任务规划/README.md @@ -13,6 +13,7 @@ | ADC 标定为什么没做、保护为什么不动作 | `数据可信.md` | | RS-485 为什么冻结、重启要满足什么 | `暂缓-RS485.md` | | 测试与架构还剩什么约束 | `测试与架构专项.md` | +| 怎么让主机跟屏板通信、先决条件是什么 | `屏板链路.md` | **一件事只有一个归属。** 本文件只负责导航与顺序,不复制各任务的验收明细; `登记簿.md` 不再复制当前 UI 任务,`数据可信.md` 的其余章节是被代码引用的设计约束, @@ -63,6 +64,7 @@ docs/测试说明/模拟器差距清单.md §2.3「开机账本 boot_stats」一 | **6** | 保护元件与采集增强 | 等待标准源或硬件改版 | 保护先解除未标定闭锁;三相同时刻类功能等 V0.2 硬件 | | **7** | 验证窗口事项 | 逢条件顺带 | CI runner、PC peer、CH340、负向故障注入条件到位时执行 | | **8** | 独立模块与暂缓项 | 当前不排 | 开入开出、故障录波、SNTP 单独立项;RS-485 与内存优化按各自重启条件 | +| **独立** | **屏板链路(对上屏板)** | **规划已出(未开工)** | 独立立项,插在任一顺位之间都能做,不占主序列;**先决条件是硬件决策**——本板现有 485 收发器只到 250 kbps,而屏板固件写死 700 kbaud(见 docs/任务规划/屏板链路.md §2 决策一);**软件可先用 TTL 台架验证,不必等硬件** | ### 2.1 关键依赖 diff --git a/docs/任务规划/屏板链路.md b/docs/任务规划/屏板链路.md new file mode 100644 index 00000000..a3eb0784 --- /dev/null +++ b/docs/任务规划/屏板链路.md @@ -0,0 +1,314 @@ +# 屏板链路任务规划(主机侧执行细则) + +> **本文定位**:让本产品**能跟 `DTU-LCD` 屏板真正通信**的执行级任务规划——顺序、改动点、判据、联调步骤、风险。 +> **需求侧**(这条链路要求做什么、与参考的差异)在 `docs/产品需求/产品需求设计文档.md` §5.16 与 §9.13,本文不重复。 +> **协议事实源**在屏板固件工程 `D:\Code\DTU Code\DTU-LCD`(不在本仓库)。 +> +> **边界**:只做**主机侧**。屏板固件不改(除非走「决策二·方案 B」要求它降速,那是屏板侧的事)。 +> 屏板实物是验收前置条件——没有实物,本文只能做到"帧发出去了"。 + +--- + +## 0. 先看结论:三个必须先解决的硬约束 + +在写任何代码之前,这三条决定了这件事**能不能按 700 kbaud 做成**: + +| # | 硬约束 | 实测值 | 后果 | +|---|---|---|---| +| 1 | **本板现有 485 收发器只到 250 kbps** | `U15 = TP8485E-SR`,手册 "300 bps to 250 kbps"(`docs/芯片手册/TP8485E-SR.md`) | **700 kbaud 在这条通道上物理不可能**,差 2.8 倍 | +| 2 | **屏板 200 ms 应答窗口 + 4208 B 帧 ⇒ 波特率下限** | 4208 B × 10 bit = 42 080 bit;700 kbaud = **60.1 ms**;250 kbps = **168.3 ms** | 250 kbps 虽勉强塞进 200 ms,但只剩 16 % 余量、且是收发器极限值 → **不可用**。留 50 % 余量需 **≥ 500 kbaud** | +| 3 | **现有 RS-485 驱动装不下长帧** | `RS485_RX_BUF_SIZE = 256`、`RS485_TX_MAX_LEN = 256`(`Drivers/BSP/RS485/rs485.h`) | 4208 B 会被 `rs485_transmit()` 直接判 `BSP_ERR_PARAM` 拒发 | + +**结论**:第 1 条是**唯一可能让整件事搁浅的点**,必须先定(见 §2 决策一)。 +第 2 条是它派生的算术,第 3 条纯软件,改起来不难。 + +好消息是:**主机侧 RX 只需要 6 B**(屏板发的是 6 字节召唤帧),4208 B 只出现在**发送方向**, +所以内存和缓冲压力比想象中小得多(§1.3)。 + +--- + +## 1. 现状盘点 + +### 1.1 已经具备的(不用重建) + +| 能力 | 在哪 | 怎么用 | +|---|---|---| +| 取 3200 B 显存 | `Drivers/BSP/UI/ui_net_sink_backend.h` 的 `ui_net_sink_get_lcdmem()` | **直接复用**,与 7003 通道同一数据源,天然一致 | +| 注入按键 | 同上,`ui_net_sink_inject_key(uint8_t)` | **直接复用**,串口与 7003 走同一入口,无需改 `ui_net_sink.c` | +| 1bpp 显存 | `g_ui_fbuf`(`ui_port.c`),`UI_FB_SIZE = 3200` | 160×160÷8,与屏板 `Picture[3200]` 逐字节同构 | +| 半双工 DE 时序经验 | `Drivers/BSP/RS485/rs485.c`(拉 DE → 等建立 → 发 → 等 TC → 收 DE) | **照思路,不照代码**(长帧限制见 §1.2) | +| UART 外设已初始化 | `Src/usart.c`:USART1/2/3、UART4/5 全部 `MX_*_Init()` 已调用(`Src/main.c:130-137`) | 端口是现成的,只需改波特率与补 DE GPIO | +| 静态任务分配 | `Inc/FreeRTOSConfig.h` `configSUPPORT_STATIC_ALLOCATION = 1` | 可用 `xTaskCreateStatic`,**不占 RTOS 堆**(堆已满,见 §1.3) | + +### 1.2 缺什么(要新建) + +1. **协议层**:CRC-16/MODBUS(仓内没有现成的,`App`/`Drivers` 里搜不到)、帧 A 解析、帧 B 组帧。 +2. **传输层**:700 kbaud 配置、DE 控制、6 B 收 + 4208 B 发的时序。 +3. **状态灯映射表**:19 路业务状态 → 屏板 LED 位的表,**参考源码里没有,必须自己定义**(`docs/产品需求/产品需求设计文档.md` §5.16.3 ①)。 +4. **一个宿主**:谁来跑这个链路(决策三)。 + +### 1.3 内存与 DMA 实况(决定实现方式) + +| 项 | 现值 | 加屏板链路后 | 判断 | +|---|---|---|---| +| `RW_CCM` | 57,376 / 65,280 = **87.9 %**(门禁 92 %) | 不动(用静态任务,不占堆) | ✅ | +| `RW_IRAM1` | 81,536 / 131,072 = **62.2 %** | +4208 B(发送缓冲)≈ **65.4 %** | ✅ 宽裕 | +| 接收缓冲 | — | **16 B 足够**(屏板只发 6 B 召唤帧) | ✅ 别照抄屏板的 4208 B | +| RTOS 堆 | 57,344 B 配置,**已满** | **不加任务则不动** | ⚠ 新增动态任务会创建失败 | + +**DMA 不是必须的**。4208 B 在 700 kbaud 下只占 60 ms,放在**独立任务里阻塞发送**完全可行 +(屏板 275 ms 才问一次,200 ms 内答完即可)。省掉 DMA 就省掉了一堆流冲突: + +> ⚠ 若要上 DMA:USART3_TX 只能用 `DMA1_Stream3`(ch4) 或 `DMA1_Stream4`(ch7), +> 而这两条**已被 SPI2 占用**(`Src/spi.c:198/216`)。USART2 的 `DMA1_Stream5/6` 才是空的。 +> 这是**选端口时必须一起考虑**的一条(见决策二)。 + +--- + +## 2. 四个必须先定的决策 + +### 决策一:波特率与收发器(**阻塞项,最优先**) + +屏板固件把 `BAUDRATE` 编译成 **700000**(`DTU-LCD/Drivers/BSP/RS485/rs485.h`),改不了我们这边。 +而我们板上唯一的 485 收发器 `TP8485E-SR` 上限 **250 kbps**。三条出路: + +| 方案 | 做法 | 代价 | 可行性 | +|---|---|---|---| +| **A. 换收发器** | 把 `U15` 换成 ≥1 Mbps 的 485 器件(THVD1450 / MAX3485E / SP3485 类,3.3 V、SOIC-8 同封装优先) | 改板或飞线;隔离侧电源域不变 | ✅ **推荐**。唯一代价是硬件 | +| **B. 屏板降速** | 屏板把 `BAUDRATE` 改到 250 kbps 以下,**同时**把 `RS485_ACK_COUNT_MAX`(200 ms)放大 | 改屏板固件 + 两个参数要配套算;250 kbps 下 4208 B 已占 168 ms | ⚠ 勉强可行,余量极小;≤115200 则 365 ms **必然超时** | +| **C. TTL 直连** | 绕开两侧收发器,主机 UART ↔ 屏板 UART2 的 TTL 侧(PA2/PA3)直连 | 只能同机箱短距;屏板要引出 TTL 焊盘 | ⚠ 台架验证可以,**不能当产品形态** | + +**决策要点**: +1. 先确认**屏板自己的收发器型号与速率上限**——屏板硬件资料不在 `DTU-LCD` 仓库里(只有固件)。 + 如果屏板侧也是 250 kbps 器件,那 700 kbaud 是**两边都做不到**,方案 B 才是唯一路。 +2. 方案 A 换器件时,**DE 极性与建立时间**要重新核(新器件的 tPZH/tPZL 可能比 562 ns 大)。 +3. **台架可以先用方案 C 跑通协议**(不动硬件、不依赖收发器),把软件全部做完, + 再回头等硬件决策——**这是让软件不空等的关键**,建议按这个顺序排。 + +### 决策二:用哪个 UART + +| 端口 | 引脚 | 现状 | DMA 可用性 | 评价 | +|---|---|---|---|---| +| **USART2** | PD5 / PD6 | 已初始化、未接 | `DMA1_Stream5/6` **空闲** | ✅ **软件最优**:DMA 有退路,引脚独立 | +| **USART3** | PB10 / PB11 | 已初始化、未接;**经 U5 子板连接器直连 TTL** | `DMA1_Stream3/4` **被 SPI2 占** | ⚠ 硬件最顺(U5 上就是 TTL,若屏板做成子板正好),但**没有 DMA 退路** | +| UART4 | PC10 / PC11 + **PA15(DE)** | RS-485 采集簇(**冻结**),`g_rs485` 绑的就是它 | — | ❌ 不要动,会牵动冻结簇的验收基线 | +| UART5 | PC12 / PD2 | 已初始化、未接 | `DMA1_Stream0/7` 空闲 | 可用,但无硬件 DE | + +**建议**: +- 若**走 485 且需要新收发器**(决策一方案 A)→ 选 **USART2**(DMA 有退路、与 U5 无关)。 +- 若**屏板作为子板插 U5、走 TTL**(决策一方案 C 且产品化)→ 选 **USART3**(U5 上现成)。 +- **两者都先不写死**:端口用句柄参数传入,模块不写死外设实例(照 `rs485_handle_t` 的做法)。 + +> ⚠ 网表报告口径有矛盾,接线前必须实物核对:`docs/硬件平台/V0.0_网表详细分析报告.md` 的摘要行写 +> "RS485#4 = **UART5**(PC12/PD2)",但同一文档的 `docs/硬件平台/V0.0_网表详细分析报告.md` §4.4 逐脚接线 +> (U11→`ST_TX4`→U15)与固件 +> (`App/app/app_main.c:197` `rs485_init(&g_rs485, &huart4, ST_DIR4_…)`)都指向 **UART4 + PD0**。 +> 摘要行与该节自相矛盾,摘要行疑误——**以实物为准,别按摘要行接线**。 + +### 决策三:谁来跑这个链路(宿主) + +| 方案 | 优点 | 缺点 | +|---|---|---| +| **独立任务(`xTaskCreateStatic`)** | 与 `uiTask` 隔离,60 ms 阻塞不拖渲染;不占堆 | 多一个任务要登记;`Src/freertos.c` 是 CubeMX 生成物,**不在那里加** | +| 塞进 `uiTask` | 零新增任务 | 7003 的 `LCDMEM` 应答最坏 ≈4 s,会把 200 ms 应答窗口撑爆 → **违反"双通道互不拖累"判据** | +| 塞进 `defaultTask` | 零新增任务 | 该任务周期 100 ms 量级,6 B 召唤帧可能被延到 >200 ms → 超时 | + +**建议:独立任务,静态分配**。理由:唯一能满足 200 ms 硬窗口、又不吃堆(堆已满)的做法。 +- 栈 **768 B** 足够(调用链只有 `memcpy` + `HAL_UART_Transmit` + 少量局部变量,4208 B 缓冲是静态的,不在栈上)。 +- 优先级 **低于 `uiTask`、高于 `defaultTask`**(屏幕是辅助功能,不该抢保护/采集)。 +- **不加入看门狗必需位掩码**(`0x37` 不动):屏板链路挂了只是没画面,不该复位整机。 + +### 决策四:屏板键值 9 / 10 的语义 + +屏板 `KEY_TYPE`:`9 = KEY_RESET`、`10 = KEY_FACTORY`;参考主机把 9/10 当 `F1` / `F1|ENT`。 +我们 `ui_port.h` 的 `UI_KEY_*` 只有 6 个(`ENTER 0x20 / UP 0x02 / DOWN 0x40 / ESC 0x01 / LEFT 0x10 / RIGHT 0x08`), +**没有 F1/F2,也没有 RESET/FACTORY**。 + +| 选项 | 做法 | 影响 | +|---|---|---| +| **映射为「复位」** | 9 → 触发 UI 的复位页(`ui_page_reset`)或直接软复位 | 需产品确认"按复位键就重启"是否可接受 | +| **映射为 ESC** | 9 → `UI_KEY_ESC` | 最省事,但语义丢了 | +| **暂不映射** | 收到 9/10 直接丢弃并记一行日志 | 保守,等产品定 | + +**建议先按「暂不映射 + 日志」实现**,把决定权留给产品,别在协议层猜语义。 + +--- + +## 3. 任务分解 + +分 4 批。**批 0 不通过就不写代码**(除了台架 TTL 验证那条路)。 + +### 批 0 · 可行性与硬件定版(3 项) + +| 序 | 任务 | 改动点 | 判据 | +|---|---|---|---| +| 0-1 | **确认屏板收发器型号与速率上限** | 只读屏板实物 / 原理图,结论回填本文 §2 决策一 | 拿到型号,且写明"是否支持 700 kbaud" | +| 0-2 | **定 700 kbaud 方案(A/B/C)** | 出结论,不改代码 | 三选一落纸,含代价与验收方式 | +| 0-3 | **选端口与 DE 引脚** | 定 `USART2` 还是 `USART3`;DE 用哪个空闲 GPIO(极性按本板收发器,**不可照抄屏板 PA1 低=发**) | 引脚表落纸,且与 `.ioc`、网表、实物三方核对过 | + +> 批 0 期间可以先做**批 1 的第 1 项**(协议层纯软件、不碰硬件),不冲突。 + +### 批 1 · 物理层打通(4 项) + +| 序 | 任务 | 改动点 | 判据 | +|---|---|---|---| +| 1-1 | **协议层模块(纯逻辑,可主机单测)** | 新建 `Drivers/BSP/ScreenLink/sb_proto.c/h`:CRC-16/MODBUS(表与屏板 `auchCRCHi/Lo` 逐字节一致)、帧 A 解析、帧 B 组帧 | 主机单测:给定 6 B 召唤帧能算出屏板同样的 CRC;组出的 4208 B 帧 CRC 自校验通过 | +| 1-2 | **波特率与格式落地** | `STM32F407-Demo.ioc` 里把选定 UART 改 **700000 / 8N1 / 16 倍过采样**(改 `.ioc` 而不是只改 `Src/usart.c`,否则 CubeMX 重生成会丢);`Src/usart.c` 只写 USER CODE | 示波器/逻辑分析仪实测位宽误差 **< 1 %**(42 MHz ÷ 700 kHz = 60 整除,理论误差 0) | +| 1-3 | **DE 控制与收发切换** | 新建 `Drivers/BSP/ScreenLink/sb_link.c/h`:拉 DE → 等建立(**不要用 `HAL_Delay(1)`,用 DWT 忙等 ≤10 µs**)→ 发 → 等 TC → 松 DE | 逻辑分析仪上看 DE 与最后一字节的时序:DE 必须在 TC 之后才翻转,不得截断末字节 | +| 1-4 | **6 B 召唤帧接收** | 选定 UART 开 IDLE 中断接收(缓冲 **16 B**);`HAL_UARTEx_RxEventCallback` 里**加本端口分支**(现有回调只认 `g_rs485.huart`,不加分支会静默收不到);`HAL_UART_ErrorCallback` 同样要加 | 屏板每 275 ms 发来的 6 B 能稳定收到,`Size == 6`,CRC 通过率 100 % | + +> ⚠ **`HAL_UARTEx_RxEventCallback` / `HAL_UART_ErrorCallback` 是全局单例**(`Drivers/BSP/RS485/rs485.c:189/205`), +> 现在只判 `huart == g_rs485.huart`。新端口必须加分支,否则中断进来了但没人处理—— +> 表象是"屏板一直超时",很容易误判成协议错。 + +### 批 2 · 协议实现(5 项) + +| 序 | 任务 | 改动点 | 判据 | +|---|---|---|---| +| 2-1 | **帧 B 组帧** | `sb_link.c`:取 `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**;CRC 覆盖范围正确;填充字节全 0 | +| 2-2 | **应答时序** | 收到帧 A 后**立即**回帧 B,不排队、不攒批 | 逻辑分析仪测:从帧 A 末字节到帧 B 首字节 **< 5 ms**;整帧 < 70 ms | +| 2-3 | **按键上行** | 帧 A 的 `key` 字节按屏板 `KEY_TYPE` 查表 → `ui_net_sink_inject_key(UI_KEY_*)`;`0` = 无键直接返回 | 7 个物理键逐个按下,UI 动作与 `docs/产品需求/产品需求设计文档.md` §8.2 菜单树一致 | +| 2-4 | **宿主任务** | `App/app/app_main_init()` 里 `xTaskCreateStatic` 建任务(栈 768 B 静态数组);句柄登记进 `App/task/task_handles.h` 并注明"非 CubeMX 创建" | 任务稳定运行 30 min,栈高水位余量 > 40 % | +| 2-5 | **链路监视与容错** | 连续 **≥10 s** 无召唤帧才判"屏板离线"(**不能按 500 ms 判**——屏板 ≈5000 s 会自复位一次,约 1 s 空窗);CRC 错帧静默丢弃并计数 | 拔掉屏板 10 s 后置离线标志;插回自动恢复;屏板自复位不被误判 | + +### 批 3 · 状态灯与 LOGO(3 项) + +| 序 | 任务 | 改动点 | 判据 | +|---|---|---|---| +| 3-1 | **状态灯位映射表** | 定义"业务状态 → 屏板 LED 位"的表(**参考源码里没有这张表,必须自定**)。建议:ID 1 = 运行、ID 2 = 告警、ID 3 = 通信、其余按 `docs/产品需求/产品需求设计文档.md` §5.8 的 6 路语义扩展;**bit0 恒 0**(ID 0 是硬件常亮的电源灯)、**bit20~31 恒 0** | 表落纸并进代码;每位点亮/熄灭可单独验证 | +| 3-2 | **状态灯下发** | 32 位小端填进 `info[0..3]`(`info[0]` = bit0~7) | 主机改掩码 → 屏板对应 LED 变化,**LED 0 不受掩码影响** | +| 3-3 | **LOGO 下发(一次性)** | 独立入口(不进常规刷新):`info[4..5] = 0x89 0x45`、`info[0..1]` = 宽、`info[2..3]` = 高(大端) | 下发后屏板显示 2 s 并写 Flash;**断电重启后 LOGO 仍在**;这帧不改 LED 状态 | + +> ⚠ LOGO 帧会把屏板 Flash `0x0803F000`(扇区 126)**整块覆盖**,且屏板固件不校验是否同一张图。 +> 建议只在出厂配置/联调流程里提供,**不要挂到菜单上**。 + +### 批 4 · 并发与验收(3 项) + +| 序 | 任务 | 改动点 | 判据 | +|---|---|---|---| +| 4-1 | **双通道并发回归** | 7003 客户端拉帧 + 屏板召唤同时进行 | 两侧画面一致;串口 4208 B 发送期间 7003 不超时;7003 拉帧期间屏板不超时 | +| 4-2 | **长稳与自复位** | 连续跑 ≥2 h(跨过屏板一次自复位) | 零帧错误、零误判离线、UI 无可见卡顿 | +| 4-3 | **文档收口** | 把实测结论回填 `docs/产品需求/产品需求设计文档.md` §5.16.2(尤其**帧长 4208 vs 3208 那条待复核项**)与 §9.13 | 待复核项清零;§9.13 对应行销项 | + +--- + +## 4. 代码骨架与关键实现点 + +### 4.1 模块划分 + +``` +Drivers/BSP/ScreenLink/ +├── sb_proto.c/h 纯逻辑:CRC-16、帧 A 解析、帧 B 组帧 ← 可主机单测 +└── sb_link.c/h 传输:UART、DE、任务宿主、状态灯映射表 ← 依赖 HAL,仅 MCU +``` + +**为什么拆两层**:协议层不碰 HAL 才能在 `test/host/` 里跑单测(项目既有做法:原样 include 生产 `.c`, +stub 提供 HAL)。屏板侧 CRC 有查表实现,**用同一张表做交叉验证**是最省事的正确性保证。 + +### 4.2 帧 B 组帧(逐字段) + +``` +偏移 0 3200 B ui_net_sink_get_lcdmem() 拷贝(160×160,1bpp,20 B/行,MSB 在前,1=白) +偏移 3200 4 B 状态灯 32 位小端:info[0]=bit0~7 … info[3]=bit24~31;bit0 恒 0 +偏移 3204 1 B info[4]:普通图填 0x00(非 0x89) +偏移 3205 1 B info[5]:普通图填 0x00(非 0x45) +偏移 3206 2 B info[6..7]:保留,填 0 +偏移 3208 998 B 填充,填 0x00(屏板不读,但参与 CRC) +偏移 4206 2 B CRC-16/MODBUS,覆盖 [0, 4205],高字节在前 +``` + +> ⚠ **`info[0..3]` 是双解释字段**:普通图当 LED 掩码、LOGO 图当宽高。区分只靠 `info[4..5]`。 +> **先判魔数再解释这 4 字节**,顺序反了会把 LED 掩码当宽高。 + +> ⚠ **帧长 4208 而不是 3208**:屏板接收侧实参是 `Check_CRC(DR, UART_RX_LEN)`,`UART_RX_LEN = 4208`, +> 即 CRC 取 `DR[4206]/DR[4207]`。只发 3210 B 会让屏板读到陈旧字节 → **CRC 必失败且静默丢弃** +> (屏板 `RS485_Process` 里 CRC 错只累加计数,不报错、不回帧,表象就是"屏板不亮")。 +> 这是**最容易踩且最难查**的一条,批 2 第一件事就是按 4208 组帧。 + +### 4.3 键值映射表 + +| 屏板 `key` | 语义 | `UI_KEY_*` | 备注 | +|---|---|---|---| +| 0 | 无键 | — | 直接返回,不产生事件 | +| 1 | ENTER | `0x20` | | +| 2 | UP | `0x02` | | +| 3 | DOWN | `0x40` | | +| 4 | ESC | `0x01` | | +| 7 | LEFT | `0x10` | | +| 8 | RIGHT | `0x08` | | +| 9 | RESET | **待定**(决策四) | 先丢弃 + 日志 | +| 10 | FACTORY | **待定** | 屏板无物理入口,理论上收不到 | +| 5 / 6 | ADD / DEC | — | **屏板无物理入口**,不会出现 | + +> 屏板 `key_configs[]` 只有 **7 项**(上/下/左/右/确认/取消/复位),可达值仅 `1/2/3/4/7/8/9`。 +> 别按 readme 的 8 键表实现。 + +### 4.4 不要做的事 + +- ❌ **不要改 `rs485.c` 的常量**(`RS485_RX_BUF_SIZE` / `RS485_TX_MAX_LEN` 改大会牵动冻结的采集簇基线)。 + 长帧走新模块,DE 时序照思路自己实现。 +- ❌ **不要在 `Src/freertos.c` 里加任务**——那是 CubeMX 生成物,重生成会丢。 +- ❌ **不要用 `HAL_Delay(1)` 做 DE 建立延时**(`rs485.c` 那样做是为了兼容它自己的场景): + 700 kbaud 下每帧多 1 ms 无所谓,但任务里阻塞 1 ms 是白给;用 DWT 忙等 ≤10 µs 更干净。 +- ❌ **不要把 4208 B 缓冲放到栈上**。 +- ❌ **不要按屏板 readme 的 3208 B 组帧**(见 §4.2 的警示)。 + +--- + +## 5. 台架联调步骤(可以逐级停下来) + +**核心思路:先不碰硬件、先用 TTL 把软件全部跑通,再等硬件决策。** 这样硬件没定时软件不空等。 + +| 步 | 做什么 | 需要什么 | 停下也能交付什么 | +|---|---|---|---| +| 1 | 协议层单测(`test/host/screenlink/`) | 无硬件 | CRC 与组帧的正确性,可回归 | +| 2 | PC 串口助手**模拟屏板**:手发 6 B 召唤帧,看主机回的 4208 B | USB 转 485 模块或 USB 转 TTL 模块 + 主机板 | 主机侧协议 100 % 可验证 | +| 3 | 反向:PC 收主机帧,用脚本核对 4208 B / CRC / 显存内容 | 同上 | 帧内容正确性(与 `g_ui_fbuf` 逐字节比) | +| 4 | **接真屏板**(TTL 直连,或经 USB 转 485 模块转接,绕过板上收发器) | 屏板实物 | 出图 + 按键 + 状态灯全链路 | +| 5 | 换成板上 485 通道(需决策一落地) | 换好收发器的主板 | 产品形态验证 | + +> 第 2、3 步是**性价比最高的两步**:不用屏板、不用改硬件,就能把 90 % 的软件问题挖出来。 +> 建议先做这两步,再谈硬件。 + +--- + +## 6. 风险与回退 + +| 风险 | 触发条件 | 影响 | 回退/应对 | +|---|---|---|---| +| **700 kbaud 硬件做不到** | 决策一判 A 但换器件来不及 | 整条链路卡住 | 走方案 C(TTL 直连)先出功能,硬件下版再补;软件不受影响 | +| 屏板侧收发器也不够快 | 屏板实物核对后发现 | 700 kbaud 两边都做不到 | 只能走方案 B(屏板降速 + 放大 ack 窗口),需屏板方配合 | +| 帧长按 3208 实现 | 照 readme 写 | CRC 全失败、屏板不亮 | 已写进 §4.2 警示;联调第一步就核 4208 | +| DE 极性接反 | 照抄屏板 PA1"低=发" | 总线一直被占、屏板收不到 | 本板 `DIR4` 是**高=发**(`rs485.c` `RS485_DIR_TX` = `GPIO_PIN_SET`);新通道按本板收发器定,逻辑分析仪一看便知 | +| 中断回调漏加分支 | 只改自己模块,忘改 `rs485.c` 的全局回调 | 收不到召唤帧,表象像协议错 | 批 1 第 4 项已列为判据 | +| 屏板自复位被误判离线 | 链路监视窗口设得太小 | 每 83 分钟误报一次 | 判据定为"连续 ≥10 s 无帧" | +| 状态灯语义返工 | 映射表拍脑袋定的 | 后期改表要重测 | 把表做成一张 `const` 数组,改表不改逻辑 | +| RAM 超门禁 | 误把 RX 缓冲也做成 4208 B | `RW_IRAM1` 逼近 92 % | RX 只要 16 B(§1.3) | + +--- + +## 7. 验收清单 + +做完这一项,要能同时满足(逐条可测,对应 `docs/产品需求/产品需求设计文档.md` §5.16.10 第 8~15 条): + +1. 屏板按 275 ms 召唤,主机每帧在 **≤200 ms** 内回 **4208 B**,连续 10 min 零超时。 +2. 帧 B 前 3200 B 与 `g_ui_fbuf` **逐字节一致**。 +3. 屏板 7 个物理键逐个按下 → UI 动作正确;无键时 `key = 0` 不产生事件。 +4. **状态灯**:掩码逐位对应,LED 0 恒亮、bit0 不影响任何灯。 +5. **双通道并发**:7003 与串口同时跑,互不拖超时、画面一致。 +6. **屏板自复位**(≈5000 s)不被误判离线,约 1 s 后自动续上。 +7. 拔插屏板:7003 侧不受影响,插回自动恢复,不用复位主机。 +8. LOGO 下发后断电重启仍在,且不改变 LED 状态。 + +--- + +## 8. 与其它工作的关系 + +- **与 `docs/任务规划/暂缓-RS485.md` 无关**:那条是"对下采集簇"(UART4 + `g_rs485`),本项是"对上屏板", + 两者**不共用外设、不共用驱动常量**。本项**不需要解冻** RS-485 采集簇。 +- **与 UI 补齐无关**:本项只消费 `ui_net_sink_get_lcdmem()` / `ui_net_sink_inject_key()`, + 不改任何页面。UI 页面已在 2026-10-28 收口,正好可以直接拿来当被测对象。 +- **不占用并行工作树名额的额外理由**:改的是新目录 + `app_main_init` 一处 + `task_handles.h` 一处 + + `.ioc` 一处,与主线其它工作写域几乎不重叠;但 `.ioc` 改动属**单点事项**(CubeMX 重生成), + 必须与其它改 `.ioc` 的工作串行。 +- **需求侧销项**:完成后回填 `docs/产品需求/产品需求设计文档.md` §9.13 与 §5.16.2 的待复核项。