docs(屏板链路): 按代码实况校正决策三的三处口径
三处口径按仓库实况改写:
- 看门狗必需位掩码现值是 0x1F7(App/util/task_watchdog.c 的
wd_required_mask:default/net/ftp/ui/timesync/adc/iec104/protect),
本项的口径是「一个位都不加」,不是引用某个掩码常量。
- RTOS 堆是「紧张」不是「动态任务必然创建失败」:抬栈后台架实测
free 16,400 / min_ever 8,464 B(口径 内存使用分析 §2.3);768 字的
退路任务只剩几 KB 余量,所以仍首选不新建。
- uiTask 那族阻塞里的「约 4 s」标成**推导上界**(netTask 内
ch395f_poll_cmd_status(4000U) 六处调用 + 调用方 portMAX_DELAY),
并补实测锚点:uis 峰 183 ms、uip 地板 61.3 ms、对时突发单轮 800 ms+。
补 DMA 收尾的现实约束:全工程没有任何 HAL_UART_TxCpltCallback /
RxCpltCallback 实现,HAL_UART_ErrorCallback 只有一处且只分发它自己
那条通道的句柄 → 三个回调都得自写并按 huart 句柄分发(写进 §1.3、
批 2 的 2-4、§6 风险行)。另记 .ioc 在仓库根、DMA2_Stream7 三处自洽。
§1.1 补新建任务的正确落点:Src/freertos.c 的 USER CODE 区 osThreadNew
是仓内先例,app_main_init 跑在 osKernelStart 之前、禁调创建类 API。
名目表加第四名目「数秒量级的等待」;defaultTask 的否决理由改挂到这
一族;§6 补一行采纳方案继承的 uiTask 挤占面(250 ms 日志排空、对时
突发);§7 加第三条另测:7003 拥塞时串口侧照常应答。
同时按「文档只写现状」清掉决策三里三处自我纠错措辞。
只改方案文档一份;需求文档不含这三处口径,本轮未动。
gate: 逐文件 check_doc_links --sections-strict(29 处引用 / 27 可判定)
与 check_ids_new 均 RC 0;合并后全量 ci_local 待他人技能文件
提交后补跑(主树仍有他人未提交的在飞文件)。
This commit is contained in:
+30
-12
@@ -34,7 +34,7 @@
|
||||
| 注入按键 | 同上,`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]` 逐字节同构 |
|
||||
| UART 外设已初始化 | `Src/usart.c`:USART1/2/3、UART4/5 全部 `MX_*_Init()` 已调用(`Src/main.c:130-137`) | 端口是现成的,只需改波特率(USART1 侧 TTL 直连,没有方向切换) |
|
||||
| 静态任务分配 | `Inc/FreeRTOSConfig.h` `configSUPPORT_STATIC_ALLOCATION = 1` | 走退路(独立任务)时**必须** `xTaskCreateStatic`——堆已满(§1.3);采纳方案不新建任务,这条只是备着 |
|
||||
| 静态任务分配 | `Inc/FreeRTOSConfig.h` `configSUPPORT_STATIC_ALLOCATION = 1` | 只有走退路(独立任务)时才用得上:仓内新建任务的先例是 `Src/freertos.c` 的 `USER CODE` 区里 `osThreadNew`(全工程没有一处调用 `xTaskCreateStatic`),**且不能建在 `app_main_init`**——它跑在 `osKernelStart` 之前,那里禁调任何创建类 API。采纳方案不新建任务,这条只是备着 |
|
||||
|
||||
### 1.2 缺什么(要新建)
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
| `RW_CCM` | 57,376 / 65,280 = **87.9 %**(门禁 92 %) | 不动(不新建任务、不占堆) | ✅ |
|
||||
| `RW_IRAM1` | 81,536 / 131,072 = **62.2 %** | +4208 B(发送缓冲)≈ **65.4 %**;双缓冲 8416 B ≈ **68.6 %** | ✅ 宽裕 |
|
||||
| 接收缓冲 | — | **16 B 足够**(屏板只发 6 B 召唤帧) | ✅ 别照抄屏板的 4208 B |
|
||||
| RTOS 堆 | 57,344 B 配置,**已满** | **不加任务则不动** | ⚠ 新增动态任务会创建失败 |
|
||||
| RTOS 堆 | 57,344 B 配置;最近一次台架实测 `free` 16,400 / `min_ever` **8,464 B**(告警线 3,072 B,口径 `docs/内存管理/RAM使用分析与优化方案.md` §2.3) | **不加任务则不动** | ⚠ 是**紧张**,不是「必然创建失败」:768 字(3 KB)栈 + TCB 只剩几 KB 余量 → 仍首选不新建 |
|
||||
|
||||
**DMA 不是可选项,是「整帧不断帧」的唯一保证。** 三个数别混:
|
||||
|
||||
@@ -71,6 +71,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 同一手法)。
|
||||
> **`.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` 核对)。
|
||||
|
||||
---
|
||||
|
||||
@@ -106,22 +111,28 @@
|
||||
|
||||
### 决策三:谁来跑这个链路(宿主)——**已裁定 2026-10-29:`uiTask` 组帧 + 中断点 DMA 起发,不新建任务**
|
||||
|
||||
**先把名目拆开**——原来把两件不同的事写进了同一格,方案就是在这儿走偏的:
|
||||
**先把四个名目拆开**——它们是四件不同的事,混在一起就会把实现方式选错:
|
||||
|
||||
| 数 | 它是什么 | 能不能避免 |
|
||||
|---|---|---|
|
||||
| **59.6 ms** | 4208 B 的**线上占用时间**(§1.3) | 不能(协议 + 物理);只有改屏板砍掉 998 B 填充才省 14.3 ms,本项不改屏板 |
|
||||
| **约 60 ms 的任务阻塞** | 「在任务里逐字节轮询发送」这个**实现选择**的成本 | **能**:整帧交 TX DMA 后,这 60 ms 里 CPU 占用为零 |
|
||||
| **205 ms / 280 ms** | 屏板的**放弃计时**与**下一轮召唤** | 不是成本,是截止期;真实边界见下面的说明与 §7 第 1 条 |
|
||||
| **数秒量级的等待** | `uiTask` / `netTask` / `defaultTask` 里那一族**等别的任务回话或等外设**的阻塞(实测与推导见方案表后的注) | 与屏板链路本身无关,但**决定谁不能当起发者**:任务不在就绪态,抬优先级救不了 |
|
||||
|
||||
| 方案 | 优点 | 缺点 | 结论 |
|
||||
|---|---|---|---|
|
||||
| **`uiTask` 组帧 + USART1 空闲中断点 DMA 起发** | 这条链路的人机交互语义(组帧、状态灯映射、注键、在离线、LOGO)**全留在 UI 上下文**,与 7003 同一份数据源、同一把笔;零新增任务、不占堆;起发延迟 = 中断延迟(µs 级),与 `uiTask` 这一轮卡不卡无关 | 组帧要占 `uiTask` 一次 `memcpy` + CRC(约 0.15 ms/帧,3.6 帧/s 约 0.05 %);多一个 4208 B 的 SRAM 发送缓冲;帧缓冲与 DMA 读取之间要做 dirty 或双缓冲 | **采纳** |
|
||||
| 独立任务里**阻塞**发送 | 与 `uiTask` 隔离;不占堆 | 那 60 ms 的 CPU 轮询本身就是断帧风险(§1.3);起发仍要等任务被调度到;多一个任务要登记栈、句柄、台账 | 退路:仅当要求「状态灯在 `uiTask` 卡死期间仍刷新」时才值得 |
|
||||
| 把起发挂在 `uiTask` 主循环里 | 改动最省 | `uiTask` 主循环现在就有**无上界**的等待:`ui_net_sink_poll()` → `net_accept` → `net_wait_result()` 走到 `xTaskNotifyWait(portMAX_DELAY)`(`App/task/ui_task.c`:505-511、`Drivers/BSP/NET/net_socket.c`:846-861);实测样本 `uisn` 154 ms 仍在段内,残余暴露见 `docs/UI设计/RemoDispBus协议与接收设计说明.md` 的「残留暴露」注(约 4 s)。**这类阻塞是等另一个任务回话——任务不在就绪态,抬优先级救不了** | 否决 |
|
||||
| 把起发挂在 `defaultTask` | 零新增任务 | 该任务周期 100 ms 量级,还挂 FTL 挂载 / 日志写入等无上界路径 → 起发延迟不可控 | 否决 |
|
||||
| 把起发挂在 `uiTask` 主循环里 | 改动最省 | `uiTask` 主循环现在就有**无上界**的等待:`ui_net_sink_poll()` → `net_accept` → `net_wait_result()` 走到 `xTaskNotifyWait(portMAX_DELAY)`(`App/task/ui_task.c`:505-511、`Drivers/BSP/NET/net_socket.c`:846-861);实测最坏见方案表后那组注。**这类阻塞是等另一个任务回话——任务不在就绪态,抬优先级救不了** | 否决 |
|
||||
| 把起发挂在 `defaultTask` | 零新增任务 | 周期 100 ms 量级只是表面;它同属「数秒量级的等待」那一族——FTL 挂载、日志排空、对时突发都压在这条任务上 → 起发延迟不可控 | 否决 |
|
||||
|
||||
**采纳方案的分工——起发者不等于业务归属**(原来那条否决理由把这两件事混成一件了):
|
||||
> **「数秒量级等待」那一行的实测锚点**(引用这一族时别把推导值当实测值):
|
||||
> - **≈4 s 是推导上界,不是实测**:netTask 里 `ch395f_poll_cmd_status(4000U)` 的忙等上限(`Drivers/BSP/CH395F/ch395f.c`:651,:1088 / :1103 / :1118 / :1133 / :1148 六处调用)+ 调用方 `portMAX_DELAY`;`docs/UI设计/RemoDispBus协议与接收设计说明.md` 里那个「约 4 s」就是这个推导数,**没有实测样本**。
|
||||
> - **实测值**:7003 往返 `uis` 峰 183 ms;`uiTask` 常态渲染地板 `uip` 61.3 ms(`docs/子系统设计/看门狗设计说明.md` §11.4);对时 relisten 突发把 `uiTask` 压成**单轮 800 ms 以上缺席**(`docs/任务规划/登记簿.md` 条目「reconcile 突发饿死 uiTask」,`uip` 在途 307 → 1083 ms)。
|
||||
> - 结论不变:这一族的延迟量级是**百 ms 到秒**,屏板只给 205 / 280 ms → 起发不能挂在任何任务态。
|
||||
|
||||
**采纳方案的分工——起发者不等于业务归属**(这两件事分开看,否决理由才站得住):
|
||||
|
||||
- **`uiTask`(10 ms 一拍)= 业务与组帧**:显存或 LED 掩码有变化时把 4208 B 帧拼好(3200 B 显存 + 4 B 灯 +
|
||||
`info[4..7]` + 998 B 填充 + 2 B CRC);消费中断留下的 `key` 字节 → `ui_net_sink_inject_key()`
|
||||
@@ -132,9 +143,13 @@
|
||||
- **两条必须防**:① DMA 正在读时 `uiTask` 改帧 → dirty 标志(传完再重组)或双缓冲(仓里 `meas` 的双区 +
|
||||
`seq` 是现成先例);② **屏板一次接收溢出就永久收不到、且自己毫无指示**(它错误回调里的重启被注释掉)
|
||||
→ 主机严格「一召唤 → 一答 4208 B」,**不提前发、不超发、不叠发**。
|
||||
- **不新建任务**:不动 `Src/freertos.c`、不动 `App/task/task_handles.h`、不碰已满的 RTOS 堆(§1.3);
|
||||
看门狗位掩码 `0x37` 不动——起发在中断里,与任务心跳无关。
|
||||
- **屏板那 200 ms 的真实语义**(决定为什么仍按 ≤200 ms 要求自己,但理由要换):它到点只置一个「可以重发」
|
||||
- **不新建任务**:不动 `Src/freertos.c`、不动 `App/task/task_handles.h`、不占 RTOS 堆(§1.3 的余量本来就紧);
|
||||
- **看门狗一个位都不加**:产品构建的必需位掩码现值是 **`0x1F7`**(`App/util/task_watchdog.c` 的 `wd_required_mask()`:
|
||||
default / net / ftp / ui / timesync / adc / iec104 / protect;口径 `docs/子系统设计/看门狗设计说明.md` §1,
|
||||
default / net / ftp / ui / timesync / adc / iec104 / protect;口径见 `docs/子系统设计/看门狗设计说明.md` 的「必需掩码」行)。
|
||||
本项不给屏板链路新增心跳位,也不改变「同一任务缺席 800 ms」那条判定。起发在中断里,与任务心跳无关。
|
||||
也不改变「同一任务缺席 800 ms」那条判定。
|
||||
- **屏板那 200 ms 的真实语义**(它不是帧作废线,但主机仍按 ≤200 ms 要求自己,理由是下面这两条):它到点只置一个「可以重发」
|
||||
的标志,不丢已收字节、不清缓冲、不重启串口;**迟到的完整帧照样会被显示**(收帧处理与超时判定相互独立,
|
||||
帧里也没有序号)。真硬的是 280 ms 那轮召唤——而且**有按键待报时最早 205 ms 就会再来一帧**,
|
||||
上一答还在总线上就是**双向对撞**。所以 ≤200 ms 内发完仍要做,理由是**防撞总线 + 不多一轮画面滞后**。
|
||||
@@ -202,7 +217,7 @@
|
||||
| 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` 已接)清 busy;busy 期间冻结帧缓冲,`uiTask` 有新内容就置 dirty、传完再重组(或双缓冲);缓冲、起发入口、收尾全收在 `sb_link.c` | 连续 30 min:无双发、无脏帧、组帧自校验 100 %;`[PROF]` 里 `uiTask` 单轮工作耗时增量 **≤0.3 ms**;拔掉屏板后不再触发、无残留 busy |
|
||||
| 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 后置离线标志;插回自动恢复;屏板自复位不被误判 |
|
||||
|
||||
### 批 3 · 状态灯与 LOGO(3 项)
|
||||
@@ -323,7 +338,7 @@ stub 提供 HAL)。屏板侧 CRC 有查表实现,**用同一张表做交叉
|
||||
| **一次噪声后链路永久静默** | 错误回调里不清 ORE | 屏板召唤再也收不到,症状与"协议不对"完全一样 | 1-3 判据含"拔插 TX 线制造噪声后自动恢复" |
|
||||
| **日志口又染上 ORE 锁存** | 新日志口照抄 `UART_MODE_TX_RX` | 控制台静默、看门狗误复位(老病复发) | 新日志口设 `UART_MODE_TX`;判据含"连续 10 min 无 ORE" |
|
||||
| 帧长按 3208 实现 | 照 readme 写 | CRC 全失败、屏板不亮 | 已写进 §4.2 警示;联调第一步就核 4208 |
|
||||
| 中断回调漏加分支 | 只改自己模块,忘了现有全局 UART 回调里的句柄分发 | 收不到召唤帧,表象像协议错 | 批 1 的 1-3 已列为判据 |
|
||||
| 中断回调漏加分支 | 只改自己模块,忘了那三个全局 UART 回调里唯一的一处实现只分发另一条通道的句柄(§1.3) | 收不到召唤帧,表象像协议错 | 批 1 的 1-3 已列为判据 |
|
||||
| 屏板自复位被误判离线 | 链路监视窗口设得太小 | 每 83 分钟误报一次 | 判据定为"连续 ≥10 s 无帧" |
|
||||
| 状态灯语义返工 | 映射表拍脑袋定的 | 后期改表要重测 | 把表做成一张 `const` 数组,改表不改逻辑 |
|
||||
| RAM 超门禁 | 误把 RX 缓冲也做成 4208 B | `RW_IRAM1` 逼近 92 % | RX 只要 16 B(§1.3) |
|
||||
@@ -332,6 +347,7 @@ stub 提供 HAL)。屏板侧 CRC 有查表实现,**用同一张表做交叉
|
||||
| **把 200 ms 当成「帧作废」线** | 误读屏板那个放弃计时 | 白加一层重试与加急逻辑;或反过来以为迟到无害,撞上 205 ms 的按键召唤 | 屏板真实规则:迟到的完整帧照样显示;**有按键待报时最早 205 ms 就再召唤** → 仍按 ≤200 ms 完成,理由换成「防撞 + 不滞后」(决策三) |
|
||||
| **按键被当成偶发失灵** | 那一轮窗口被错过 | 屏板的键**只发一次不重试**(发完立刻清备份)→ 该次按键直接丢失 | 起发留在中断里(µs 级);2-3 按「逐键必到」测,不看统计均值 |
|
||||
| **屏板背光 100 s 熄灭被当成掉线** | 现场长时间没人按键 | 误报链路故障 | 按任意键即复亮;判离线只看召唤帧有无(2-5 的 10 s 窗口) |
|
||||
| **采纳方案继承 `uiTask` 的挤占面** | `uiTask` 是 BelowNormal,排在 defaultTask(Normal)之后,而后者一次日志排空就是 250 ms(`App/log/console_log.c` 的 `DBG_CON_DRAIN_BUDGET_MS`)、NAND 写数百 ms;对时 relisten 突发实测把 `uiTask` 单轮压到 800 ms 以上 | 只表现为**画面滞后与注键延迟**——起发在中断,不撞总线、不进屏板那套超时阶梯 | 与 7003 同一来源,属已知成本;现场不可接受就走退路(组帧挪独立任务),**起发仍留在中断里** |
|
||||
|
||||
---
|
||||
|
||||
@@ -349,11 +365,13 @@ stub 提供 HAL)。屏板侧 CRC 有查表实现,**用同一张表做交叉
|
||||
7. 拔插屏板:7003 侧不受影响,插回自动恢复,不用复位主机。
|
||||
8. LOGO 下发后断电重启仍在,且不改变 LED 状态。
|
||||
|
||||
**另测两项**(属本方案的实现判据,不在 §5.16.10 编号内):
|
||||
**另测三项**(属本方案的实现判据,不在 §5.16.10 编号内):
|
||||
|
||||
- 逻辑分析仪量整帧发送期间**不得出现 ≥2 个字符时间(约 28 µs)的间隙**——这是断帧安全性的直接证据,
|
||||
也是必须用 DMA 的理由(决策三);同时记下召唤帧末位到应答首位的实测值,确认 30 µs 的切换留量够用。
|
||||
- 跑一遍 LOGO 下发(3-3)之后,验证「给屏板断电重上」仍能恢复通信:屏板在写它自己那块 Flash 时若被打断,
|
||||
- **7003 拥塞时串口侧照常应答**:挂着 7003 客户端不读、或制造对时 / relisten 突发把 `uiTask` 卡住,
|
||||
屏板侧应**零超时**、画面停在最后一帧(这就是「起发不在任务态」的直接证据);`uiTask` 一醒画面立刻续上、总线无对撞。
|
||||
可能进入它自己救不回来的状态(它的错误回调与重初始化都被注释掉了)。
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user