diff --git a/App/log/console_log.c b/App/log/console_log.c index 5d8571bf..41386b23 100644 --- a/App/log/console_log.c +++ b/App/log/console_log.c @@ -62,6 +62,13 @@ * 三本账分开(docs §13-O2):新增 dbg_console_tx_timeout(),dropped 只记 * 真丢的,tx_fault 仍只记软复位自愈次数;[ERR] dropped 行改为"发成功才 * 销账",通道不可用期间不再把丢弃证据一起冲掉。 + * 2026-10-28 王建锋 **根因归因更正(纯注释,无行为变更)**:原注释把 ORE 锁存 + * 写成"PA10 悬空 / 未接线",用户确认 PA10 **一直是接线的**,该归因不成立。 + * 真正的充分条件是"**RX 使能了却从不读 DR**"——线上任意一个字节或一个 + * 噪声沿使 RXNE 置位后无人读,下一个事件即锁存 ORE(ORE 只能靠"先读 SR + * 再读 DR"清除 ⇒ 永不自愈);引脚悬空只影响发生率,不是必要条件。 + * 缓解措施与结论不变,仅改注释;机制详见 + * docs/子系统设计/控制台单写者日志设计说明.md §12。 */ #include "console_log.h" @@ -467,10 +474,14 @@ uint8_t dbg_console_writer_ready(void) { /* * 函数功能:USART1 僵尸通道自愈(清错误锁存 + 整外设重初始化) - * 函数说明:RX 悬空噪声(PA10 未接线)锁存的 ORE/NE/FE/PE 使 HAL 轮询 TX 恒快速失败 - * (控制台静默、系统不复位);发送器卡死状态则使 TXE 等待耗满超时(冻结唯一 - * 写者 → 看门狗误复位,2026-10-02 台架实证)。外设软复位清锁存与卡死状态后 - * 重新初始化,故障时间被单块超时限定 + * 函数说明:**RX 使能了却从不读 DR** → 线上任意一个字节或一个噪声沿使 RXNE 置位后无人读, + * 下一个事件即锁存 ORE(常伴 NE/FE/PE);ORE 只能靠"先读 SR 再读 DR"清除 ⇒ 永不自愈。 + * 锁存后 HAL 轮询 TX 恒快速失败(控制台静默、系统不复位);发送器卡死状态则使 TXE + * 等待耗满超时(冻结唯一写者 → 看门狗误复位,2026-10-02 台架实证)。外设软复位清锁存与卡死状态后 + * 重新初始化,故障时间被单块超时限定。 + * ⚠ 2026-10-28 更正:原注释把根因写成"PA10 未接线 / 悬空",用户确认 PA10 一直是接线的; + * 充分条件是"RX 使能却不消费 DR",与引脚是否悬空无关 + * (详见 docs/子系统设计/控制台单写者日志设计说明.md §12)。 */ static void con_uart_reinit(void) { __NVIC_ClearPendingIRQ(USART1_IRQn); diff --git a/docs/产品需求/产品需求设计文档.md b/docs/产品需求/产品需求设计文档.md index 8ca936c4..cc6d71dc 100644 --- a/docs/产品需求/产品需求设计文档.md +++ b/docs/产品需求/产品需求设计文档.md @@ -7,7 +7,7 @@ > 待实现项与路线图、验收判据。 > **事实来源**:本文所有数值与结论均取自仓库内**受管事实源**(代码、`.ioc`、`.map`、网表分析报告与 > 各模块设计文档),凡与其它文档冲突处,以本文标注的权威来源为准。 -> **版本**:v1.8 **日期**:2026-10-28 **状态**:现状基线(含已实现项 + 待实现项) +> **版本**:v1.9 **日期**:2026-10-28 **状态**:现状基线(含已实现项 + 待实现项) > **阅读前提**:建议先读 [`docs/README.md`](../../docs/README.md)(文档索引)与 > [`docs/架构设计/项目架构总览.md`](../../docs/架构设计/项目架构总览.md)(全景架构)。 > **范围口径:1:1 复刻**——本产品是参考产品 **CLS100**(`Scatter_P_HV03.01_SV01.01`,OMAP-L138 @@ -507,7 +507,7 @@ sequenceDiagram | **GD5F2GQ5UE NAND** | SPI1 | PE0 CS / PB3 SCK / PB4 MISO / PB5 MOSI / PB8 WP / PE1 HOLD | 256 MB SPI NAND(2 Gbit) | | **TPAFE5160 ADC** | 并行 16 位 | PG0~PG15 数据 / **PC6 FRSTDATA** / **PC7 BUSY**(EXTI) / **PC8 RD** / **PC9 CONVST** / **PA8 OS2** / **PA11 OS1** / **PA12 OS0** | 高精度多通道 ADC | | **SD2506 RTC** | I2C1 | PB6 SCL / PB7 SDA | 提供墙钟基准与日志时间戳 | -| **USART1(调试串口 / U5 直连 TTL)** | UART | PA9 TX / PA10 RX | 115200 8N1;现为 printf 日志 + `@test` 采集通道。**端口重排后改作屏板链路**(700 kbaud,§5.16.2)。⚠ PA10 目前**未接线**,作屏板链路前必须接上 | +| **USART1(调试串口 / U5 直连 TTL)** | UART | PA9 TX / PA10 RX | 115200 8N1;现为 printf 日志 + `@test` 采集通道。**端口重排后改作屏板链路**(700 kbaud,§5.16.2);PA10 双向均已接线 | | **UART4(RS-485)** | UART | PC10 TX / PC11 RX / **PA15 DE** | 半双工通信(V0.1 轮换后 RS485#4 = UART4,`docs/硬件平台/V0.1_网表详细分析报告.md` §4.2) | | **USART2(RS-232 #3)** | UART | PD5 TX / PD6 RX | 经 U10 隔离 → U13 TPT3232E → CN1;**端口重排后为日志口** | | **UART5(RS-232 #1)** | UART | PC12 TX / PD2 RX | 经 U9 隔离 → U13 → CN1;备选日志口 | @@ -1078,10 +1078,16 @@ sequenceDiagram > 备选 RS232#1 = UART5 PC12/PD2,两者都经隔离器 → `U13` TPT3232E → CN1), > 迁移在屏板链路跑通之后做,见下方"主机侧端口重排"。 > -> ⚠ **USART1 的 RX(PA10)目前没有接线**:`App/log/console_log.c` 的文件头与自愈函数注释都记着 -> "PA10 未接线"——RX 悬空噪声锁存 ORE/NE/FE/PE,正是控制台历史上反复静默的根源。 -> 屏板链路是**双向**的(主机必须收 6 B 召唤帧),所以 **PA10 必须物理接上**, -> 否则只能发不能收,链路做不成。这是**两条物理前置**之一,另一条是把台架串口线从 USART1 挪到 RS-232 侧(见 §9.13)。 +> ⚠ **"PA10 未接线"这条旧结论已撤销(2026-10-28 用户确认)**:`App/log/console_log.c` 与 +> `docs/子系统设计/控制台单写者日志设计说明.md` §12 里的"PA10 悬空"是 2026-10-02 的**归因推断**, +> 不是实测;PA10 一直是接线的。所以屏板链路**不需要额外飞线**。 +> +> 但 ORE 锁存的**真正充分条件与引脚悬不悬空无关**——**RX 使能了却从不读 `DR`**:线上只要出现 +> 任意一个字节或一个噪声沿,`RXNE` 置位后没人读,下一个事件就把 **ORE 锁存**;而 ORE 只能用 +> "先读 SR 再读 DR"清除,于是**永不自愈**。这条对本实现有两条硬要求: +> ① 屏板链路要用 RX,就必须真的消费 `DR`,并在 `HAL_UART_ErrorCallback` 里**清 ORE 后重挂接收** +> (否则一次噪声就永久锁死,症状与"屏板不回帧"一模一样);② 新日志口设 `UART_MODE_TX` +> 仍然是对的——那是从机制上把 RX 摘掉,与 PA10 接不接线无关。 **帧 A:召唤 / 按键(屏板 → 主机,固定 6 B)** @@ -2585,12 +2591,13 @@ pre-commit run --all-files > 第 11 条的帧长仍有一处待实测复核——代码实参是 **4208 B**,屏板 readme 写 **3208 B**(§5.16.2); > 台架抓到第一帧真实报文后**立即固化**,在此之前以代码实参为准,不得按 readme 实现。 > -> **两条物理前置(软件做不了,须先落硬件)**: -> ① **PA10 接线**——USART1 的 RX 现在是悬空的(`App/log/console_log.c` 文件头有明确记载), -> 屏板链路要收 6 B 召唤帧就必须把它接上;顺带这一接也消掉了控制台 ORE 锁存的老病根。 -> ② **台架串口线挪位**——现在的 CH340/`COM6` 接在 USART1 调试口上,日志迁到 RS-232 侧后 -> 必须改接到 CN1(或 U14 RJ45),否则 `@test` 与 `serial-assistant` 全部抓不到日志, -> 等于把台架弄瞎。**这条要在第 16 条动代码之前完成。** +> **一条物理前置 + 一条实现纪律**: +> ① **台架串口线挪位**——现在的 CH340/`COM6` 接在 USART1 调试口上,日志迁到 RS-232 侧后 +> 必须改接到 CN1(或 U14),否则 `@test` 与 `serial-assistant` 全部抓不到日志,等于把台架弄瞎。 +> **这条要在第 16 条动代码之前完成。** +> ② **RX 要真的读、ORE 要真的清**——PA10 一直是接线的(原"未接线"结论已撤销,§5.16.2), +> 但"RX 使能却不读 `DR`"本身就足以让 ORE 永久锁存。第 11 条实现时必须在 +> `HAL_UART_ErrorCallback` 里清 ORE 并重挂接收,否则一次噪声就永久静默。 > > **已消解的旧阻塞**:原"屏板协议拿不到、不得按猜测定契约"的告警**不再适用**——物理层、帧格式、 > 键值、状态灯、时序均已从 `DTU-LCD` 源码确定。剩下的只是"实物联调确认",不是"协议未知"。 @@ -2715,3 +2722,4 @@ pre-commit run --all-files | 2026-10-28 | **v1.6 屏板源码到手:串口通道协议定版 + 状态灯下行** | ① **屏板固件已获得**(`D:\Code\DTU Code\DTU-LCD`,STM32F103ZE + UC1698U 160×160 单色 + **20 路状态指示灯** + 7 个物理按键),v1.5 列的"四项未知"**全部消解**;② **§5.16.2 重写为「串口通道:物理层与帧格式(屏板源码实测)」**:**700000 8N1**(APB1 42 MHz 分频零误差、`BRR = 0x3C0`)、软件 DE、**角色反转(屏板是 Modbus 主站、主机是从站 `0x01`)**、帧 A 6 B 召唤(`01 03 type key CRC_H CRC_L`,`KEY_CMD=0` / `RT_CMD=1`,CRC-16/MODBUS 只覆盖前 4 B、**高字节在前**)、帧 B **4208 B** 图像应答(3200 显存 + 4 B 状态灯 + 4 B info + 998 B 填充 + 2 B CRC,覆盖 `[0, 4205]`)、时序 275 ms / 200 ms / 5 次超时、200 ms 预算下 4208 B 约 60.1 ms(占 30 %)、主机端口默认 **USART3 + 另配 DE GPIO**(**不必解冻 UART4**);③ **§5.16.3 重写为「屏板侧行为:状态灯 / 按键 / 背光 / LOGO」**,新增**状态灯下行**:`info[0..3]` 为 **32 位小端位掩码**、**ID 0 = 电源灯硬件常亮、ID 1~19 对应 bit1~19**,给出 20 路 LED 引脚表,并指出"**位 → 业务状态映射表参考源码没有、须由本产品自定**";键值 `KEY_TYPE` 表(`0` 无键 / `1` ENTER / `2` UP / `3` DOWN / `4` ESC / `5` ADD / `6` DEC / `7` LEFT / `8` RIGHT / `9` RESET / `10` FACTORY),并**指出与参考主机 `getRs485Key()` 的 `9` / `10` 语义冲突**(参考是 `F1` 与 `F1+ENT`)、`key_configs[]` 仅 7 项且无 ADD/DEC 入口;背光自管、LOGO(Flash `0x0803F000`、魔数 `0x89 0x45`)、屏板健壮性(CRC 错 ≥3 仅清计数**不自愈**、**≈5000 s 自复位**、DMA 接收问题未解决);④ **§5.16.9 差异表**第 8 / 11 / 12 / 13 / 14 行按屏板实测重写,并新增"来自屏板固件的新增约束"5 条;⑤ **§5.16.10 判据由 12 条扩到 15 条**(275 ms 召唤 / ≤200 ms 回 4208 B、显存逐字节、7 键、**状态灯逐位**、双通道并发、**自复位容忍**、拔插恢复、**LOGO 下发**);⑥ **§9.13 任务由 13 条改为 15 条**:第 10 条由"物理层定版"改为"**串口物理层落地**"(参数已定)、新增第 12 条"**状态灯位映射表**"与第 15 条"**LOGO 下发流程**"、第 14 条并入"自复位容忍",批次表第三批改 10~15 并注明**不必解冻 UART4**,删除"拿不到固件不得定契约"告警(已消解),保留"**4208 vs 3208 待台架复核**";⑦ **§5.8 补屏板 20 路指示灯与主板 6 路 LED 是两套独立输出**;§5.6 FR-UI-1 改为"**双出口推流**";§6.2 口径注改为"串口通道是 Modbus RTU 风格裸帧、**不能共用组帧代码**";§2.4.1 X3、§2.5.2 第 20 项、§5.16.1、§5.16.8 `tTimerLCD` 行、§9.1.2、§9.12、§10.3 第 13 项同步。**本次只改本文件** | | 2026-10-28 | **v1.7 单色口径:删除多色协议,INIT 由 29 B 改 16 B** | 用户裁定"屏幕板都是单色的,多色协议可以删除、不必对齐"。**核查后确认这不是让步,而是删死字段**:① **三处源码证据**——`RemoDispBus.h:46` `CN_RDISP_TYPE_MONO = TRUE` → `CN_RDISP_LCD_TYPE = CN_MODE_GREY_MONO`、`CN_RDISP_MEMSIZE = (X>>3)*Y`(链路显存本就是 1 bpp);`RDisp_Form_InitInf` 里 `CN_RGB_RED/GREEN/BLUE` 是**编译期常量**而非逐帧数据;`RDisp_LcdColor_To_Mono()` 对 `CN_MODE_GREY_MONO` **直接 `return TRUE` 不转换**,真正干活的 `RDisp_Lcd16B_To_Mono()`(RGB565→单色)挂在 `CN_MODE_COLOR_16B` 下**本构建永不进入**(死代码);② **§5.16.5 INIT 应答表由 29 B 重写为 16 B**(宽 2 + 高 2 + 显存 4 + 8 键值表),删掉 1 B 颜色位 + 12 B RGB,并补"为什么是 16 B"的证据块;③ **§6.2 命令集 INIT 行**同步改 16 B 并加"单色口径"注(颜色位与 RGB 整段删除、`RDisp_LcdColor_To_Mono()` 不存在);④ **§5.16.6 第 6 条注**改为"该函数在参考单色构建下就是空转",避免误以为本产品漏了一步转换;⑤ **§5.16.9 第 1 行**由"补 21 B"改为"**补 8 B 键值表 → 16 B**,颜色 13 B 不对齐";"新增约束"补"单色"一条;末尾警示改为 **16 B vs 29 B 不可互换**(从偏移 4 起含义即不同,客户端不得复用参考 29 B 解析器截前 16 B);⑥ **§5.16.10 判据第 1 条**改 16 B 且"不得出现颜色位或 RGB";⑦ **§9.13 第 2 条**改"INIT 应答补足 **16** 字节 / 不加颜色位与 RGB"、第 9 条客户端按 16 B 解析、破坏性变更警示改 16 B(后 8 B 而非后 21 B)并补"不可与参考 29 B 互换";⑧ **§2.4.1 补"多色显示不在本表内"**——参考与本产品都是单色链路,删的是死字段,**不属于"决定不做"的能力**;§2.5.2 第 20 项、§9.1.2、§9.12、§10.3 第 13 项同步;⑨ **§8.1 色深行**补"无灰度、无彩色,7003 INIT 颜色字段已删",渲染行改为"串口屏板通道(主)+ TCP 7003(镜像)双出口"。**本次只改本文件** | | 2026-10-28 | **v1.8 端口重排:屏板占 USART1,日志迁 RS-232 通道** | 用户裁定"把现在的日志 UART 让给屏幕,RS-232 通道后面用来输出日志"。**这条裁定把 v1.6/v1.7 的头号阻塞直接消掉了**:① **§5.16.2 新增「端口裁定」块**——屏板改走 **USART1(PA9/PA10,MCU 直出 TTL、不经任何收发器)**,因此 `U15 = TP8485E-SR` 只有 250 kbps 这件事**对屏板链路不再构成约束**;日志迁 **RS-232 通道**(V0.1 板上 RS232#3 = USART2 PD5/PD6 为默认,RS232#1 = UART5 PC12/PD2 为备选,两者都经隔离器 → U13 TPT3232E → CN1);② **纠正分频算法**——旧文写"`42 000 000 / 700 000 = 60` → `USARTDIV = 60.0`、`BRR = 0x3C0`"**漏了 16 倍过采样**,照此写实际波特率只有 43 750;正确值:USART1(APB2 84 MHz)`USARTDIV = 7.5` → **`BRR = 0x0078`**,APB1 实例(42 MHz)`USARTDIV = 3.75` → **`BRR = 0x003C`**,两者误差均为 0;③ **§5.16.2「主机侧端口选型」整表重写为「主机侧端口重排」**,按 `docs/硬件平台/V0.1_网表详细分析报告.md` §4.2 的三对 UART 轮换列明 RS485#4 = UART4(DE = PA15)、RS232#1 = UART5、RS232#3 = USART2,并补两条实现约束(`console_log.c` 里 `huart1` 5 处 + 自愈函数直写 `USART1_IRQn` / `MX_USART1_UART_Init()` 需收口成一处端口宏;新日志口必须 `UART_MODE_TX`,否则重演 ORE 锁存);④ **DE 口径改为「TTL 直连无 DE」**,仅"经外部 485 模块接 A/B"时才需处理,且极性由该模块决定;⑤ **主机 RX 口径**由"吃下 4208 B"改为"只需 6 B(16 B 缓冲够)",并说明 TTL 直连无自发自收;⑥ **§4.2 外设接口表**:USART1 行补"端口重排后改作屏板链路 + PA10 目前未接线",新增 USART2(RS-232 #3,日志口)与 UART5(RS-232 #1,备选)两行,UART4 行注明 V0.1 轮换依据;⑦ **§5.16.9「新增约束」**与 **§9.12** 的 700000 行同步为"USART1 TTL 直连、无 DE";⑧ **§9.13 新增第 16 条「日志口迁移」**并置于第三批首位,批次表注明 **16 必须排在 10 之前**(两者共用 `USART1->DR`);第 10 条改为 `BRR = 0x0078`、无 DE、`DMA2_Stream7/2` 现成;**前置阻塞**补两条物理前置(PA10 接线、台架串口线从 USART1 挪到 CN1)。**本次只改本文件** | +| 2026-10-28 | **v1.9 撤销"PA10 未接线",ORE 机理改正** | 用户反馈"PA10 这个端口我接线了,当时分析出这个问题来,我怀疑是错误的"。核查后确认:**`App/log/console_log.c`(文件头与 `con_uart_reinit()` 注释)与 `docs/子系统设计/控制台单写者日志设计说明.md` §12 里的"PA10 悬空 / 未接线"是 2026-10-02 的归因推断,不是实测**,且与用户确认的接线状态相反。改动:① **§4.2 外设接口表** USART1 行删掉"PA10 目前未接线、作屏板链路前必须接上",改为"PA10 双向均已接线";② **§5.16.2** 原"USART1 的 RX 目前没有接线"警示块整段重写为「旧结论已撤销 + ORE 真正机理」——ORE 的充分条件是**RX 使能却从不读 `DR`**(任意一个字节或噪声沿使 `RXNE` 置位后无人读,下一个事件即锁存 ORE,且只能靠"读 SR 再读 DR"清除 ⇒ **永不自愈**),与引脚悬空无关;据此补两条硬要求(屏板链路必须真的消费 `DR` 并在 `HAL_UART_ErrorCallback` 里清 ORE 后重挂接收;新日志口设 `UART_MODE_TX` 仍正确,因为那是从机制上摘掉 RX);③ **§9.13 前置阻塞**由"两条物理前置"改为"一条物理前置(台架串口线挪位)+ 一条实现纪律(RX 要真读、ORE 要真清)",并写明原"PA10 接线"结论已撤销;第 11 条的验收隐含"拔插 TX 线制造噪声后能自动恢复"。**当年那场 ORE 风暴的真因仍待定**——接线正常时更常见的是对端波特率不匹配或适配器 TX 在 USB 掉线时浮空,下次复现先量空闲电平与波特率,不要再默认"引脚悬空"。**本次只改本文件** | diff --git a/docs/任务规划/README.md b/docs/任务规划/README.md index 02230526..8b303ea6 100644 --- a/docs/任务规划/README.md +++ b/docs/任务规划/README.md @@ -64,7 +64,7 @@ docs/测试说明/模拟器差距清单.md §2.3「开机账本 boot_stats」一 | **6** | 保护元件与采集增强 | 等待标准源或硬件改版 | 保护先解除未标定闭锁;三相同时刻类功能等 V0.2 硬件 | | **7** | 验证窗口事项 | 逢条件顺带 | CI runner、PC peer、CH340、负向故障注入条件到位时执行 | | **8** | 独立模块与暂缓项 | 当前不排 | 开入开出、故障录波、SNTP 单独立项;RS-485 与内存优化按各自重启条件 | -| **独立** | **屏板链路(对上屏板)** | **规划已出(未开工)** | 独立立项,插在任一顺位之间都能做,不占主序列;**端口已裁定(2026-10-28):屏板占 USART1,日志迁 RS-232 通道**,所以原来的 250 kbps 收发器阻塞已解除;剩下的是三条物理前置——**接 PA10、台架串口线挪到 CN1、日志口迁移**(见 docs/任务规划/屏板链路.md §0 与 §3 批 0);**软件可先用 PC + USB 转 TTL 台架验证,不必等屏板实物** | +| **独立** | **屏板链路(对上屏板)** | **规划已出(未开工)** | 独立立项,插在任一顺位之间都能做,不占主序列;**端口已裁定(2026-10-28):屏板占 USART1,日志迁 RS-232 通道**,所以原来的 250 kbps 收发器阻塞已解除;剩下两条前置——**台架串口线挪到 CN1、日志口迁移**(见 docs/任务规划/屏板链路.md §0 与 §3 批 0);**软件可先用 PC + USB 转 TTL 台架验证,不必等屏板实物** | ### 2.1 关键依赖 diff --git a/docs/任务规划/屏板链路.md b/docs/任务规划/屏板链路.md index 869f616a..0682feac 100644 --- a/docs/任务规划/屏板链路.md +++ b/docs/任务规划/屏板链路.md @@ -9,25 +9,39 @@ --- -## 0. 先看结论:一条硬约束已解除,四条前置顶上来 +## 0. 先看结论:一条硬约束已解除,三条前置顶上来 **2026-10-28 用户裁定:屏板走 USART1,日志口让位给 RS-232 通道。** 这条裁定把原来的头号阻塞直接消掉了, -代价是换成几条**物理**前置——都不是软件能解决的,但都比换收发器便宜。 +代价是换成几条**流程**前置——都不是软件能解决的,但都比换收发器便宜。 | # | 项 | 实测 / 出处 | 后果 | |---|---|---|---| | 1 | ~~485 收发器只到 250 kbps~~ **已解除** | 屏板改走 **USART1 TTL 直连**,不经任何板上收发器 | 700 kbaud 不再受 `U15 = TP8485E-SR`(250 kbps)限制;`BRR = 0x0078`(APB2 84 MHz,误差 0 %) | -| 2 | **PA10 未接线** | `App/log/console_log.c` 文件头与 `con_uart_reinit()` 注释均记"PA10 未接线" | 屏板链路要收 6 B 召唤帧 → **PA10 必须物理接上**,否则只能发不能收 | +| 2 | ~~PA10 未接线~~ **已撤销(2026-10-28 用户确认)** | `console_log.c` 与 `docs/子系统设计/控制台单写者日志设计说明.md` §12 的"PA10 悬空"是 2026-10-02 的**归因推断**,不是实测;用户确认 PA10 **一直是接线的** | 双向通道物理成立,**不需要额外飞线**;原"接 PA10"这条任务作废 | | 3 | **日志口占着 USART1** | `console_log.c` 里 `huart1` 5 处;自愈函数直写 `USART1_IRQn` / `MX_USART1_UART_Init()` | 两者共用同一个 `USART1->DR`,同时用就是互相插字节 → **日志必须先迁到 RS-232 通道** | | 4 | **台架串口线接在 USART1 上** | CH340 → `COM6`,`@test` / `serial-assistant` 都靠它抓日志 | 日志一迁,台架立刻瞎 → **先把线挪到 RS-232 侧(CN1)再动代码** | -**执行顺序因此是**:接 PA10 → 挪台架线 → 迁日志口 → 才开始写屏板链路。 -前两步是拧螺丝,第三步约 20 行代码(§3 批 0),第四步才是原来规划的那些批次。 +**执行顺序因此是**:挪台架线 → 迁日志口 → 才开始写屏板链路。 +第一步是拧螺丝,第二步约 20 行代码(§3 批 0),第三步才是原来规划的那些批次。 好消息是:**主机侧 RX 只需要 6 B**(屏板发的是 6 字节召唤帧),4208 B 只出现在**发送方向**, 所以内存和缓冲压力比想象中小得多(§1.3)。 -> 顺带纠正一条旧结论(本文 v1 写错了):`docs/硬件平台/V0.1_网表详细分析报告.md` §4.2 做过一次 +> ⚠ **顺带纠正一处机理(2026-10-28)**:当年把 ORE 锁存归因于"PA10 悬空",既然 PA10 是接线的, +> 这条归因就不成立。真正的充分条件**与引脚悬不悬空无关**——**RX 使能了却从不读 `DR`**: +> 线上只要出现任意一个字节或一个噪声沿,`RXNE` 置位后没人读,下一个事件就把 **ORE 锁存**; +> 而 ORE 只能用"先读 SR 再读 DR"清除,于是**永不自愈**。悬空只是让它更容易发生,不是必要条件。 +> +> 这条纠正对实现有直接影响: +> 1. **屏板链路要用 RX,就必须真的消费 `DR`,并在 `HAL_UART_ErrorCallback` 里清 ORE** +> (`__HAL_UART_CLEAR_OREFLAG` 或读 SR 再读 DR)。否则一次噪声就永久锁死, +> 症状与"屏板不回帧"一模一样,很容易又查错方向。 +> 2. **新日志口设 `UART_MODE_TX` 仍然是对的**——那是从机制上把 RX 摘掉,与 PA10 接不接线无关。 +> 3. 当年那场风暴的**真因仍待定**:接线正常时更常见的是**对端波特率不匹配**(适配器不是 115200) +> 或适配器 TX 在 USB 掉线时浮空。下次复现先量适配器 TX 的空闲电平与波特率, +> 别再默认"引脚悬空"。 + +> 顺带纠正另一条旧结论(本文 v1 写错了):`docs/硬件平台/V0.1_网表详细分析报告.md` §4.2 做过一次 > "三对 UART 轮换"——**V0.1 的 RS485#4 = UART4(PC10/PC11,DE = PA15)**,RS232#1 = UART5、RS232#3 = USART2。 > 所以 `rs485_init(&g_rs485, &huart4, …)` 与 V0.1 **一致,不用动**;V0.0 报告里的"RS485 = UART5"是旧板。 > 之前把它当成"网表自相矛盾"是拿 V0.0 摘要行对 V0.1 代码,判断依据错了,结论也就错了。 @@ -110,9 +124,9 @@ > 摘它等于动冻结簇的验收基线。本文 v1 曾把这里判成"网表自相矛盾",是拿 V0.0 摘要行对 V0.1 代码, > 判断依据错了——见 §0 末尾那条纠正。 -> ⚠ **USART1 交接前必须确认两件事**:① `PA10` 已接线(否则收不到召唤帧); -> ② 控制台已不再写 `USART1->DR`(否则两条流互相插字节,症状是"屏板偶尔花屏 + 日志偶尔乱码", -> 很容易误判成 CRC 或时序问题)。 +> ⚠ **USART1 交接前必须确认两件事**:① 控制台已不再写 `USART1->DR`(否则两条流互相插字节, +> 症状是"屏板偶尔花屏 + 日志偶尔乱码",很容易误判成 CRC 或时序问题); +> ② 屏板链路的错误回调里**要清 ORE**(机理见 §0 那条纠正),否则一次噪声就永久锁死。 ### 决策三:谁来跑这个链路(宿主) @@ -147,16 +161,17 @@ 分 5 批。**批 0 不做完就不要碰屏板链路代码**——批 0 干的是"把 USART1 腾出来",腾不出来就没地方接屏板。 -### 批 0 · 腾位置(4 项,全是前置,做完才有 USART1 可用) +### 批 0 · 腾位置(3 项,全是前置,做完才有 USART1 可用) | 序 | 任务 | 改动点 | 判据 | |---|---|---|---| -| 0-1 | **接 PA10** | 硬件:把 USART1_RX(PA10)接到屏板 TX 一侧 | 主机能收到字节(先用 115200 手动发几个字节验证) | -| 0-2 | **台架串口线挪位** | 硬件:CH340 从 USART1 调试口改接 CN1 的 RS-232 端(或 U14) | `serial-assistant` 在 RS-232 侧能收到日志 | -| 0-3 | **日志口迁移(USART1 → RS-232)** | ① `console_log.c` 的 `huart1`、`USART1_IRQn`、`__HAL_RCC_USART1_CLK_*`、`MX_USART1_UART_Init()` 收口成**一处端口宏**,指向 `huart2`(RS232#3)或 `huart5`(RS232#1);② 该实例改 **`UART_MODE_TX`**(只发不收,别把 ORE 病搬过去) | 日志在 RS-232 侧可读、内容与迁移前一致;连续 10 min 无 ORE 锁存、无 `[ERR]`;USART1 完全空出 | -| 0-4 | **定 USART1 那端的物理介质** | 直连屏板 TTL 侧,或外部 ≥1 Mbps 485 模块(决策一的剩余子决策) | 结论落纸,含接法与验收方式 | +| 0-1 | **台架串口线挪位** | 硬件:CH340 从 USART1 调试口改接 CN1 的 RS-232 端(或 U14) | `serial-assistant` 在 RS-232 侧能收到日志 | +| 0-2 | **日志口迁移(USART1 → RS-232)** | ① `console_log.c` 的 `huart1`、`USART1_IRQn`、`__HAL_RCC_USART1_CLK_*`、`MX_USART1_UART_Init()` 收口成**一处端口宏**,指向 `huart2`(RS232#3)或 `huart5`(RS232#1);② 该实例改 **`UART_MODE_TX`**(只发不收,把 RX 从机制上摘掉) | 日志在 RS-232 侧可读、内容与迁移前一致;连续 10 min 无 ORE 锁存、无 `[ERR]`;USART1 完全空出 | +| 0-3 | **定 USART1 那端的物理介质** | 直连屏板 TTL 侧,或外部 ≥1 Mbps 485 模块(决策一的剩余子决策) | 结论落纸,含接法与验收方式 | -> 0-3 是最容易出岔子的一步:`console_log.c` 是**看门狗喂狗节奏的唯一写者** +> PA10 **不需要动**——用户 2026-10-28 确认一直是接线的,原"接 PA10"那条任务作废(§0 第 2 行)。 + +> 0-2 是最容易出岔子的一步:`console_log.c` 是**看门狗喂狗节奏的唯一写者** > (机制见 `docs/子系统设计/控制台单写者日志设计说明.md`),改端口后必须台架跑一轮长稳, > 确认没把发送超时判据、drain 预算、僵尸通道自愈这些保护改坏。 > 改完先跑一次 `@test`,别急着往下做。 @@ -169,7 +184,7 @@ |---|---|---|---| | 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` 里把 **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 分支**(现有回调只认 `g_rs485.huart`,不加分支会静默收不到);`HAL_UART_ErrorCallback` 同样要加 | 屏板每 275 ms 发来的 6 B 能稳定收到,`Size == 6`,CRC 通过率 100 % | +| 1-3 | **USART1 收中断与全局回调分支** | 开 IDLE 中断接收(缓冲 **16 B**);`HAL_UARTEx_RxEventCallback` 里**加 USART1 分支**(现有回调只认 `g_rs485.huart`,不加分支会静默收不到);`HAL_UART_ErrorCallback` 同样要加,且**必须清 ORE/NE/FE 并重挂接收**(`__HAL_UART_CLEAR_OREFLAG` 或读 SR 再读 DR) | 屏板每 275 ms 发来的 6 B 能稳定收到,`Size == 6`,CRC 通过率 100 %;**拔插一次 TX 线制造噪声后能自动恢复**(不清 ORE 会永久锁死) | | 1-4 | **(条件项)外部 485 模块的 DE** | **仅当** 0-4 选了"经外部模块接 A/B"才做:新建 `sb_link.c/h`,拉 DE → 等建立(**不要用 `HAL_Delay(1)`,用 DWT 忙等 ≤10 µs**)→ 发 → 等 TC → 松 DE | 逻辑分析仪上看 DE 与最后一字节的时序:DE 必须在 TC 之后才翻转,不得截断末字节 | > ⚠ **`HAL_UARTEx_RxEventCallback` / `HAL_UART_ErrorCallback` 是全局单例**(`Drivers/BSP/RS485/rs485.c:189/205`), @@ -281,7 +296,7 @@ stub 提供 HAL)。屏板侧 CRC 有查表实现,**用同一张表做交叉 | 步 | 做什么 | 需要什么 | 停下也能交付什么 | |---|---|---|---| -| 0 | **腾位置**:接 PA10、CH340 挪到 RS-232 侧、日志迁走 | 一根飞线 + 改接串口线 | USART1 空出来,日志照旧能看(批 0) | +| 0 | **腾位置**:CH340 挪到 RS-232 侧、日志迁走 | 改接串口线 | USART1 空出来,日志照旧能看(批 0) | | 1 | 协议层单测(`test/host/screenlink/`) | 无硬件 | CRC 与组帧的正确性,可回归 | | 2 | PC 串口助手**模拟屏板**:手发 6 B 召唤帧,看主机回的 4208 B | **USB 转 TTL 模块**接 USART1(PA9/PA10)+ 主机板 | 主机侧协议 100 % 可验证 | | 3 | 反向:PC 收主机帧,用脚本核对 4208 B / CRC / 显存内容 | 同上 | 帧内容正确性(与 `g_ui_fbuf` 逐字节比) | @@ -299,7 +314,7 @@ stub 提供 HAL)。屏板侧 CRC 有查表实现,**用同一张表做交叉 |---|---|---|---| | **日志迁移把台架弄瞎** | 先改代码、后挪串口线 | 什么都看不到,误以为固件挂了 | 顺序钉死:0-2 先挪线、0-3 再改代码;改完先跑一次 `@test` | | **控制台与屏板链路抢 `USART1->DR`** | 日志没迁干净就接屏板 | 屏板花屏 + 日志乱码,像 CRC / 时序错 | 0-3 的判据明确要求"USART1 完全空出" | -| **PA10 没接或接了仍悬空** | 只接了 TX | 收不到召唤帧,主机永远不回帧 | 0-1 单独验一次"能收到字节"再往下做 | +| **一次噪声后链路永久静默** | 错误回调里不清 ORE | 屏板召唤再也收不到,症状与"协议不对"完全一样 | 1-3 判据含"拔插 TX 线制造噪声后自动恢复" | | **日志口又染上 ORE 锁存** | 新日志口照抄 `UART_MODE_TX_RX` | 控制台静默、看门狗误复位(老病复发) | 新日志口设 `UART_MODE_TX`;判据含"连续 10 min 无 ORE" | | 帧长按 3208 实现 | 照 readme 写 | CRC 全失败、屏板不亮 | 已写进 §4.2 警示;联调第一步就核 4208 | | DE 极性接反(仅外部模块方案) | 照抄屏板 PA1"低=发" | 总线一直被占、屏板收不到 | 极性由那个模块定,逻辑分析仪一看便知 | diff --git a/docs/子系统设计/控制台单写者日志设计说明.md b/docs/子系统设计/控制台单写者日志设计说明.md index 4381530b..39a57091 100644 --- a/docs/子系统设计/控制台单写者日志设计说明.md +++ b/docs/子系统设计/控制台单写者日志设计说明.md @@ -370,6 +370,18 @@ sequenceDiagram **ORE(Overrun Error)置位** → HAL 发送路径卡在错误状态。对外设做软复位后 ORE **毫秒级重锁**,于是每轮 drain 都会耗掉 100ms 量级的块超时。 +> ⚠ **2026-10-28 更正(用户反馈)**:上面"PA10 悬空、无上拉"是**归因推断,不是实测**—— +> 用户确认 PA10 **一直是接线的**。而 ORE 的**充分条件本来就不依赖引脚悬空**: +> RX 使能了却从不读 `DR`,线上任意一个字节或一个噪声沿使 `RXNE` 置位后无人读, +> 下一个事件即锁存 ORE;ORE 只能靠"先读 SR 再读 DR"清除 ⇒ **永不自愈**。悬空只是提高发生率。 +> +> 因此本节的**观察与缓解措施仍然成立**(ORE 确实锁存、软复位后确实毫秒级重锁、drain 确实被拉长), +> 但**归因需要改写**为"RX 使能却不消费 DR",不是"引脚悬空"。 +> +> 当年那场风暴的真因仍待定:接线正常时更常见的是**对端波特率不匹配**(适配器不是 115200) +> 或适配器 TX 在 USB 掉线时浮空。下次复现先量适配器 TX 的空闲电平与波特率, +> 别再默认"引脚悬空"。 + **为什么会被误判**:本设计是"心跳位全员健康、无 STALL 日志"的**纯节奏性饥饿**—— 没有任何任务真的死等,只是主循环周期被 drain 拉长到 450~500ms,而喂狗抽样是 100ms 节流的、IWDG 最坏容限仅 1.7s,间歇被拉长就触发停喂。**看日志会以为"系统很健康"**。 diff --git a/scripts/size_attestation.json b/scripts/size_attestation.json index 2f5b7c86..996c8aa2 100644 --- a/scripts/size_attestation.json +++ b/scripts/size_attestation.json @@ -1,15 +1,15 @@ { - "built_at": "2026-10-28T07:12:48+08:00", - "fingerprint": "4e4d74f95dd9e07c6c89258a37e32b064981fa02ec1a43978de4801a02b2f1a6", + "built_at": "2026-10-28T10:38:53+08:00", + "fingerprint": "b4562719f05544c215ae12c3f9de42c1bf5a7d7ec3a94e0a79df456642dd7bfa", "fingerprint_files": 1452, "fingerprint_source": "git-index", "gated_regions": [ "RW_IRAM1", "RW_CCM" ], - "git_head": "8f7c141a0ba824e5239f7fc9418de956d5b51064", + "git_head": "eb2c454aed29411e806241f899db4a34494233c6", "map": "MDK-ARM/STM32F407-Demo/STM32F407-Demo.map", - "map_mtime": 1793123126, + "map_mtime": 1793155110, "pct": { "ER_IROM1": 25.23, "RW_CCM": 87.89,