diff --git a/docs/问题记录/项目审查与优化建议报告.md b/docs/问题记录/项目审查与优化建议报告.md index 3593a114..51435796 100644 --- a/docs/问题记录/项目审查与优化建议报告.md +++ b/docs/问题记录/项目审查与优化建议报告.md @@ -1,648 +1,386 @@ # STM32F4-Base 项目审查与优化建议报告 -| 项目 | 内容 | -|------|------| -| 审查日期 | 2026-09-18(初版) | -| **最近复核** | **2026-10-15**(R-9 落地后重链接),基线 `HEAD = a7f5265`(分支 `v2-on-ui`,工作树干净;其后的 `0cece5a` 文档提交与 `172b3f4` 模拟器侧改动都不影响固件);固件基线 = `MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(0 Error / 0 Warning,Code 212,344 = 已含 `506eccc` 的 `cfg_file` / `sd2506` 改动,比 2026-10-14 那次干净基线多 48 B)。本报告只在**内存/Flash 水位**上追这次构建,其余结论仍属 2026-10-14 复核 | -| **销项状态** | 本报告采用**修复即销项**:已修复项的正文与表格行已删除,去处见文末**已销项条目的记录去处**;未闭环清单见 **§0.4** | -| 审查范围 | 仓库全量(2,466 个受版本控制文件 / 79.3 MB);重点为自研代码 50,874 行(191 文件)与构建产物内存画像 | -| 审查方式 | 静态审查 + 链接图(.map)量化分析 + 构建脚本/门禁核对;**未做上机动态验证**(除特别标注外) | -| 目标 | 结构合理性、文档分布合理性、模块设计合理性、改进路线图、内存优化、UI 标准化 | - -> **与既有文档的关系**:本报告不重复 [`../任务规划/README.md`](../任务规划/README.md)(逐条缺陷台账 + **推进顺序唯一权威**)与 `../内存管理/RAM使用分析与优化方案.md`(内存专项方案),而是做**体系层评审 + 量化补强 + 新增发现**。 +> 版本 **v3.0** 2026-10-22(v2.x 2026-09-11 起多次增量修订) > -> **编号口径**:本报告自有编号 `N-xx / D-x / S-x / R-x / F-x / U1~U5`,⚠ **与登记簿同名不同义**(报告 N-16 = 日志逐条开关,登记簿 N-16 = A2/A4 负向台架;报告 S-3 = 注册反向校验,登记簿 S-3 = USART1 TX-only 根治)→ **引用一律带文档名**。未闭环项已于 2026-10-11 并轨进登记簿(新编 `N-19`~`N-29` + U 簇,出处列保留本报告编号);**本报告编号本身不回改、不重排**(`../编号规范.md`)。 +> **本版是全量重写**:按 2026-10-22 的仓库重新取数审计,已闭环条目、失效数字与已完成的规划正文(六维 +> 评分旧表、UI 8 人日方案正文、波次交付物清单)整段删除。**刻意保留三层**:结论层(§0~§7)、**编号族 +> 登记处(§9,`check_ids.py` 的属主登记处,删了就断门禁)**、变更记录与销项去处(文末,按项目纪律冻结)。 > -> **销项约定**:问题修复后其分析正文与表格行**从本报告删除**,完整记录沉淀到对应模块文档。因此本报告**只呈现未闭环问题**,不随时间膨胀。 - ---- +> ⚠ 待办**顺序的唯一权威** = [`../任务规划/README.md`](../任务规划/README.md) §2;本文只留"评审快照 + +> 证据 + 为什么这样判",不另立排期,避免两份清单漂开(本版重写就是因为旧 §0.4 与登记簿漂开了)。 ## 0. 执行摘要 -### 0.1 六维评分(2026-10-15 复核) +### 0.1 六维评分(2026-10-22 复评) -| 维度 | 评分 | 一句话结论 | 仍开放的主要缺口 | -|------|------|-----------|-----------------| -| 项目结构 | **A−** | 六层分层真实可执行、命名一致、门禁到位;反向依赖控制在 4 处 | `NETCFG`/`UI` 语义层错位(S-1)、4 处裸 `extern`(S-2)、`rs485_task.c` 仍在注册白名单里等 B2 | -| 文档分布 | **A−** | 工程文档 **87 篇 / 30,781 行**(2026-10-15 随 `错误码传播审计.md` 删除而 −1 篇 / −635 行),`docs/README.md` 索引 + `docs/文档规范.md` + 三档一致性门禁全部落地,`check_doc_links --all` = 0 失效 | **D-1**:`docs/` 占仓库 **71.8%**、手册图片 **36.3 MB**、`.git` 已 **235 MB**(已决策暂不处理,登记观察) | -| 模块设计 | **A−** | 看门狗(IWDG + 心跳位图 + 4 个 Fault 现场 + `.noinit`)、就绪屏障、卷锁、FTP 鉴权、`Error_Handler` 自恢复、ADC 全链均已落地;**原列的 dhara 移植层两处缺陷(N-12/N-13)与日志逐条开关(N-16)已于 2026-10-15 修复** → `GD5F2GQ5UE_Trap_Records.md` TRAP-GD5F-07、`../架构设计/STM32F4-Base存储架构说明.md` §5.3、`../子系统设计/运行与自检记录设计说明.md` §4.4/§4.5 | 驱动层无统一错误码约定(S-5)、RS485 无协议栈(随 B2 暂缓) | -| 内存使用 | **B+** | 堆迁 CCM + 栈/堆三面观测常开 + `mem-budget` 92% 门禁 + 预算台账与趋势基线;**R-9 已落地(2026-10-15)→ SRAM1 由 74.9% 降到 56.1%;同日移植层与日志修复 +1.6 KB → 现 **57.4%(余 55,840 B)**,不再是卡点** | **内存优化簇已整簇暂缓**(用户裁定 2026-10-15):R-2 后半(待迁 29,570 B → CCM,只差缩堆 56→32 KB)与 R-3 / R-4 / R-6 不再排期,理由与重开条件见 §5.3;**当前真正的约束是 CCM 只剩 7,904 B**——加任务 / 抬栈 / 加堆会先在这里撞墙,届时按 §5.3 重开;MicroLIB 512 B C 堆仍无机械检查(§5.5) | -| Flash 使用 | **A−** | F-1/F-2/F-3 落地省 64,576 B;**F-6 亦已落地(2026-10-22,−332 B)→ 现 22.1%(231,768 B)**,1 MB 用两成、余 ≈800 KB,产品需求无 OTA/双镜像预算 | **F-4 判定不做、F-5 降级观察项**(实测复核见 §5.4)→ **Flash 侧已无可回收项** | -| UI 标准化 | **B+** | W6 第一波已落地(2026-10-15):**L0 设计令牌层** `ui_tokens.h` 建成、**L2 页面注册表** `ui_page_registry.{c,h}` 由生成器产出并**删除了手写 `case` 分发**、`font-charset` 钩子 + `registration-check` 第 ④ 类接线校验上线、字体子集瘦身 −80 B | L1 组件库已起步(U2 首个 `ui_w_records`,2026-10-20),但仅此 1 个组件、**目标页未落地**(现存 7 个 `ui_page_*.c`)、**golden 像素回归已于 2026-10-20 挂入提交门禁**(`ui-golden`)、`ui_w_form` 推迟 U5(§6) | +| 维度 | 评级 | 今天的实测依据 | 相比 2026-10-15 的变化 | +|---|---|---|---| +| 结构与分层 | 🟢 良好 | 单向依赖未被破坏;双端注册反向校验 PASS;326 个编译单元全部可追溯 | 无退化 | +| 文档与治理 | 🟢 良好 | 37 个提交钩子 + 16 套主机单测;工程 md 91 份;`doc-links` 全库 0 问题 | 新增 `编号门禁增量拦截` 与内存快照回填常态化 | +| 内存与 Flash | 🟡 可控但已换位置 | SRAM1 **57.4%**(余 55,808 B)、Flash **22.1%**、**CCM 87.9%(余 7,904 B)** | **红线从 SRAM1 迁到 CCM**(堆 2026-10-11 迁 CCM + 池 40→16 KB 后 SRAM1 已宽松) | +| 可靠性与自恢复 | 🟢 良好 | 4 个 Fault 全采集现场;IWDG 五面 `required_mask`;复位原因与 `[STACK]` 水位常开 | 台架复跑持续 PASS(今日真机 708 ms 起网、无 `[ERR]`/Fault) | +| 测试与验证 | 🟡 半办 | 16 套主机单测 + `ui-golden`/`font-charset` 入门禁;TC 双向追溯 593∶271 交集 265 | **仍无覆盖率**、`coding-standard` **仍无 `malloc` 正则** | +| UI 标准化 | 🟢 基本落地 | `ui_tokens.h`(L0 令牌)+ `ui_page_registry.c`(描述符注册表)+ 7 个内容页 + 两枚 UI 钩子 | **U1/U3/U4 从"未做"变为已落地**;U2 只有 `ui_w_records` 一个组件 | -### 0.3 主线建议 +### 0.3 主线建议(2026-10-22) -> **推进顺序的唯一权威是 [`../任务规划/README.md`](../任务规划/README.md) §2**;本节只是体系评审视角的一条结构性建议,不另立顺序。 +1. **最大功能缺口仍是 RS-485 采集未接线**(B2 按用户决策冻结);接线前必须先过 E-59 门禁—— + `rs485.c` 仍有 3 处 `HAL_Delay`(E-49 超时拉 DE、E-50 单缓冲撕裂未修)。 +2. **CCM 是当下唯一预算红线**(余 7,904 B)。加任务 / 抬栈 / 加堆都会先在这里撞墙;`mem-budget` + 92% 钩子会先报警。SRAM1 与 Flash 都已不是卡点。 +3. **结构性内存优化整簇暂缓**(R-2 后半 / R-3 / R-4 / R-6,用户裁定 2026-10-15),治理面常开; + 重开条件见 §5.3。今天又收口一项:**F-4 判定不做、F-6 已落地、F-5 降级观察**(§5.4)。 +4. **验证的下一步不是加用例而是补面**:覆盖率口径 + `malloc(` 门禁正则(§5.5 第 5 条),把"读代码 + 才发现"的缺陷类型(N-12/N-13 那类)变成机器拦截。 +5. **仓库体积重心在外部资料**(`docs/芯片手册` 717 文件 / 39.5 MB,占 docs 的 87%;`.git` 248 MB), + 见 §3.3 D-1 与登记簿 N-29——已决策暂不处理,但**推送/对外协作前必须重评**。 -1. **把 UI 从"每页手写"变"声明 + 原型库"**:令牌层 → 原型库 → 生成器补全注册 → golden 入门禁,使新增一页的成本从 8 处手工改动降到 1 处声明(§6)。 +## 0.4 未闭环清单(2026-10-22 复评) ---- +> 只列**仍未闭环**项,已修项按「修复即销项」删除(去处见文末)。逐项细节以 +> [`../任务规划/登记簿.md`](../任务规划/登记簿.md) 为准,本表是评审快照(图例:❌ 未办 / 🟡 半办 / +> ⏸ 暂缓 / 🟡 观察)。 -## 0.4 未闭环清单(2026-10-15 复核) - -> 只列**未办 / 半办**项;已修项按「修复即销项」删除(去处见文末)。图例:❌ 未办 / 🟡 半办。 -> **顺序权威在 [`../任务规划/README.md`](../任务规划/README.md) §2,本表为评审快照**;登记簿编号在括号内。 - -| 类别 | 编号 | 未闭环内容 | 下一步 | -|------|------|-----------|--------| -| 内存 RAM | **R-2 剩余** ⏸ **暂缓**(N-27,原 W3) | 待迁体量已由 54,146 B 降到 **29,570 B**(R-9 落地后);CCM 只剩 7,904 B | **用户裁定 2026-10-15:暂不做**——没有功能在等这块预算;重开条件 → §5.3 | -| 内存 RAM | R-3 / R-4 / R-6 ⏸ **暂缓**(N-27,原 W3) | 页缓冲未合并 / UI 帧 4 副本 / 栈仍动态 | **同上裁定,暂不做**。本簇已落地的只有 R-9(2026-10-15,SRAM1 74.9%→56.1%,见 §5.3);**R-5 已判定不可行**(§5.3) | -| 内存 RAM | N-14 🟡(N-22,W4) | 写回缓存无空闲/掉电强制 flush(整页写与换页已 flush) | 空闲 N ms 由 defaultTask flush | -| **Flash** | F-5 🟡 观察项(N-28,W4) | ~~F-4~~ **判定不做**、~~F-6~~ **已落地**(2026-10-22,见 §5.4)。仅剩 uvprojx 挂 201 个 LVGL `.c`,其中 **127 个在 `.map` 里零产出**(空 TU)——只在全量重建 / 改 `lv_conf.h` 时才重编;增量构建实测 4~5 s,且远程 CI 刻意不跑 `@build` → 非产品指标 | 有实测的全量构建耗时痛点时再做:用 Keil 原生 `0`(可逆)而非删条目,且必须排在任何 `lv_conf.h` 改动之后(空 TU 集合随裁剪变化) | -| **性能/资源** | N-09 / PHY / P3-x / rs485 DE ❌(N-25,W4) | PASV 每次新建监听;PHY 强制 100M 无自协商回退;`NET_MAX_SOCKETS 8` 与单连接语义不符;`HAL_Delay(1)` 阻塞 DE(DE 项随 B2 暂缓) | 见 §4.2 / §4.7 各行建议 | -| **结构** | S-1 / S-2 / S-4 ❌ | `NETCFG`/`UI` 语义层错位;4 处裸 `extern`;`app_cfg.h` 开关未接线未删 | 随登记簿 C5 处置;`rs485_task.c` 随 B2 一并出白名单 | -| **存储/日志** | N-15 🟡 / P2-10 ❌ | `nand_ftl` 三块 2 KB 页缓冲未合并(BBT 缓冲已收紧);容量轮转 `f_unlink` 与 UI 读并发未纳入卷锁 | R-3 评估;卷锁覆盖面 → 登记簿 S-5 | -| **驱动规范** | S-5 ❌ | 无统一"错误码 + 超时 + 重试 + 自检上报"约定(`GD5F_OK` / `0` / `bool` 混用) | 定义 `bsp_err_t` + `bsp_retry()` 写入 `docs/代码规范/` | -| **仓库** | D-1 ❌(N-29,W7) | 图片 36.3 MB 仍在仓;**`.git` 已 235 MB** | 降采样 / 拆 submodule / LFS 三选一 **+ 一次 `git filter-repo` 历史重写**;**已决策暂不处理、登记观察** | -| **UI** | U1 / U2 / U3 / U5 ❌(U 簇,W6) | 无令牌层、无组件库、无页注册表(手写 `case` 仍在);**27 页**未落地 | 按 §6.5 顺序,U4 的 golden 已可当 U1 的验收工具;**本轮不排期** | -| UI | U4 残余 🟡(U 簇,W6) | golden 已建但未挂 pre-commit;字符集差集检查仍是约定 | 两个脚本入门禁 | -| **测试** | P1-10 🟡 / 覆盖率 ❌ | CH395F 套件收尾两步(构建实测链接 + `@test` 闭环);无覆盖率概念;主机侧对 `dhara` 移植层无测试 | N-12/N-13 这类缺陷至今只能靠人工读代码发现 | - ---- +| 类别 | 编号(登记簿) | 今天的现状 | 下一步 | +|---|---|---|---| +| 功能缺口 | RS-485 采集(B2) | `rs485.c` 3 处 `HAL_Delay` 未修 → E-59 挡住 `rs485_task` | 待用户排期;先修 E-49/E-50 再谈接线 | +| 硬件改版 | **N-18** | V0.1 把 8 路电气量分给两片转换器(无共同采样触发,跨转换器相位/功率不可用) | V0.2 全接 TPAFE;V0.1 上禁做矢量运算 | +| 内存 RAM | R-2 剩余 / R-3 / R-4 / R-6 ⏸(N-27) | 待迁体量 29,570 B,而 CCM 只剩 7,904 B → 搬迁已无空间收益 | 暂缓;重开条件 §5.3 | +| 内存 RAM | N-14 🟡(N-22) | FTL 单页写回缓存无空闲/掉电强制 flush(整页写与换页已 flush,`f_close` 经 `CTRL_SYNC` 已落盘) | 用户裁定 2026-10-22 暂不做;残余窗口只是"单条记录" | +| Flash | **F-5 🟡 观察项**(N-28) | uvprojx 201 个 LVGL `.c` 里 **127 个空 TU**;增量构建 4~5 s、CI 不跑 `@build` | 有实测全量耗时痛点再做,用 `` | +| 结构 | S-1 / S-2 / S-4 ❌ | `NETCFG`/`UI` 语义层错位;裸 `extern` 仍在;`app_cfg.h` 三个 `APP_*_ENABLE` 里两个是未接线占位(未删) | 随登记簿 C5 处置 | +| 驱动规范 | S-5 ❌ | `bsp_err_t` 至今只出现在本报告里(无统一错误码/超时/重试约定) | 定义后进 `docs/代码规范/` | +| 网络 | N-09 / PHY ❌(N-25) | `NET_MAX_SOCKETS` 仍为 8(与单连接复用语义不符);PHY 强制 100M 无自协商回退;PASV 每次新建监听 | 见 §4.2;随 N-25 一并处置 | +| 存储 | N-15 🟡(N-25) | `nand_ftl.o` 静态 6,216 B——三块 2 KB 级页缓冲并存未合并 | 与 R-3 同批评估 | +| 存储/日志 | P2-10 ❌(N-25) | 卷锁现有 5 个使用者(配置原子写 / 日志 / SOE / lftpd_io / defaultTask),容量轮转删除与 UI 读并发仍未全覆盖 | 登记簿 S-5 面(卷锁覆盖面) | +| 仓库 | D-1 ❌(N-29) | jpg 707 个 / 37.3 MB;`docs` 45.3 MB 里芯片手册占 39.5 MB;`.git` **248 MB** | 降采样 / 拆 submodule / LFS 三选一 + 历史重写;已决策暂不做 | +| UI | U2 残余(U 簇) | 组件库只有 `ui_w_records.c/h` 一个;其余仍是页内自绘 | 按产品需求决定要不要继续抽组件 | +| 测试 | 覆盖率 ❌ / `malloc` 门禁 ❌ | 无覆盖率脚本;`check_coding_standard.py` 无 `malloc(` 正则 | 见 §5.5 与 §4.8 | +| CI | T-7 ❌ | 远程 CI 只跑本机一台 runner,且不跑 `@build` | 保持现状即可;扩机器前重读 `docs/CI/README.md` | ## 1. 审查方法与证据基线 -### 1.1 数据源 +### 1.1 数据源(本版全部结论的出处) -| 数据 | 来源 | 可信度 | -|------|------|--------| -| 内存/Flash 占用 | `.map` 的 component 表(237 个含码对象,列序 `Code incData RO RW ZI Debug Name`) | 高(构建产物,权威) | -| 总量 | map 尾部 `Total RO / Total RW / Total ROM` + `Execution Region` 表 | 高 | -| 依赖方向 | 逐文件 `#include` 扫描 + map 的 `refers to` 交叉表 | 高 | -| 构建注册 | `MDK-ARM/STM32F407-Demo.uvprojx` + `simulator/CMakeLists.txt` + git 文件清单三方对齐(已机械化:`scripts/check_registration.py`) | 高 | -| 文档链接 | `scripts/check_doc_links.py --all`(含 `--sections-strict`) | 高(门禁) | +- 体量:`MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(2026-10-22 `@build`)→ `data/ram_report_20261022.md` +- 栈:`STM32F407-Demo.htm`(静态调用图)+ 同日真机 `[STACK]` 心跳 → `data/stack_report_20261022.md` +- 仓库:`git ls-files` 逐个 `stat`(必须 `-z`,否则中文名被控制台代码页破坏) +- 门禁:`.pre-commit-config.yaml`(37 个 `- id:`)+ 各钩子今日实跑输出 +- 待办:`docs/任务规划/登记簿.md`(在策 18 条)与 `docs/任务规划/README.md` §2 ### 1.2 复现命令(本报告所有数字可重跑) ```powershell -# 1) 构建体量(三区水位 + Top-10 RW+ZI) -& '.venv\Scripts\python.exe' scripts/ram_report.py -& '.venv\Scripts\python.exe' scripts/ram_baseline.py --trend -# 2) MicroLIB C 堆真实大小(应为 0x200 = 512 B) -Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap_base|__heap_limit|HEAP' -# 3) 三方文件注册对齐(uvprojx / CMakeLists / git) -& '.venv\Scripts\python.exe' scripts/check_registration.py --strict -# 4) 仓库体量:git ls-files -z | 逐个统计字节(必须用 -z,否则中文名会被控制台代码页破坏) +# 1) 三区水位 + Top-10 RW+ZI +& '.venv\Scripts\python.exe' scripts/ram_report.py MDK-ARM/STM32F407-Demo/STM32F407-Demo.map -o docs/内存管理/data/ram_report_<日期>.md +# 2) 每任务栈:静态最坏链 × 真机 [STACK] 水位 +& '.venv\Scripts\python.exe' scripts/stack_report.py -i MDK-ARM/STM32F407-Demo/STM32F407-Demo.htm -s .dsh/skills/serial-assistant/scripts/state/serial.log -o docs/内存管理/data/stack_report_<日期>.md +# 3) MicroLIB C 堆(应为 0x200 = 512 B) +& 'tools\grep_binaries\find.exe' -E 'Heap_Size +EQU' startup_stm32f407xx.s # 或直接 git grep Heap_Size -- Src +# 4) 仓库体量:逐文件字节(-z 防中文名破坏) +git ls-files -z | xargs -0 stat -c '%s' +# 5) 门禁全量自查(不含 @build) +& '.venv\Scripts\python.exe' scripts/ci_local.py ``` -> ⚠️ **踩坑记录**(供后续审查复用): -> ① map 组件表是 **6 列数值**(含 `incData` 与 `Debug`),按 5 列解析会得到 0 个对象; -> ② `git ls-files` 输出经 PowerShell 管道会按控制台代码页破坏中文路径,导致"文件不存在"的假结论,必须用 `-z` + 字节流解码; -> ③ 终端渲染的中文乱码不等于文件损坏,判定损坏须统计 `EF BF BD` 字节数(本报告统计结果:**0**,全库无 U+FFFD)。 - ### 1.3 口径定义 -| 术语 | 定义 | -|------|------| -| 自研代码 | `App/`、`Drivers/BSP/`(不含 `Lib/`、`Middlewares/`、HAL/CMSIS)、`test/`;不含 CubeMX 生成的 `Src/`、`Inc/` | -| Flash 占用 | `Code + RO-data` = `ER_IROM1` **231,768 B(22.1% of 1 MB,2026-10-22 构建)**;`ROM` 再加 `RW-data` 初始化表 = 232,060 B | -| RAM 占用 | `RW-data + ZI-data`,**跨两个区域** = SRAM1 `RW_IRAM1` **75,232 / 131,072(57.4%)** + CCM `RW_CCM` **57,376 / 65,280(87.9%)** + `UNINIT_CCM` 48 B(`.noinit` 崩溃现场保留区)。⚠ 原报告"CCM 不在 scatter 内计 0"的口径已不成立 | -| "死代码" | 该对象在镜像中有码,但其功能被工程配置(如 `LV_COLOR_DEPTH 1`)或调用点扫描(零调用者)证明不可达 | - -> **内存数字的权威源**:区域水位与子系统配额由 `../内存管理/RAM预算台账.md`(受管区,`ram_baseline.py --write` 生成)持有,趋势在 `scripts/ram_baseline.tsv`。本报告只在支撑结论处引用,不与之竞争事实源。 - ---- +| 名词 | 口径 | +|---|---| +| Flash 占用 | `Code + RO-data` = `ER_IROM1` = **231,768 B(22.1%)**;`ROM 合计` 再加 `RW` 初始化表 = 232,952 B(22.2%) | +| SRAM 占用 | `RW_IRAM1`(DMA 可达区)与 `RW_CCM`(仅 CPU)分开计,二者不可互换(规则 R2/R3) | +| 最坏栈 | `max(armlink 静态调用图深度, 真机 [STACK] 高水位)`;判定线 `余量 ≥ max(1024 B ISR, 配置×30%)` | +| 编译单元 | uvprojx 的 `` 计数(今日 **326 个 `.c`**,其中 LVGL 201);"自研 `.c`" 另按目录口径 114 个 | +| 工程文档 | `docs/` 的 `.md` 105 份,其中 `docs/芯片手册` 14 份为外部资料 → 工程侧 **91 份** | ## 2. 项目结构合理性 -### 2.1 规模与分布(2026-10-14 实测) +### 2.1 规模与分布(2026-10-22 实测) -| 分类 | 文件 | 行数 | 说明 | -|------|------|------|------| -| `App/`(自研应用) | 64 | 13,841 | 配置引擎、墙钟、日志、任务编排;**`App/algo/`(L1~L3 + 测频 + 定值/SOE)14 文件 / 2,855 行** | -| `Drivers/BSP/`(自研驱动/子系统) | 76 | 21,845 | NET 8,264、UI 6,073(含 `ui_font_cn_12.c`)、CH395F 2,641、GD5F 2,035、SD2506 835、NETCFG 766、TPAFE 680、RS485 310 | -| `test/`(自研测试) | 51 | 15,188 | **`test/host/` 主机侧单测 33 文件 / 7,634 行** + `test/board/` 板级套件 18 文件 / 7,554 行 | -| **自研合计** | **191** | **50,874** | 2026-09-18 基线为 108 / 24,353 | -| CubeMX `Src/`+`Inc/` | 26 | 5,528 | 受 USER CODE 标记保护 | -| `simulator/`(自研部分,不含 SDL2 SDK) | 10 | 2,665 | PC SDL2 宿主 | -| 第三方 `Lib/` | 958 | 529,983 | LVGL 9.3、FreeRTOS、FatFs、dhara、lftpd | - -**评价**:自研体量与第三方体量比约 **1:10.4**(基线 1:20),仍属"薄自研 + 厚中间件"。这决定了**结构审查的重点不在自研代码内部,而在"自研如何约束第三方"**——§5.4 量化的 `lv_conf.h` 约束失守代价(64,576 B 死代码)已修,但**两份 `lv_conf.h` 必须同一特性子集**这条硬约束如今靠 `lv-conf-sync` 钩子守住,属"约束被机械化"的正例。 +| 顶层 | 入库文件 | 字节 | 说明 | +|---|---|---|---| +| `docs/` | 881 | 45.3 MB | **全仓最大目录**,其中 `芯片手册` 717 文件 / 39.5 MB(外部资料,见 D-1) | +| `Lib/` | 1,036 | 21.3 MB | lvgl + FatFs + dhara(只读,本地补丁须文档登记) | +| `Drivers/` | 192 | 8.9 MB | 手写 BSP(含 `UI` 42 文件、`NET`、`CH395F`、`GD5F2GQ5UE`) | +| `App/` | 74 | 629 KB | 应用层 L1~L3 + 任务编排 + 配置/日志/卷锁 | +| 全仓 | **2,498** | **80.0 MB** | `.git` 248 MB(D-1/N-29 的关注点在这里) | ### 2.2 分层与依赖方向 -实测自研层间的**全部反向依赖**(BSP → App)——2026-10-14 复核为 **4 处**: +单向依赖(业务 → UI/基础服务 → 中间件 → BSP → HAL/RTOS)在今天的抽查里**未被破坏**:算法落 +`App/algo/`(L1 `adc_input`/`adc_health`、L2 `sample_ring`/`phasor`、L2.5 `freq`、L3 `prot_set`/`prot_elm`), +任务侧只做消费与编排。三处"属主唯一"约束仍是硬规矩:日志唯一写者 = defaultTask、配置引擎属主 = +defaultTask、`net_*` 只在 netTask 串行执行。 -| 位置 | 反向 include | 判定 | -|------|-------------|------| -| `Drivers/BSP/NETCFG/net_cfg.c:31` | `cfg_file.h` | 可接受但需说明:NETCFG 实为"业务配置模块",放在 BSP 层是历史命名 | -| `Drivers/BSP/NETCFG/ftp_cfg.c:17` | `cfg_file.h` | 同上(FTP 鉴权配置随 `net_cfg` 落在同一目录) | -| `Drivers/BSP/UI/ui_page_runlog.c:19` | `sys_log.h` | UI 读日志属正常消费者,但 `sys_log` 在 App 层 → 层次倒置 | -| `Drivers/BSP/UI/ui_page_selftest.c:19` | `sys_log.h` | 同上 | -| `Lib/FatFs/ffsystem.c:19` | `sys_clock.h`(App/util) | 中间件→应用;已在文件内注释声明为"④中间件层"有意为之 | +### 2.3 双构建清单对齐 -**评价:良好**。在 50k 行规模上把反向依赖控制在 4 处,说明分层不是墙上的图。但存在**语义层错位**: +`registration-check`(反向校验 + UI 页面描述符接线)今日实跑 PASS:uvprojx 与 `simulator/CMakeLists.txt +` 无单侧缺项。**新增信息**:uvprojx 里 201 个 LVGL `.c` 有 **127 个在 `.map` 中零产出**(空 TU), +它不影响镜像只影响全量重编 → 判为观察项(§5.4 F-5),不是缺陷。 -- `Drivers/BSP/NETCFG/`、`Drivers/BSP/UI/` 名字是"板级支持包",内容是**业务**(网络/FTP 配置语义、菜单/页面/权限/内容页框架)。后果:`sys_log`(App)与 `net_cfg`(BSP)互相引用,层次图自相矛盾。 -- **建议(S-1)**:把 `NETCFG`、`UI` 在概念上重新定性为 `App/` 或新增 `Service/` 层,`Drivers/BSP/` 只留下与引脚/芯片强绑定的 `ch395f`、`gd5f2gq5ue`、`sd2506`、`tpafe5160`、`rs485`、`dbg_*`。迁移成本最低的做法:**先改文档与分层图(零风险),目录迁移留到有 CI 兜底之后**。 +### 2.4 根目录卫生 -依赖方向裸引用(绕过头文件契约的 `extern`)实测 4 处——**2026-10-14 复核:4 处全部仍在**,S-2 未办: - -| 位置 | 裸 extern | -|------|-----------| -| `Drivers/BSP/CH395F/ch395f.c:177` | `extern volatile uint8_t g_spi1_dma_done;`(实为 GD5F 在 `gd5f2gq5ue.c:50` 定义的变量,跨模块反向且**未进任何头文件**) | -| `Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c:47` | `extern SPI_HandleTypeDef hspi1;`(`Inc/spi.h` 已导出,局部重复) | -| `Drivers/BSP/NET/net_socket.c:77` | `extern osMessageQueueId_t netMsgQueueHandle;` | -| `Drivers/BSP/SD2506/sd2506.c:20` | `extern I2C_HandleTypeDef hi2c1;`(`Inc/i2c.h` 已导出,局部重复) | - -**建议(S-2)**:CubeMX 句柄类(`hspi1`/`hi2c1`)统一由 `main.h` 导出并 include,删除局部 extern;`g_spi1_dma_done` 应由 GD5F 模块自己定义并在头文件 `extern` 声明(当前跨模块裸 extern 是真正的耦合隐患);`netMsgQueueHandle` 改为 net_socket 内部持有 + 提供投递 API。 - -### 2.3 双构建清单漂移 - -把 **git 文件清单 × uvprojx × simulator/CMakeLists** 三方对齐后: - -| 类别 | 文件 | 后果 | -|------|------|------| -| **两边都不注册** | 基线 5 个 → 现剩 **1 个**:`App/task/rs485_task.c`(E-59 门禁下的**刻意**缺口,随 B2 暂缓,已登记 `KNOWN_GAPS`) | `crc.c`/`ringbuf.c` 已整体删除(全库无调用者)、`lftpd_log.c` 已删除并入 `lftpd_auth.c`、`adc_task.c` 已挂 uvprojx(仅 MCU,符合"任务层不入模拟器")——**"写了不编译"的盲区已消** | -| **仅 simulator 注册** | `cfg_file_store_sim.c`、`net_socket_sim.c`、`sim_port.c`、`sim_syslog.c`、`ui_net_sink_backend_sim.c` | 正常(后端适配层模式) | -| **uvprojx** | 326 个 `.c`(含 201 个 LVGL,其中 **127 个在 `.map` 里零产出** = 空 TU) | LVGL 全量参与编译 → 只在全量重建 / 改 `lv_conf.h` 时体现(见 §5.4 F-5) | - -**评价**:AGENTS.md 的"新文件必须注册两处"铁律如今**两端都有机械保障**——正向由 `new-source-file` skill 约束,反向由 `scripts/check_registration.py`(pre-commit `registration-check` 钩子,2026-10-12)校验"存在但未注册",并反向查"注册了但文件不存在"的幽灵注册。**残余只有一条刻意缺口**:`rs485_task.c` 在 `KNOWN_GAPS` 里等 B2 立项,随该簇一并处置即可(清完后把 `--strict` 设为默认)。 - -### 2.4 目录组织与根目录卫生 - -| 观察 | 证据 | 判定 | -|------|------|------| -| 根目录散置工作文件 | `ui_menu_tree.txt`、`ui_cn_chars.txt` | 菜单树/字符集是**生成器输入**,放根目录合理且已在 `docs/` 有权威说明 | -| `Lib/` 命名 | `Lib/` 下混放 `lvgl/`、`lv_conf.h`、`FatFs/`、`dhara/` | `lv_conf.h` 是**工程配置**不是库文件;建议移到 `Config/lv_conf.h` 并同步 include 路径(低风险;⚠ 须一并处理 `simulator/lv_conf.h`,两份必须同一特性子集) | -| 功能开关名存实亡 | `App/app/app_cfg.h` 的 `APP_NET_ENABLE/APP_ADC_ENABLE/APP_RS485_ENABLE` **全库零引用** | 开关不生效比没有开关更糟——评审者会误信"ADC 可裁剪"。文件头 v1.1 已如实标注"预留占位、不门控任何代码",但**仍未接线也未删**(S-4) | +根目录 37 项。本机产物均**未入库**且已被忽略:`sim_config.ini`、`sim_run.log`、`sim_selftest.log`、 +`ci-artifacts/`、`.workbuddy-ai/`、`tmp_save.bin`(由 `*.bin` 规则命中,属**顺带**忽略而非显式声明—— +改名即失守,建议显式登记)。入库的两个 `ui_cn_chars.txt` / `ui_menu_tree.txt` 是字模提取与菜单树清单, +与生成器一一对应,可接受。**没有发现散落的临时脚本或裸备份文件。** ### 2.5 结构结论 -**总体判断:结构合理,无需重构**。要做的是三类**低成本纠偏**:S-1 层次定性、S-2 消裸 extern、S-4 让 `app_cfg.h` 开关真正生效或删除。 - ---- +结构本身不是当前风险源;风险集中在 §0.4 的语义层错位(S-1/S-2/S-4/S-5)与体积面(D-1)。 ## 3. 文档分布合理性 -### 3.1 体量画像(2026-10-14 实测,工作树) +### 3.1 体量画像(2026-10-22) -| 对象 | 文件数 | 体积 | 占仓库 | -|------|--------|------|--------| -| 仓库受控文件总计 | 2,465 | **79.2 MB** | 100% | -| `docs/` 合计 | — | **56.9 MB** | **71.8%** | -| └ `docs/芯片手册/` 图片 | 703 张 | **36.3 MB** | **45.8%** | -| `.git` 目录 | — | **235.3 MB** | — | -| 工程文档(Markdown,自研) | 87 篇 / **30,781 行**(2026-10-15 随 `错误码传播审计.md` 删除,−1 篇 / −635 行) | — | — | -| 外部资料(Markdown,datasheet 转录) | 14 篇 / 58,244 行 | — | — | +- `docs/` = 881 文件 / 45.3 MB;其中 `docs/芯片手册` **717 文件 / 39.5 MB(87%)**,`.jpg` 全库 707 张 / 37.3 MB +- 工程 md **91** 份(外部资料 md 14 份,最大单份 `STM32F4xx参考手册_V18英文版_1-1000.md` = 40,239 行) +- `docs/images` 23 文件 / 2.0 MB——**全部由 `tools/arch_diagram/gen_*.py` 生成**,脚本与产物一并入库 -**关键失衡**:工程文档与外部资料的行数比已从 1:4.2 改善到 **1:1.9**,但**全部 Markdown 只有 5.4 MB,图片体量(36.3 MB)是它的 6.7 倍、占仓库近一半**,`git clone` 的净耗时几乎全花在扫描手册图片上。⚠ `.git` 已达 **235 MB**(受控文件才 79.3 MB),说明历史里存过多版图片/产物——**D-1 单靠拆仓或 LFS 不足以解决 clone 耗时,还需一次历史重写**。 +### 3.2 已制度化、应保留的做法 -### 3.2 做得好的(应保留并显式制度化) - -1. **文档密度与可执行性**:每篇都带"步骤 + 判据"(如 UI 制作流程有 P0→P4 Gate);测试说明按模块拆分并给出预期串口标记,可直接接 `@test`。 -2. **陷阱台账**:`docs/问题记录/*_Trap_Records.md` + `docs/任务规划/`(带状态与日期)——这是本工程最有价值的资产,多数嵌入式项目没有。 -3. **图与脚本同提交**:`tools/arch_diagram/`、`tools/refgraph/` 生成器 + 产物入库,图可再生而非手绘。 -4. **UI 设计资产链完整**:原始参考图 → 说明 md → 代码,可追溯。 -5. **文档一致性已机械化**:`doc-links`(含 `--sections-strict`)、`task-table`、`mem-numbers`、`lv-conf-sync`、`cubemx-survival` 等只读钩子守住"文档说的 == 代码做的"。 +- 数字同源:内存快照由 `check_mem_numbers.py --write` 写受管区,人只写判读文字 +- 章节同源:任务表由 `gen_task_table.py` 生成;配图由生成器出;引用图由 `tools/refgraph` 出 +- 引用可校验:`doc-links --sections-strict`(今日全库 146 文档 0 问题,章节引用 2,513 处、可判定 59%) +- 编号可校验:`id-check` 增量拦截(存量 650 指纹豁免,可判定 **82.98%** ≥ 基线 82.6%) ### 3.3 问题清单 -#### D-1(P1)外部资料与工程文档同仓同树 +#### D-1(P1)外部资料与工程文档同仓同树(登记簿 N-29) -**影响**:clone 慢、备份慢、code review 时 diff 噪音大;`docs/芯片手册/` 的 703 张图属于**只读输入资料**,与代码演进无关。 - -**建议**(三选一,按代价递增): -1. **最省**:把手册 JPG 降采样到长边 1,200 px(约 36 MB → 8 MB),一次性提交并在 `../文档规范.md` 写入"入库图片上限 200 KB/张"; -2. **推荐**:`docs/芯片手册/` 拆为独立仓库 `dtu-datasheets`,用 `git submodule` 挂回原路径(现有全部链接不需改写); -3. **最正**:Git LFS(需要托管端支持)。 - -> ⚠ 三项都不解决历史膨胀:`.git` 235 MB 需一次 `git filter-repo` 历史重写(破坏性操作,所有克隆需重取)。**2026-10-11 已决策:暂不处理、登记观察**(登记簿 N-29),推送/协作前重评——届时未推送提交更少、重写代价最低。 +今天实测:`docs/芯片手册` 717 文件 / **39.5 MB**(占 docs 87%),全仓 80 MB 的一半;`.git` **248 MB**。 +影响:clone / 备份 / worktree 开空间 / 对外交付的体积都被外部资料决定,且历史里删不掉。 +三个选项:① 图片降采样(一次性、可保留可读性)② 拆 submodule(干净但要改所有引用路径) +③ Git LFS(要服务端配合)。任一条都要配一次 `git filter-repo` 历史重写才真正减小 `.git`。 +**当前状态:已决策暂不处理**(本机单 runner、内网协作,痛点未爆发);**触发重评的条件** = 对外交付 / +新机器 clone 明显变慢 / 需要双 remote。 ### 3.4 文档结论 -**内容质量高(A−)、分布形态差**。D-1 处理后可到 A:工程量约 1 人日(不含拆仓与历史重写),收益是 clone 时间与"文档可信度"。 +文档制度是本项目最强的一面,但**同一事实的多份副本仍会漂**——本版重写正是这件事的第三次实证 +(旧 §0.4 未闭环清单与登记簿漂开)。因此本版把"待办清单"降级为快照 + 指向,**权威只留登记簿**。 +同类教训同日还有两例:`项目部署环境要求.md` 的任务栈副本表(已删表改指针)、 +`RAM使用分析与优化方案.md` 的阶段子项与顶层章节同号撞面(已改层级编号 `4.阶段.项`)。 ---- +## 4. 功能模块设计评审(现状复核) -## 4. 功能模块设计评审 +> 写法变更:本节不再重述设计(设计有专属文档),只记"今天复核到的状态 + 仍在册的问题 + 证据"。 ### 4.1 启动与任务框架 -**设计良好**: -- `app_main_init` 顺序有因(墙钟优先,使后续日志带时间戳); -- **Event Flags 就绪屏障**(`NET_READY/STORAGE_READY/FTP_READY/TIMESYNC_READY`)取代魔法延时,是本项目最漂亮的设计之一; -- 配置引擎属主收敛到 defaultTask 单任务,消除了跨任务竞态; -- 初始化失败路径已收口:`Error_Handler` 打 1 条自检日志 + `NVIC_SystemReset()`(不再 `__disable_irq(); while(1)`)。 - -**问题**: - -| 编号 | 问题 | 影响/建议 | -|------|------|-----------| -| S-4 | `App/app/app_cfg.h` 三个 `APP_*_ENABLE` 零引用(文件头已标注"预留占位") | 开关要么生效要么删;产品定义(登记簿 B0)落定前至少别让评审者误信"可裁剪" | +9 任务、就绪屏障四位(`osFlagsNoClear`)、内核前模式均已固化在 `docs/架构设计/项目启动顺序管理.md`; +`Src/freertos.c` 584 行(含 USER CODE 区手写 ISR 节拍)。原"仓库里没有 IWDG 驱动"等立项取证已随实施 +失效,取证归 `docs/子系统设计/看门狗设计说明.md`。 ### 4.2 网络子系统(CH395F + BSD 抽象层) -**设计良好**: -- 所有 `net_*` 经 `netMsgQueue` 串行到 netTask 执行 → 单线程语义清晰; -- 全部 CH395F 中断集中在 `net_poll()` 内 `GET_GLOB_INT_STATUS` 排空(AGENTS 铁律,已落实); -- 网络代次号机制使"改 IP 免重启"能重建各监听; -- **挂起 recv/send 模型**(`net_pending.c/h`)取代"强加 30 s 默认预算",recv 语义已主流化。 +`net_socket.c` 3,120 行 + 拆出的 `net_pending.c`(挂起 recv,真机专属);错误码经任务通知值回传; +单连接复用 + `auto_relisten` 语义已文档化。**仍在册**(N-25):`NET_MAX_SOCKETS = 8` 与单连接语义不符、 +PHY 强制 100M 无自协商回退、PASV 每次新建监听。 -**问题**: +### 4.3 存储子系统(GD5F + dhara + FatFs) -| 编号 | 问题 | 证据 | 建议 | -|------|------|------|------| -| N-09 | `cmd_pasv` 每次新建监听 `lftpd_inet_listen(0)`(随机端口) | `lftpd.c:472` | 在**单连接 socket** 上与 UI `:7003`、对时 `:8888` 争用同一物理 socket;建议限定监听资源池 + 显式"FTP 会话独占"策略,并在文档写明"一次只允许一个 TCP 服务活动" | -| — | PHY 强制 100M 全双工,忽略自协商 | `App/task/net_task.c:110` 无条件 `ch395f_set_phy(CH395F_PHY_100M_FULL)` | 与某些交换机会联不通;至少加"链路 up 失败 3 次后回退自协商" | -| P3-x | `NET_MAX_SOCKETS 8`(`net_config.h:33`)是**软件表**,与硬件单连接语义不符 | | 文档已说明,但 API 层看不到限制 → 建议在 `net_socket.c` 断言"同时 LISTEN 数 ≤ N" | +链路:GD5F SPI NAND → dhara FTL → FatFs → `/log`、`/config.ini`、FTP 根。**今天澄清的一处旧口径**: +`f_close → f_sync → sync_fs → disk_ioctl(CTRL_SYNC) → ftl_flush_cache()` 已把写回缓存落盘, +所以"必须写另一个文件才能逼出脏页"不成立(旧报告与源码注释都这么写,已改)。2026-10-02 那次 +"复位后整段丢失"的根因是 dhara resume 锚错 checkpoint,不是缓存未刷 → `../架构设计/STM32F4-Base存储架构说明.md` §5.4.1。 +残余风险 = 单次写事务中途掉电(登记簿 N-22,用户裁定暂不做)。 -### 4.3 存储子系统(GD5F2GQ5UE + dhara + FatFs) +### 4.4 配置引擎(`App/cfg/cfg_file.c`,1,358 行) -**设计良好**: -- 分层正确:`gd5f2gq5ue`(芯片)→ `nand_ftl`(dhara map/journal + 页缓存)→ FatFs R0.14 → 应用; -- `Lib/FatFs/ffsystem.c` 用真实 `osMutexNew` 实现 `FF_FS_REENTRANT=1` 的卷锁(多数移植是空实现)——**这是本工程移植质量的一个亮点**;`get_fattime()` 接系统墙钟,使 FAT 时间戳有意义; -- BBT 保留块池把坏块表存储区从 dhara 可见空间中排除,避免被 FTL 擦写; -- **DMA 可达性三层防线**(原 N-11 的处置,2026-10-11):编译期窗口断言(`gd5f2gq5ue.h:96-107` 负数组 typedef,DMA 白名单窗口与 CCM 永不得重叠 + 阈值契约)→ 启动期自检(`nand_ftl.c:241-243` 三块静态页缓冲逐一过守卫)→ 运行时强制(`gd5f_dma_buf_ok()` 不可达即回落轮询 + `[ERR]`);规则本身(**R2 DMA 缓冲只允许 SRAM 静态**、**R3 堆/栈禁为 DMA 源/目的**)已写进 `../内存管理/内存内容分配策略.md` §4,并有 TC-GD5F-704/705 负路径用例实证。**缓冲自身地址无法编译期断言**(`.bss` 地址链接期才定,AC5 取不到)——这条边界在头文件注释里写明,别再试图"加个 `_Static_assert`"。 +defaultTask 单属主;原子写经卷锁;旧档迁移已删除。**仍列为后续项**:9,217 B 暂态峰值(整文件驻留), +流式读写的方案与验收在 `docs/子系统设计/配置文件设计说明.md` §6.2/§8。 -**问题**: +### 4.5 日志子系统(`App/log/sys_log_mcu.c`,751 行) -| 编号 | 问题 | 证据 | 影响/建议 | -|------|------|------|-----------| -| N-14 | 写回缓存 `s_cache_buf/s_cache_dirty` 无掉电保护策略:`CTRL_SYNC` 已实现但**没有空闲时强制 flush** | `nand_ftl.c:418` | 掉电窗口 = 最后一次未跨页的写。已改善:整页写立即 flush、换页时 flush、`CTRL_SYNC` 走 flush + `dhara_map_sync`;仍无空闲定时 flush → 建议 defaultTask 空闲 N ms flush | -| N-15 | 页缓冲重复:`nand_ftl` 3×2,048(`s_page_buf/s_cache_buf/s_copy_buf`) | 对象 ZI:nand_ftl 6,216 | 与 `s_copy_buf` 复用可省 2 KB(§5.3 R-3)。🟡 `gd5f` 的 `s_bbt_page_buf` 已由 2,048 B 收紧为 272 B | -| — | `disk_ioctl GET_BLOCK_SIZE = 1` | `nand_ftl.c:442-444` | FatFs 的擦除块提示失效(FAT32 对齐优化拿不到信息);建议返回 64 页(=1 物理块) | - -### 4.4 配置引擎(`App/cfg/cfg_file.c`,1,343 行) - -**设计良好**:描述符 + 段注册表、外科式(surgical)保存、模板生成、2 s 轮询(`cfg_file_poll`)响应 `SYS_EVT_CFG_SAVE`,属主单任务化。这是全项目设计最完整的一个模块。销毁用户文件的路径(超长/损坏)已补自检留痕(`cfg load oversized` / `cfg load read fail` → `SYSLOG_SELFTEST`),现场无调试器时可追溯。 - -**问题**: - -| 编号 | 问题 | 建议 | -|------|------|------| -| — | 读失败仍静默回落工厂默认(`:1071-1102`),语义合理(不阻塞组网),但**用户改了 IP 重启就丢,串口之外无提示** | 装置信息页给出"当前为出厂配置"标识(自检记录已有,UI 侧未接) | - -### 4.5 日志子系统(`App/log/sys_log_mcu.c`) - -**设计良好**:定长 64 B 记录(`SYSLOG_REC_SIZE`)、单写者(defaultTask)、RAM 队列 + `syslog_drain`、容量满时删除重建、`/log` 目录惰性创建(`mkdir` 失败保持未就绪 + 幂等重试 + 上报);`/log/selftest.log` + 开机自检锚点 + Fault 现场 `.noinit` 跨复位 → 现场故障从"不可知"变成"可回读"。控制台侧另有**单写者队列**(`App/log/console_log.c`,对象 ZI 4,120 B)把"多任务直接 `printf` 到 USART1"收敛为一条流;代价已量化进 AGENTS.md(**每条日志约 90 B Flash + 216 B 栈**,深调用链模块另以 `#define DBG_LINE_BUF_MAX 128U` 收紧)。 - -**问题**: - -| 编号 | 问题 | 证据 | 建议 | -|------|------|------|------| -| P2-10 | 容量轮转 `f_unlink` 与 UI 读并发 | | 轮转只在写者、读者持版本号;卷锁覆盖面评估 → 登记簿 S-5 | +64 B 定长记录、双文件轮转、`[ERR]` 与 Fault 现场同通道。成本模型未变:**每条 `DBG_*` 约 90 B Flash + +216 B 栈**(调用点内联行缓冲),所以"补日志"要按调用点计费(§5.2)。 ### 4.6 UI 子系统 -见 §6 专题(同时是"标准化"章节)。此处仅记设计层结论:三层拆分(数据 `ui_menu_tab` / 视图 `ui_menu_view` / 控制器 `ui_menu`)+ 内容页描述符框架**方向正确**,但**框架未收敛到"新页面零框架改动"**(`menu_content_page_for()` 仍是手写 `case`,`ui_menu.c:118-128`),且 4 份 3,200 B 帧副本占 12.8 KB RAM(§5)。✅ 验证侧已到位:golden 回归已建(§6.1),UI 改动从此有逐像素/逐字节基线可依赖。 +今天的真实形态:`ui_tokens.h`(69 行,L0 令牌层)+ `ui_page_registry.c`(描述符注册表,取代手写 `case`) ++ 7 个内容页(`ui_page_ac/dc/device_info/runlog/selftest/net_cfg/password`)+ 组件仅 `ui_w_records`。 +回归面已进门禁:`ui-golden`(`scripts/check_ui_golden.py`,菜单 8 状态 + 内容页黄金参考)与 +`font-charset`(字符集差集)。**新增一页的实际成本**:轻页 40~71 行(ac/dc/device_info/runlog/selftest), +重页 275~370 行(password/net_cfg 含表单交互)——旧报告"新增一页 ≤30 行"的验收目标**未达**,但接线 +处数已从三处降到"页面文件 + 注册表一行"。 ### 4.7 外设驱动 -| 模块 | 评价 | 未闭环问题 | -|------|------|-----------| -| `sd2506`(RTC) | I2C 超时齐备,降级策略明确(RTC 故障→默认时基 + 自检上报),WRTC 解锁顺序按手册、负温度符号扩展已处理 | — | -| `tpafe5160`(并行 ADC) | BUSY 超时、EXTI 读取路径齐备;取数已收口为 `tpafe5160_snapshot()` 原子快照(帧序号只增不清 + FRSTDATA 自检) | ⚠ **V0.1 硬件缺陷(登记簿 N-18,V0.2 改版项)**:8 路电气量被分给两片转换器 → 主板侧**禁止**跨转换器做相位/功率/功率因数运算;并行总线无批量采样框架,16 位端口读依赖 `gpio.c` 初值 | -| `rs485` | 结构简单 | DE 时序用 `HAL_Delay(1)` **阻塞 1 ms**(在调用任务里,`rs485.c`);无帧校验、无接收环(`crc.c`/`ringbuf.c` 已删除 → **需从零重做**);`rs485_task.c` 仍未注册,消费者被 E-50(包撕裂)/E-49(TX 超时拉 DE)挡住(随 B2 暂缓) | -| `ch395f` | 事务包裹 `begin/end`、SPI 分频 8 为 PCB 约束(已文档化,勿动) | 静态 DMA 缓冲 2×2,052 B(obj ZI 4,162),必须留 SRAM1 | - -**驱动共性缺口**:没有统一的"错误码 + 超时 + 重试 + 自检上报"约定,各模块自定返回码(`GD5F_OK` / `0` / `bool` 混用)。**建议(S-5)**:定义 `bsp_err_t` 统一错误码 + `bsp_retry()` 辅助 + 所有失败必须走自检记录,作为驱动编写规范写入 `docs/代码规范/`。 +`ch395f.c` 1,777 行(SPI 分频必须 8,PCB 约束);TPAFE 走临界区原子快照(旧 `get_buf/data_ready` 已删); +片内 ADC1 上电自校准手写;`rs485.c` 仍有 3 处 `HAL_Delay`(E-49/E-50)→ E-59 门禁仍在挡消费者接线。 ### 4.8 测试体系 -**设计良好**:`test/board/test_config.h` 的 `TEST_SUITE_*` 互斥(`#error` 保护)、CH395F 硬件层/NET 层 Phase 互斥、`@test` 一键闭环(烧录→wait-idle→标记校验)、模拟器 `--cfgtest/--logtest`、主机侧单测 16 个钩子(含 `sha256` / `ftp_auth` / `vol_lock` / 采集算法族 / **`nand_port` dhara 移植层**)。 +- 主机侧 **16 套**单测(提交门禁实跑);板级 `test/board/` 19 文件、套件开关互斥受 `test-suite-switch` 管 +- TC 双向追溯:代码侧 593 号 / 文档侧 271 号 / 交集 265,差异 13 处(当前仅告警,未升门禁) +- **两处空白**:① 无覆盖率口径;② `check_coding_standard.py` 没有 `malloc(` 正则——"自研禁用 C 库堆" + 这条铁律目前只靠人读 + 模拟器照不出来(MicroLIB C 堆仅 512 B) +- 破坏性套件(`ENABLE_DESTRUCTIVE_TESTS`)有**持久代价**(NAND 坏块记账不可逆),默认关闭是正确设计 -> ⚠ 仍缺:**覆盖率概念**、**主机侧对 `dhara` 移植层的测试**(N-12/N-13 这类缺陷至今只能靠人工读代码发现)。 +## 5. 内存与 Flash(2026-10-22 实测收口) -**问题**: +### 5.1 实测基线 -| 编号 | 问题 | 建议 | -|------|------|------| -| P1-10 | CH395F 硬件测试套件静态 RAM 预算(`L6406E` 历史:HW 套 +8.7 KB、NET 层固件 +28 KB) | 基线已重建、测试缓冲已瘦身、RAM 侧因堆迁 CCM 大幅缓解;**剩"套件构建实测链接确认 + `@test` 闭环"两步**(登记簿唯一 P 类收尾项,W5) | +| 指标 | 2026-10-22 | 2026-10-15 | 说明 | +|---|---|---|---| +| `Code` | 213,188 B | 213,520 B | F-6 收口 −332 B | +| `ER_IROM1`(Code+RO) | **231,768 B / 22.1%** | 232,280 B / 22.2% | 余 ≈804 KB | +| `RW_IRAM1` | **75,264 B / 57.4%**(余 55,808) | 73,568 B / 56.1% | 增量来自 B5/B8 功能码 | +| `RW_CCM` | **57,376 B / 87.9%**(余 7,904) | 57,376 B / 87.9% | **当前唯一红线** | +| FreeRTOS 堆 | 56 KB(57,344 B)@CCM;实测 `free=15,800` / `min_ever=8,464` | 同 | 2026-10-22 真机 | +| 任务栈 | **9/9 PASS**(最深 defaultTask 静态 3,024/5,120,余 40%) | 8/8 PASS | `data/stack_report_20261022.md` | +| LVGL 池 | 16 KB;实测峰值 6,568 B(40%)、菜单基线 4,784 B | 同 | 2026-10-15 由 40 KB 收缩 | ---- +### 5.2 结构性诊断(三条,仍然成立) -## 5. 内存优化(量化) +1. **静态大头是"缓冲常驻"**:`lv_mem_core_builtin.o` 16,384 + `lftpd.o` 7,724 + `ui_net_sink.o` 6,746 + + `ui_port.o` 6,408 + `nand_ftl.o` 6,200 = 43,462 B,占 `RW_IRAM1` 的 58%(今日 Top-5 实测)。 +2. **动态面只有一个**:所有任务栈 + TCB + 队列都在 `heap_4` 里,堆在 CCM → **栈的余量问题就是 CCM 的 + 余量问题**,这就是红线迁到 CCM 的机制原因。 +3. **栈深的主因是日志与配置写链**,不是算法本体:`s_append` 每调用点 216 B 栈 + 配置原子写链 + (`cfg_store_write_atomic → f_rename`)把 defaultTask 推到 3,024 B。 -### 5.1 实测基线(2026-10-15 `.map`,R-9 落地后) +### 5.3 RAM 优化路线(收益 / 风险 / 优先级) -| 指标 | 数值 | 上限 | 占用 | -|------|------|------|------| -| `Code + RO-data`(`ER_IROM1`) | 232,280 B | 1 MB Flash | **22.2%** | -| `ROM`(`Code + RO + RW` 初始化表) | 233,416 B | 1 MB | 22.3% | -| **SRAM1(0x20000000,128 KB)** `RW_IRAM1` | **75,232 B** | 131,072 B | **57.4%(余 55,840 B)** | -| **CCM(0x10000000,64 KB)** `RW_CCM` | **57,376 B** | 65,280 B | **87.9%(余 7,904 B)** | -| `UNINIT_CCM`(`.noinit` 崩溃现场) | 48 B | 256 B | 18.8% | +| 项 | 原收益 | 今天判定 | +|---|---|---| +| **R-9** LVGL 池 40→16 KB | 24,576 B | ✅ 已落地 2026-10-15(SRAM1 74.9%→56.1%),不回退 | +| R-2 后半 待迁缓冲进 CCM | ≈29.6 KB | ⏸ **暂缓**——CCM 只剩 7,904 B,搬不动也无收益 | +| R-3 页缓冲合并 / R-4 UI 帧副本 / R-6 栈静态化 | 中 | ⏸ 暂缓(N-27) | +| R-5 提高 `LV_MEM_SIZE` 上限换性能 | — | ❌ 已判定不可行(单色 160×160 用不到) | -> **R-9 已落地(2026-10-15)**:`LV_MEM_SIZE` 40→16 KB → SRAM1 由 98,144 B / **74.9%** 降到 **73,568 B / 56.1%**(−24,576 B,与池缩减量逐字节吻合)。SRAM1 首次从"最紧的一块"变成**余 57.5 KB**(现 75,232 B / 57.4%、余 55,840 B,增量来自同日移植层守卫与日志缓冲 +1,664 B);**当前瓶颈换到 CCM(87.9%,只剩 7,904 B)**。⏸ 该簇后续动作(缩堆 → 迁 CCM)已按用户裁定 2026-10-15 **整簇暂缓**,见 §5.3。 - -> **权威源**:区域水位与子系统配额 → `../内存管理/RAM预算台账.md`(受管区);趋势 → `scripts/ram_baseline.tsv`(`ram_baseline.py --trend` 高亮 RW+ZI 增长 >1 KB 的对象)。本节只列支撑结论所需的数。 - -**RW+ZI 主要对象(`ram_baseline.tsv` 末行 Top-10)** - -| 对象 / 符号 | 字节 | 所在区域 | 归属 | DMA 可达性 | -|-------------|------|---------|------|-----------| -| `ucHeap`(`heap_4.o`) | 57,376 | **CCM**(`0x10000020`) | FreeRTOS 堆(`configTOTAL_HEAP_SIZE` 56 KB + 元数据) | **DMA 不可达**——规则 R3「堆/栈禁为 DMA 源/目的」已写进 `../内存管理/内存内容分配策略.md` §4,并由 `gd5f_dma_buf_ok()` 运行时强制 | -| `work_mem_int`(`lv_mem_core_builtin.o`,= `LV_MEM_SIZE 16*1024`,**R-9 后**) | 16,384 | **SRAM1** | LVGL 池(实测峰值 6,568 B = 40%) | 纯 CPU,**可迁 CCM(R-2 未完成项)** | -| `lftpd.o` | 7,884 | SRAM1 | FTP(含鉴权) | 任意 | -| `ui_net_sink.o`(`g_tx` + `g_lcdmem_data` + `g_rx`) | 6,766 | SRAM1 | UI 网络发送 | 可迁 CCM(CH395F 侧 memcpy 到自有 SRAM1 缓冲) | -| `ui_port.o`(`g_ui_fbuf` + `g_ui_draw_buf`) | 6,420 | SRAM1 | UI 帧/绘制缓冲 | 可迁 CCM | -| `nand_ftl.o`(3×2,048 页缓冲 + 控制块) | 6,216 | SRAM1 | 存储 | **必须 SRAM1**(规则 R2/R3) | -| `ch395f.o`(`s_spi2_dma_tx/rx[2052]`) | 4,162 | SRAM1 | 网络 | **必须 SRAM1** | -| `console_log.o`(行环形缓冲) | 4,120 | SRAM1 | 日志 | 任意(TX 走 DMA → 实际必须 SRAM1) | -| `default_task.o` | 2,736 | SRAM1 | 任务薄壳 | — | -| `sys_log_mcu.o` | 1,924 | SRAM1 | 日志 | 任意 | - -**两个静态池(57,376 + 16,384 = 73,760 B)仍是 RAM 问题的核心**,且**分居两区**:堆在 CCM(占 CCM 的 87.9%)、LVGL 池在 SRAM1(R-9 后只占 SRAM1 的 22%)。→ 下一步收益点仍是**把 LVGL 池 + UI 两份缓冲搬进 CCM**,但体量已由 54,146 B 降到 **29,570 B**(16,384 + 6,420 + 6,766),而 CCM 只剩 7,904 B → **R-9 已做完,还差"缩堆"这一步**(56→32 KB 可腾 24 KB → CCM 余 ≈31.9 KB ≥ 29,570 B;解锁链 → `../内存管理/RAM预算台账.md` §3)。⚠ 迁移前必须过 DMA 可达性清单:区域放置与"堆/栈禁为 DMA 源/目的"的规则族(R1~R4)以 `../内存管理/内存内容分配策略.md` §4 为准,本仓另有两条实证同族约束(ADC1 DMA 目的缓冲必须在 `RW_IRAM1`;CCM 上做 DMA 静默不落地)。 - -**Flash 侧(Code+RO,按 `.map` 组件表对象名归类)** - -| 子系统 | Code+RO | 占镜像 | -|--------|---------|--------| -| LVGL(核心/绘制/字体/缓存/图像) | 87,706 | **38.0%** | -| 网络(NET + CH395F + NETCFG + lftpd) | 27,561 | 11.9% | -| 存储(FatFs + dhara + NAND + 卷锁) | 19,265 | 8.3% | -| 手写 UI 页面/框架(`ui_*.o`) | 18,507 | 8.0% | -| FreeRTOS + CMSIS-RTOS | 13,562 | 5.9% | -| HAL/CMSIS | 13,532 | 5.9% | -| 算法层 L1~L3(`adc_input`/`phasor`/`sample_ring`/`prot_*`) | 5,454 | 2.4% | -| 其余(`App/` 应用与驱动自研、startup、C 库、填充) | ≈45,497 | ≈19.7% | -| **合计** | **231,084** | 100% | - -> 归类按 `.map` 组件表对象名前缀,合计与区域总量可差几十字节(链接器填充)。前 7 行逐字节与 2026-10-14 那次构建相同;并行未提交改动(`cfg_file` / `sd2506`)的 +48 B 落在"其余"。⚠ **本表是那次构建的构成快照**(合计 231,084 B);当前镜像 **231,768 B / 22.1%**(2026-10-22 构建;快照之后的净变化含 §5.4 F-6 的 −332 B),同期增量 +1,192 B `Code` 来自同日 dhara 移植层守卫与日志批量化(登记簿原 N-21 / N-24),**不改变"LVGL 是最大单一子系统"的结论**。 - -**要点**:LVGL 的绝对量自 F-1/F-2/F-3 裁剪后**未再增长**(87,706 B),仍是镜像最大单一子系统;但**剩余部分已被结构性锁死**(§5.4 F-4 判定不做)→ LVGL 体积自此不再是可优化项。 - -### 5.2 结构性诊断(三条) - -1. **瓶颈已从 SRAM1 换到 CCM**:R-9 落地后 SRAM1 只剩 56.1%(**现 57.4%、余 55,840 B**),而 **CCM 被堆占掉 87.9%(只剩 7,904 B)**,待迁的 LVGL 池 + UI 两份缓冲 = **29,570 B**。 - → R-2 的剩余部分**只差"缩堆"这一步**(56→32 KB,`../内存管理/RAM使用分析与优化方案.md` §4.2.2(阶段 2 第 2 项"缩堆");⚠ 2026-10-12 B5 是反向扩容,缩回前必须重跑 `stack_report.py` + 上板 `min_ever` 复测);顺序不可颠倒(`../内存管理/RAM预算台账.md` §3)。 - ⏸ **本簇已按用户裁定 2026-10-15 整簇暂缓**(§5.3)——这条诊断保留作将来重开时的现状基线,**不代表现在要动**。 -2. **任务栈全部动态取自 FreeRTOS 堆**:`osThreadAttr_t` 只设 `stack_size` 不设 `stack_mem`(`Src/freertos.c`)→ 8 条产品任务栈合计 **9,792 words = 39,168 B**(另 testTask 2,048 words 仅测试构建)连同 idle/timer 栈、TCB、IPC 全部从堆里扣,**留给动态分配的实际余量很紧**(堆 56 KB 是 2026-10-12 B5 扩容后的值)。 - → **R-6(栈静态化)是把"池内碎片"变"可核算静态区"的正解,前置条件(观测常开 + `stack_report.py` 每任务最坏链)已满足。** 实时水位以串口 `[STACK]` / 自检打印为准,不在本文固化快照。 -3. **MicroLIB 512 B C 堆是隐形地雷**:任何新代码写一个 `malloc` 都会中招。 - → `Heap_Size EQU 0x200` 未变、`.map` 仍 `Removing startup_stm32f407xx.o(HEAP), (512 bytes)`;铁律已进 AGENTS.md(只用 `pvPortMalloc/vPortFree` 或收进平台后端),但**没有机械化检查**——`check_coding_standard.py` 未含 `malloc(` 正则 → §5.5 第 5 条未落地。 - -### 5.3 RAM 优化路线(收益/风险/优先级) - -> ⏸ **本表除已落地的 R-9 外整簇暂缓(用户裁定 2026-10-15)**,理由三条: -> ① **没有功能在等这块预算**——SRAM1 已降到 57.4%(余 55,840 B)、Flash 22.2%,离 `mem-budget` 的 92% 红线很远; -> ② **搬迁本身有风险和工时**——R-2 要动 scatter 并逐条过 DMA 可达性清单(规则 R2/R3,违例是**静默传错**不是报错),R-4 要重排 sink 与 LVGL 刷新的生命周期,R-6 静态化后栈溢出更难发现,缩堆还得上板复测 `min_ever`; -> ③ **收益与代价不对称**——腾出的 CCM 是给"以后可能有的功能",代价是现在动存储与显示两条主链。 -> -> **保留常开的是治理面**(不是优化动作):`mem-budget` 92% 门禁、`../内存管理/RAM预算台账.md` 受管区、`scripts/ram_baseline.tsv` 趋势、`[STACK]` 水位与 `stack_report.py` / `ram_report.py`。它们是传感器,会在真正吃紧时报警。 -> -> **重开条件(任一成立即重新评估,不必现在预防)**: -> ① `mem-budget` 逼近 92%,或某项**已定功能**的增量装不进现有余量——最可能先撞的是 **CCM**:只剩 **7,904 B**,而 FreeRTOS 堆实测 `min_ever` 仅 8,464 B(2026-10-15 台架),**加任务 / 抬栈 / 加堆都会先在这里撞墙**; -> ② 出现必须把 UI 帧缓冲搬离 SRAM1 才能做的显示功能;③ 送检或现场暴露 RAM 相关的稳定性问题。 - -| 编号 | 措施 | 收益 | 风险 | 优先级 | 状态 | -|------|------|------|------|--------|------| -| **R-2** | **启用 CCM 承载 LVGL 池与 UI 缓冲**:对 `work_mem_int` 与 UI 帧/绘制缓冲加段属性(`.sct` 已有 `RW_CCM`) | **−29,570 B**(R-9 后的待迁体量:16,384 + 6,420 + 6,766) | 中:必须确保这些缓冲**不被任何 DMA 直接寻址**。已核实 UI 发送路径 memcpy 进 CH395F 自有 SRAM1 DMA 缓冲,LVGL 为纯 CPU;**NAND 路径(规则 R2/R3)绝对不可迁入** | ~~P1(W3)~~ → **⏸ 暂缓** | ⏸ **暂缓(2026-10-15 裁定)**。现状仍是**只做一半**:`RW_CCM` 只收 `heap_4.o`(57,376 B)→ SRAM1 已减 49 KB+;剩余一步是缩堆 56→32 KB(CCM 只剩 7,904 B,29.5 KB 装不下) | -| **R-3** | 存储页缓冲合并:`nand_ftl` 三块 2 KB 中 `s_page_buf`(dhara scratch) 与 `s_copy_buf` 不可并发,可复用 | **−2 KB** | 中:需严格审查 dhara 调用序列(copy 期间不会用 map scratch) | ~~P2~~ → **⏸ 暂缓** | ⏸ **暂缓(2026-10-15 裁定)**;未办(`s_bbt_page_buf` 已单独收紧为 272 B) | -| **R-4** | UI 帧副本去重:同一 3,200 B 帧当前存 **4 份**(`g_ui_draw_buf` → `g_ui_fbuf` → `g_lcdmem_data` → `g_tx`)。让 `ui_flush_cb` 直接把 `color_p` 交给 sink、sink 发送时才拼帧头 | **−6.4 ~ −9.6 KB** | 中:需要 sink 与 LVGL 刷新生命周期对齐(同任务,可控) | ~~P1~~ → **⏸ 暂缓** | ⏸ **暂缓(2026-10-15 裁定)**;未办(`ui_port.o` 6,420 + `ui_net_sink.o` 6,766 = 13,186 B,副本数未变) | -| **R-6** | 任务栈改**静态分配**(`stack_mem` 指向各自 `.bss` 数组),从堆里释放 ≈39 KB | 堆可用 +39 KB(**总量不变**,但把"池内碎片"变成"可核算静态区") | 低-中:静态后溢出检测更难发现;先测水位再定尺寸 | ~~P2~~ → **⏸ 暂缓** | ⏸ **暂缓(2026-10-15 裁定)**。若将来重开,前置已满足:观测三面常开 + `stack_report.py` 已给出每任务最坏链 → 可直接按实测值定尺寸 | - -> ✅ **R-9 已落地并销项(2026-10-15)**:`LV_MEM_SIZE` 40→16 KB(两份 `lv_conf.h` 同步,`lv-conf-sync` 生效值比对 163 项 PASS)。依据 = 池水位门禁实测(菜单基线 5,368 B / 开页-关页往返 5,448 B / 最重页面峰值 6,568 B = 池的 40%),**≈2.5× 余量**;PC 侧 11 页黄金参考逐字节比对 10/11 一致(唯一差异是 `netcfg_save` 保存态文本,A/B 复测证明与本次改动无关——真因是 `--cfgtest` 留在工作目录的 4000 B 伪造配置档触发了引擎"拒写超长文档"这条**正确**行为,同日已修,见 `../UI设计/UI_自动化测试流程.md` §五)、往返池水位 5,448 B **分毫未变**;`@build` 0 错 0 警,ZI 精确 −24,576 B。**真机复跑 ✅(2026-10-15 `@test` PASS)**:`menu build done: total=15020 free=11828 used=3192`——菜单根框只占建池后 15,020 B 的 **21%**(TLSF 元数据 ≈1,364 B),全程无 `[ERR]`/Fault。再往下试 8 KB 需先跑全量 UI 回归(PC + 真机),**不在本条范围**。 - -> ⚠️ **R-2 与 R-4 不可叠加计算**:R-4 省的是"副本份数",R-2 已把这四份缓冲整体搬到 CCM,因此 **R-4 在 R-2 之后省的是 CCM 而非 SRAM1**(CCM 侧 13,186 B → ≈6.8 KB,为后续 UI 扩展腾空间)。若只做 R-4 不做 R-2,则直接为 SRAM1 省 6.4 KB——两条路线对 RAM 总量等价,**但顺序决定收益落在哪个区域**。 -> -> **R-5 已判定不可行(原建议 `configMAX_PRIORITIES` 56→32 省 480 B)**:CMSIS-RTOS2 包装层 `freertos_os2.h:293` 有 `#if (configMAX_PRIORITIES != 56) #error`——改小直接编译失败。为 480 B 去改第三方包装层不划算,**本条关闭**(登记簿 N-27 的评估清单已据此剔除 R-5)。 - -与 `../内存管理/RAM使用分析与优化方案.md` 的关系:本表**沿用其阶段划分**(阶段 0 测量 / 阶段 1.1 池收缩 / 阶段 1.2 CCM / 阶段 2 堆收缩),本报告新增的是 ①R-4 帧副本去重(原文档未识别 4 份副本)②R-3 存储侧缓冲合并 ③**DMA 可达性硬边界**(原报告 N-11 提出,现已沉淀为 `../内存管理/内存内容分配策略.md` §4 规则 R2/R3,属实施风险关键点)。 +> **重开条件(满足任一条才谈搬迁/缩堆)**:① 出现"必须新增 >8 KB RAM"的功能且产品要它;② `min_ever` +> 跌破 4 KB(碎片或峰值失控,此时应先削峰值而非搬家);③ CCM 撞墙导致新任务建不起来。 +> 搬迁要动 scatter、逐条过 DMA 可达性清单(违例是**静默传错**不是报错),缩堆还得上板复测——收益不抵风险。 ### 5.4 Flash 优化(2026-10-22 收口:F-6 已落地、F-4 判定不做、F-5 降级观察项) -> **F-1 / F-2 / F-3 已落地并销项(2026-10-11)**:两份 `lv_conf.h` 对齐为同一特性子集 → **实测省 64,576 B**,Flash 26.3% → 20.1%(现 22.2%,增量来自 B4/B5 功能代码)。完整记录见 `../内存管理/RAM使用分析与优化方案.md` §2.6.4 与 `../UI设计/UI_设计架构.md` §1.4 第 10 条。 +> 动手前逐条对 `.map` 与源码复核,结论:**旧报告三项里两项建议不成立、一处归因错误**(字节数全对)。 -| 编号 | 措施(原建议) | 原估收益 | 实测复核(2026-10-22 逐项对 `.map`,基线 Code 213,520 / RO 18,580) | 状态与结论 | -|------|------|-----------|------|------| -| **F-4** | 关闭未用的绘制子模块与 `LV_USE_SNAPSHOT`(MCU 端)/`LV_USE_OBJ_PROPERTY`/`LV_USE_FLOAT` 等 | 原估 ≈10 KB | **五项字节数逐字节核对仍准确**(3,246 + 2,960 + 1,972 + 952 + 930 = 10,060 B),**但没有一项有开关**:根因在 `Lib/lvgl/src/draw/sw/lv_draw_sw.c` 的 `execute_drawing()` 用**无条件 switch** 引用全部 task 类型(只有 VECTOR 带 `#if`)→ 链接器 `--remove` 无从下手。上游唯一总开关 `LV_DRAW_SW_COMPLEX`(关它可连带去掉 `mask` 3,862 与 shadow、arc、mask_rect、triangle、圆角/渐变/粗斜线分支,≈9.5–11 KB)**在本工程不可关闭**:主界面顶部"开口浅 V"两条 3 px 粗斜腿由 `Drivers/BSP/UI/ui_deco.c` 用 `lv_draw_line()` 绘制,斜段只在复杂渲染器下存在,**2026-09-03 已实测失败并把两端 =1 固化** → `../UI设计/UI_设计架构.md` §1.4 第 7 条、`../UI设计/UI_界面制作流程.md` 陷阱表。⚠ **原"mask / grad 是文字与矩形绘制必经路径"的理由同样错**:字形走 `lv_draw_sw_letter.c` 的 `blend_dsc.mask_buf`(blend 层的字形位图),不经 `lv_draw_sw_mask_*`;关不掉的真实理由是**斜线**、不是文字 | ❌ **判定不做**。剩余唯一路线 = 给 `Lib/lvgl` 打本地补丁给分发分支逐个加门控(阴影、弧、mask_rect、三角 + 图片链,理论上限 ≈16 KB),代价是第三方补丁登记 + 每次升级重放 + 逐分支像素回归;而 Flash 只占 22.1%(余 ≈800 KB)、产品需求无 OTA/双镜像预算 → 收益不抵代价 | -| **F-5** | 从 uvprojx 中**移除未使用的 LVGL 控件源文件**(201 个 → 实际需要的子集) | 原称编译时长 −40% | uvprojx 实测 **326 个 `.c`,其中 LVGL 201 个**;`.map` 里 LVGL 有产出 74 个、**零产出 127 个**(空 TU 样例:`lv_draw_3d`、`lv_draw_vector`、`lv_draw_arc`、`lv_obj_property`、`lv_draw_sw_blend_to_rgb565` 与 `_al88`、`_l8`)。但增量构建实测 **4~5 s**(`MDK-ARM/build_log.txt` 的 `Build Time Elapsed`),空 TU 只在全量重建或改 `lv_conf.h` 时才参与重编;**远程 CI 刻意不跑 `@build`**(需 Keil 授权)→ 对 CI 时长零收益。**"−40%"在仓库里没有任何基线记录**,不予复述 | 🟡 **降级为观察项**(非产品指标)。真要做:用 Keil 原生 `0`(保留清单、可逆、diff 小)而非删条目,且**必须排在任何 `lv_conf.h` 改动之后**(空 TU 集合随裁剪变化);动手前先补一次全量构建计时做基线 | -| **F-6** | 双精度软浮点:`dadd`/`dmul`/`ddiv`/`depilogue`/`dfixul`(`mf_w.l`),原称"来自 `printf` 的 `%f`/double 提升" | 原估 −1 KB + 运行期 | **原建议的成因判断不成立**:全库 **0 处 `%f`/`%g`/`%e`**(扫 `App/`、`Src/`、`Inc/`、`Drivers/BSP/` 共 173 个 `.c/.h`;`App/util/task_prof.c` 与 `App/task/adc_task.c` 的注释早已改用定点整数打印)→ "把 `%f` 改整数刻度"没有可执行对象。剩下的 `dadd`+`dmul`+`ddiv`+`depilogue`+`dfixul` = **1,018 B** 由 `mc_w.l(printfa.o)` 的 `_printf_core → _fp_digits` **无条件引用**(`.map` 交叉引用可查):只要还用 MicroLIB `snprintf` 就删不掉。自研真正的双精度只有 `App/algo/prot_set.h` 两个 Q10 宏里的显式 `(double)` | ✅ **已落地(2026-10-22)**:宏改单精度(去 `(double)`、`1024.0` → `1024.0f`)→ `@build` 0 错 0 警、Code 213,520 → **213,188(−332 B)**;`.map` 里 `dflti`、`d2f`、`f2d`、`dfixi`、`fepilogue` 共 300 B 消失,自研侧不再引用任何双精度 helper。**数值逐位等价**(×/÷1024 是 2^10 指数平移;宏内 42 个字面量 + `int32` 全域往返穷举零差异);`prot_set` 90/90、`prot_elm` 47/47、`iec104` 97/97 主机单测全过;真机 `@test` PASS(2026-10-22,无 `[ERR]`/Fault)。运行期收益:保护判据每 20 ms 每元件 3 次定值读取由软件双精度改为 F407 单精度 FPU | +| 项 | 原估收益 | 实测复核 | 状态与结论 | +|---|---|---|---| +| **F-4** 关未用绘制子模块 | ≈10 KB | 五项字节数逐字节仍准(10,060 B),但**无一项有开关**:`lv_draw_sw.c` 的 `execute_drawing()` 无条件 switch 引用全部 task 类型;唯一总开关 `LV_DRAW_SW_COMPLEX` 被主界面"浅 V"3 px 粗斜腿依赖(2026-09-03 已实测失败并两端固化 =1)。原"mask/grad 是文字必经路径"证伪:字形走 blend 层 `mask_buf` | ❌ **判定不做**(要打补丁才谈得上,上限 ≈16 KB 换 1.6% Flash,不值) | +| **F-5** 清 uvprojx 空条目 | 编译时长 −40% | 201 个 LVGL `.c` / 127 个空 TU;增量构建 4~5 s,空 TU 只在全量重建或改 `lv_conf.h` 时重编,**远程 CI 刻意不跑 `@build`**;−40% 无基线记录 | 🟡 **降级观察项**;真要做用 Keil 原生 `0`(可逆),且排在任何 `lv_conf.h` 改动之后 | +| **F-6** 双精度软浮点 | ≈1 KB | **归因错**:全库 0 处 `%f`;剩余 `dadd/dmul/ddiv/depilogue/dfixul` 1,018 B 锁在 MicroLIB `printfa.o` 的 `_printf_core → _fp_digits`,只要用 `snprintf` 就删不掉。但**根因侧确有可修点**:`App/algo/prot_set.h` 两个 Q10 宏写成 `double` | ✅ **已落地 2026-10-22**:改单精度 → `@build` −332 B、5 个 helper(300 B)消失;数值逐位等价(穷举 42 个字面量 + int32 全域 ÷1024 往返 4.49×10⁶ 例零差异);真机 `@test` PASS | -**Flash 侧剩余可回收项 = 0**:F-4 需给 `Lib/lvgl` 打本地补丁(已判定不做),F-6 已收口,F-5 省的是编译时长不是 Flash。 +**当前可回收 Flash = 0 B**(F-4/F-6 已定论,图片链同理不打补丁)。LVGL 仍占镜像 **37.8%** +(87,706 / 231,768 B)——这是"用 LVGL 画单色菜单"的结构性代价,F-1/F-2/F-3 已省掉 64,576 B。 -### 5.5 内存治理长效机制 +### 5.5 内存治理长效机制(常开,不受暂缓影响) -> **已闭环(2026-10-12,登记簿 N-27 前半 / W1-0)**:① 超阈值即失败的 **`mem-budget` 钩子**(`ram_baseline.py --gate`,**92% 红线** + Top-10 RW+ZI 附表,无 `.map` 自适应跳过)② 趋势基线 **`scripts/ram_baseline.tsv`**(`--update` / `--trend`)③ **`../内存管理/RAM预算台账.md`**(子系统配额 + CCM 分配台账 + 超配额流程)。 -> **阈值与配额的权威值现在只在那份台账里**(本报告不再重复数值);`mem-numbers` 钩子继续防"文档数字漂移"。 -> -> **已闭环(2026-10-11,登记簿 N-26)**:**DMA 可达性清单与规则族**——`../内存管理/内存内容分配策略.md` §3「全板 DMA 使用方清点」+ §4 规则 R1~R4(含 **R3 堆/栈禁为 DMA 源/目的**),落地为编译期窗口断言 + 启动期自检 + 运行时守卫三层(见 §4.3)。原"独立清单文档 + `_Static_assert`"的建议形态**已被这份更好的实现取代**。 +1. 每次 `@build` 后 `scripts/ram_baseline.py --update`,再 `--write` 回填台账受管区;`--gate` 判 92% +2. `check_mem_numbers.py --scan/--write`:文档里的内存数字一律由 `.map` 反查,禁止手填 +3. 改栈 / 加大局部变量前后各跑一次 `stack_report.py`(静态 × 真机 `[STACK]`) +4. 子系统配额与 CCM 台账 → `../内存管理/RAM预算台账.md`;趋势 → `scripts/ram_baseline.tsv` +5. **仍未落地的两条补强**:`check_coding_standard.py` 加 `malloc(` 正则(把 C 库堆禁令变成机器拦截)、 + 覆盖率口径(现在只有"跑过哪些 TC",没有"覆盖到哪些分支") -仍开放一条: +## 6. UI 界面标准化:从方案到已落地 -5. **禁止新增 C 库 `malloc`**:写进 `docs/代码规范/`,并在 `check_coding_standard.py` 加正则检查(`App/`、`Drivers/` 内出现 `malloc(` 即告警)。 - → **🟡 前半已做、后半未做**:AGENTS.md 已有铁律(含模拟器照不出来的原因);❌ `check_coding_standard.py` **未含 `malloc(` 正则** → 目前只靠人评审,属机械检查缺口。 +### 6.1 现状(U1~U5 复核) ---- +| 项 | 旧状态(2026-10-15) | 今天 | 证据 | +|---|---|---|---| +| U1 令牌层 | ❌ 未建 | ✅ 已落地 | `Drivers/BSP/UI/ui_tokens.h`(69 行,L0) | +| U2 组件库 | ❌ 未建 | 🟡 仅 1 个组件 | `ui_w_records.c/h`;其余仍是页内自绘 | +| U3 页注册表 | ❌ 手写 `case` | ✅ 已落地 | `ui_page_registry.c`(`content_page_def_t` 描述符)+ 7 页 | +| U4 回归门禁 | 🟡 未挂 pre-commit | ✅ 已挂 | `ui-golden`(菜单 8 状态 + 内容页)、`font-charset` | +| U5 页面扩展 | 27 页未落地 | 🟡 按需求推进 | 现 7 页(含网络配置与改密两重页) | -## 6. UI 界面标准化实现方案 +### 6.2 新增一个页面要走多少步(今天实测) -> ⚠ **本节"建议 / 目标结构 / 草图"不可照抄——落地前必须逐条对代码核实**(2026-10-15 立此口径)。 -> -> 本节是**体系评审视角的建议**,不是实现规格。启动 W6 后按 §6 实际执行 U1 / U3 / U4 三包,**四次证伪**: -> -> | # | 报告写的 | 代码实况 | 照抄的后果 | -> |---|---|---|---| -> | 1 | §6.3 L0 清单要新建 `UI_SCREEN_W/H` | `ui_port.h:21-22` **早有同名宏** | 同名宏二次定义 | -> | 2 | `UI_COL_FG = LV_COLOR_BLACK` / `UI_COL_BG = LV_COLOR_WHITE` | LVGL 9.3 **无这两个宏**(只有 `lv_color_black()/white()` 函数);且**极性反了**——代码里所有墨色恒用 `lv_color_white()`,选中行才是黑字 | 先编译失败,改完若按报告极性则**整屏反色** | -> | 3 | §6.5 U1 行"27 页未落地" | 现存 `ui_page_*.c` 实测 **7 个**;27 是**目标页数**不是文件数 | 工作量与拆包按错数排 | -> | 4 | §6.3 生成器草图 `@page=deadzone` | **密码页不是节点绑定的**(任何 `#P` 叶子进同一页),注册表必须单独出口 | 做不出能进密码页的注册表 | -> -> 另有 §6.3 L0 清单 **9 条取值/存在性错误**(行距 13→实为 14、列表顶 15→实为 24、滚动条宽 2→实为 4、`UI_PAD_X 3` 不存在、`UI_ROW_H 12` 不存在、`UI_TITLE_H 11` 不存在、`UI_STYLE_FLAT` 在 U1 阶段做不了……)**逐条明细见 §6.3 更正块**,此处不重复。 -> -> **执行规则**:① 按本节动手前,先 grep 代码核一遍建议值与建议结构是否已存在、取值是否对;② 发现不符**一律以代码为准**,不要"先按报告做再改";③ 把差集**回报并记进本节**,不要静默修正(否则下一个人还会再踩);④ 本节"发现"栏(现状描述、问题定位、成本量化)经复核**仍然可信**,不可照抄的是**建议栏**。 -> -> **同一规律在本报告之外也复现过**:§4 的 N-13 建议用了本仓裁剪版 dhara 里**不存在**的 `DHARA_E_UNSUPPORTED`(照抄编译失败)。所以这不是 UI 章节的局部问题,而是**"建议"这一体裁的通病**——写建议时没有对代码核。后续任何按本报告排期的工作包,任务卡都应显式写一句"以代码实况为准,报告建议值不可直接采用"。 +四步:① 新建 `ui_page_.c`(轻页 40~71 行)② 在 `ui_page_registry.c` 加一条描述符 ③ 菜单表挂节点 +④ 跑 `ui-golden` 更新黄金参考 + `@build`。旧流程里的"改 `ui_task.c` 手写 case"已不存在。 +**未达项**:验收目标"≤30 行/页"——最省的 `ui_page_dc.c` 也要 40 行。 -### 6.1 现状(框架已有,但未"闭环") +### 6.3 已完成部分的记录去处 -| 层 | 现状 | 评价 | -|----|------|------| -| 内容页 | 描述符 `content_page_def_t` 仍是**唯一**页面描述符(刻意不新建第二套);节点→页面映射由**生成器产出** `ui_page_registry.c`,`{node, desc}` 表 + `ui_page_registry_for()` + `ui_page_registry_password()` | ✅(2026-10-15 U3)**手写 `menu_content_page_for()` 已删除**,仓库内仅存 2 处历史说明注释;密码页按代码实况**单独出口**(非节点绑定),不进 `node→desc` 映射 | -| 设计令牌 | `Drivers/BSP/UI/ui_tokens.h` **已建**(2026-10-15 U1,20 个宏),框架 + 7 页 + `App/task/ui_task.c` 全部接入;**屏幕几何复用 `ui_port.h` 既有 `UI_SCREEN_W/H`**,不重复定义 | ✅ 纯替换验收:`@build` 0/0 且 Program Size 逐字节不变、golden 逐像素一致、池水位未变。⚠ 落地时修正本报告 L0 清单 9 处取值/存在性错误(见 §6.3 更正块) | -| golden 回归 | `simulator/tests/` 四脚本:`check_menu_rows.py`(菜单 8 状态全像素)、`check_content_pages.py`(内容页黄金参考逐字节 + 文本断言 + **LVGL 池水位门禁**)、`regen_ref_png.py`、`check_remo_sink.py` | ✅ **已入自动门禁(2026-10-20)**:新钩子 `ui-golden` = 增量构建模拟器 + 两条 golden 串成一条,实测改一个 UI 源文件后合计 2~4 秒;有牙实测 = 注入 3 px 位移 → FAIL 并点名页面与差异像素数 | +原"8 人日方案"正文(四层架构设问、逐日排期、27 页清单)已随落地删除;现行做法与约束只在两处详述: +`../UI设计/UI_设计架构.md`(分层、令牌、双端同步、陷阱表)与 `../UI设计/UI_自动化测试流程.md`(回归与 +黄金参考);历史决策与验收证据见本文**变更记录**(2026-10-15 W6 三行)与 `../UI设计/菜单页面设计.md`。 -### 6.2 新增一个页面要走多少步(当前) +### 6.4 UI 侧的内存纪律(写进规范,勿再试探) -1. `ui_menu_tree.txt` 加节点 → 2. 重跑 `gen_menu.py` → 3. 新建 `ui_page_xxx.c` → 4. 在 `ui_content_page.c` 的枚举/`s_defs[]` 注册 → 5. 在 `ui_menu.c:menu_content_page_for()` 手写 `case N:`(**下标硬编码,表序一变即错**)→ 6. 注册到 `MDK-ARM/*.uvprojx` → 7. 注册到 `simulator/CMakeLists.txt` → 8. 新汉字跑 `font-regen`。 +1. 1bpp 单色:绘制缓冲 160×160/8 = 3,200 B/份,多缓冲每加一份都算进 `RW_IRAM1`(DMA 必须在 SRAM) +2. `LV_MEM_SIZE` 现为 16 KB;实测峰值 6,568 B(40%)→ **再降到 8 KB 前必须先看这个水位** +3. 字符集:新增中文必须过 `font-charset`,缺字 = 白框(静默视觉缺陷) +4. UI 侧缓冲**不得进 CCM**(DMA2 不可达);`ui_net_sink` 推流缓冲常驻 6.4 KB,产品构建可加开关 +5. 字体与像素改动 = 视觉回归范围,必须走 `ui-verify` skill 的五步 -**8 步里 5 步是纯机械劳动且 2 处易错(步骤 5 的魔法下标、步骤 6/7 漏注册)。** 步骤 6/7 如今有 `registration-check` 钩子兜底(存量走白名单),但**新页面落在新目录而没把目录加进 `uvprojx` 的 ``** 时,报错落在**消费者 TU** 上(`cannot open source input file`),仍不等于"存在但未注册"的检查。 +### 6.5 实施顺序(历史,已执行完) -### 6.3 标准化目标:四层 + 一个闭环 +W6 簇按 U4 → U1 → U3 → U2 的顺序执行完毕(U2 停在"够用即止"),顺序与理由见 §6.1 与变更记录 +2026-10-15 三行;不再另存排期。 -```mermaid -graph LR - A[L0 设计令牌
ui_tokens.h] --> B[L1 组件库
ui_w_* 自绘控件] - B --> C[L2 页面原型
list / detail / form / records] - C --> D[L3 页面实例
ui_page_xxx.c 仅填数据] - D --> E[生成器 gen_page_registry.py] - E --> F[ui_page_registry.c
菜单节点→页 映射] - F --> G[门禁: check_registration + golden] -``` +## 7. 统一改进路线图(不另立权威) -**L0 设计令牌(1 天,零行为变化)** — 新建 `Drivers/BSP/UI/ui_tokens.h`: +| 波次 | 本报告条目(今天状态) | 备注 | +|---|---|---| +| ~~W3 预算与内存~~ ⏸ | R-9 ✅ / R-2 后半、R-3、R-4、R-6 暂缓 | 暂缓期间无验收动作,观测照常 | +| W4 结构债 | N-14 ⏸、F-5 🟡、S-1/S-2/S-4/S-5、N-25 | `--strict` 已默认;`bsp_err_t` 仍未进规范 | +| W5 验证常态 | 覆盖率、`malloc` 正则、TC 差异 13 处升门禁 | 逢窗口顺手做 | +| W6 UI 扩展 | U2 残余、U5 页量 | 随产品需求,不再单列专项 | +| W7 仓库决策 | D-1(N-29) | 已决策暂不处理;对外交付前重评 | -```c -/* 屏幕与骨架 */ -#define UI_SCREEN_W 160 -#define UI_SCREEN_H 160 -#define UI_TITLE_Y 2 -#define UI_TITLE_H 11 -#define UI_PAGE_LINE_Y 143 /* 页脚分隔线 */ -#define UI_ROW_PITCH 13 /* 行距(12px 字体 + 1) */ -#define UI_ROW_H 12 -#define UI_LIST_TOP 15 -#define UI_SCROLLBAR_W 2 -#define UI_PAD_X 3 -#define UI_BUF_STRLEN 32 /* = CP_ROW_BUF_LEN,统一一处 */ -/* 语义色(1bpp:只有黑白,但保留语义名以便换深度时平移) */ -#define UI_COL_FG LV_COLOR_BLACK -#define UI_COL_BG LV_COLOR_WHITE -#define UI_COL_SEL_FG UI_COL_BG -#define UI_COL_SEL_BG UI_COL_FG -/* 样式:只用 const 描述符(动态 lv_style 会每次泄漏 88 B,见 ui_content_page.c) */ +## 附录 A 关键证据索引(2026-10-22) -> ⚠ **上面这份 L0 清单与代码实况有 9 处不符,U1 落地时已按代码为准修正**(2026-10-15 实测,`ui-tokens` 报回)。**照抄会翻车的两条**: -> ① **`UI_SCREEN_W/H` 不是新建**——`ui_port.h:21-22` 早有同名宏,在 `ui_tokens.h` 再定义一次就是同名宏二次定义;实际做法是 `ui_tokens.h` 只 include `ui_port.h` 复用,并把散落的 7 处 `160`(`CP_/MENU_/SB_/DECO_/PW_SCREEN_`、推流协议 `UI_W/H`)收敛到这一个来源。 -> ② **`UI_COL_FG = LV_COLOR_BLACK` / `UI_COL_BG = LV_COLOR_WHITE` 两处都错**——本仓 LVGL 9.3 **没有** `LV_COLOR_BLACK/WHITE` 宏(全库 0 处定义),只有函数 `lv_color_black()/lv_color_white()`,照抄直接编不过;而且**极性反了**:代码里所有墨(文本 / 分隔线 / 边框 / 滚动条)恒用 `lv_color_white()`,选中行才是黑字。按本报告写会把整屏反色。落地定义:`FG=white`、`BG=black`、`SEL_FG=black`、`SEL_BG=white`。 -> -> 其余 7 条是取值/存在性错误(报告值 → 代码实况):`UI_TITLE_H 11` → 代码无此值,真实重复量是 `+15` 文本条带(建为 `UI_TEXT_BAND_H 15`);`UI_ROW_PITCH 13` → 列表行距实为 **14**(13 是字体行高/选中带高);`UI_ROW_H 12` → 代码无 12 作行高,选中白带高 **13**(`UI_ROW_BAND_H`);`UI_LIST_TOP 15` → 列表首行顶实为 **24**;`UI_SCROLLBAR_W 2` → 实为 **4**;`UI_PAD_X 3` → 不存在"统一内边距 3",那个 3 是选中白带**右内缩**(建为 `UI_SEL_BAND_INSET_R 3` 配 `_L 2`);**`extern const lv_style_const_prop_t UI_STYLE_FLAT[]` 在 U1 做不了**——共享 `extern` 数组需新增 `.c` 落定义 → 必须双端注册(U1 禁碰注册文件),退化成头里 `static const` 又会在"包含但未使用"的 TU 上招 `#177-D` 破 0 警,已并入 U3。 -> -> **教训**:本报告的 UI 章节是**体系评审视角的建议**,不是实现规格。落地前必须逐条对代码核一遍——本次 9 条里若照抄,两条编译失败、一条整屏反色。U3/U5 执行时同样以代码为准,发现与本节不符**直接按代码做并在回报里列出**。 -> -> ⚠ **U3 开工侦察又补两条**(2026-10-15,`ui-tokens`):① **密码页不是节点绑定的**——任何 `#P` 叶子都进同一个密码页,所以注册表除 `node→desc` 映射外**必须单独出口一个"密码页"入口**;本节生成器草图的 `@page=deadzone` 没覆盖这条,照抄会做不出能进密码页的注册表。② **`ui_w_form` 从 U3 推迟到 U5**(Lead 裁定):全仓只有 **N=1** 个表单实例(`ui_page_net_cfg.c`),且它的"表单"实质是"5 行地址列表 + 逐字节编辑光标"、**并未走配置引擎的段注册表**,与本节设想的"cfg 段/键绑定 + 十进制/IP 输入约束"**并不同形**。从单一实例反推接口等于猜,而这是要动像素的重构——**重复三次再抽象**。U3 范围因此收敛为「注册表 + 删手工分发 + 共享 flat 样式」三件(这才是 U5 能并行的真前提);`ui_w_form` 等 U5 有 2~3 个真实 form 实例可对照时再抽。 -> 同批裁定:**不新建 `ui_page_desc_t`**——代码里 `content_page_def_t`(title/count_cb/refresh/draw_cb/key_cb/edit_capable…,7 个页面全在用)已是唯一描述符,再造一套等于一个页面两套描述符;注册表只加 `ui_page_entry_t{node, desc}` 做节点绑定。**不加 archetype 枚举**(无消费者的元数据,真正需要它的是 U5 的"由原型自动实例化页面")。 -extern const lv_style_const_prop_t UI_STYLE_FLAT[]; -``` +| 结论 | 证据位置 | +|---|---| +| 三区水位与 Top-10 静态大头 | `../内存管理/data/ram_report_20261022.md` §1/§2/§3 | +| 9 任务栈判定 | `../内存管理/data/stack_report_20261022.md` | +| F-4 关不掉 | `Lib/lvgl/src/draw/sw/lv_draw_sw.c`(`execute_drawing()`)+ 两份 `lv_conf.h` 的 `LV_DRAW_SW_COMPLEX` 注释 + `Drivers/BSP/UI/ui_deco.c` | +| F-6 已落地与等价性 | `App/algo/prot_set.h` Q10 宏注释;`MDK-ARM/STM32F407-Demo.map` 交叉引用区(`refers to mf_w.l(d*.o)`) | +| `snprintf` 必带 `_fp_digits` | 全库 `%f` 命中 0;`mc_w.l(printfa.o)` 交叉引用 | +| 任务栈配置唯一来源 | `Src/freertos.c` + `README.md`/`AGENTS.md` 的 `TASK_TABLE` 受管块 | +| UI 落地形态 | `Drivers/BSP/UI/ui_tokens.h`、`ui_page_registry.c`、`ui_w_records.c`、`scripts/check_ui_golden.py` | +| 外部资料体积 | `git ls-files docs/芯片手册`(717 文件)+ `du -sm .git`(248 MB) | -要点:**保留现有"只用 `lv_style_const_prop_t` 常量样式"的纪律**(这是已被验证的必要约束:动态样式每个 88 B 泄漏);令牌化后所有页面自动获得统一观感,改一行即全局生效。 +## 附录 C 确认「无需改动」的设计(避免后续误改) -**L1 组件库(3 天)** — 把 `ui_menu_view.c` / `ui_content_page.c` 中重复的自绘逻辑抽成无状态组件(纯 `lv_layer_t` 绘制,不创建 LVGL 对象,保持零控件、零池压力): +1. **MicroLIB 的 `_fp_digits` 及其双精度 helper(1,018 B)**——不是"忘了清 `%f`",是 `snprintf` 的固有 + 依赖;删不掉,别再去搜 `%f`。 +2. **`LV_DRAW_SW_COMPLEX = 1`**——2026-09-03 与 2026-10-22 两次实测:关掉后"浅 V"两腿不渲染。 +3. **FTL 单页写回缓存**(N-14/N-22)——`f_close` 已经 `CTRL_SYNC` 落盘,为"单条记录"加空闲/掉电强制 + flush 属过度设计。 +4. **uvprojx 的 127 个空 LVGL TU**(F-5)——不影响镜像与增量时长,删条目反而破坏"改 `lv_conf.h` 全量 + 重编"的一致性。 +5. **CH395F SPI 分频 = 8**、**ADC1 走 DMA2 Stream4**、**TIM3 做 1 kHz 节拍源**——都是 PCB/硬件约束, + 不是可调优参数。 +6. **就绪屏障位用 `osFlagsNoClear`**——曾被默认消费行为坑到 FTP 静默不监听。 +7. **`ui_page_registry` 描述符注册表**——已取代手写 `case`,不要再退回分发式写法。 -| 组件 | 职责 | 从何处抽取 | -|------|------|-----------| -| `ui_w_title` | 标题栏 + 分隔线 + 页码 | `ui_content_page.c` 通用绘制链 | -| `ui_w_list` | 行列表 + 选中反白 + 滚动条 | `ui_menu_view.c` 行绘制 | -| `ui_w_kv` | "名称: 值" 两列(装置信息/实时数据) | `ui_page_device_info.c`、`ui_page_ac.c` | -| `ui_w_records` | 定长记录分页浏览(含 `s_cache_valid/s_last_rec` 缓存范式) | ✅ **已建成(2026-10-20 U2)**:运行/自检记录两页按实例复用,两页净 −62 行、逻辑单源;缓存内嵌实例而非组件级静态(共享会致切页读到另一页的陈旧行)。`@build` 0/0 + golden 11 页逐字节一致 + `stack_report` FAIL 0;`ER_IROM1` −116 B、`RW_IRAM1` +8 B(`.id` 有初值使实例落 .data 而非 ZI,非误差) || -| `ui_w_form` | 键值 + 光标 + 可编辑(含十进制/IP 输入约束) | `ui_page_net_cfg.c` | -| `ui_w_msgbox` | 确认/错误提示("密码输入错误"等) | `ui_page_password.c` | +## 9. 编号族登记处(**冻结**,`check_ids.py` 的属主登记处) -**L2 页面原型(4 类,覆盖全部剩余页面)**: +> 本文是下列 6 个族的属主登记处(`scripts/id_registry.json`,`check_dangling=true`),其它文档以 +> 「报告 N-14」「报告 F-4/F-5/F-6」「报告 D-1」「报告 R-2/R-3」式写法引用它们。**删除本节会让 +> 编号门禁报"定义缺失"并拦住提交**。新增条目一律改用**语义短语**、不再发新号(`../任务规划/README.md` §0 +> 的用户裁定);本节只保留存量号的最终状态。 -| 原型 | 适用菜单 | 数据契约 | -|------|---------|---------| -| `detail`(KV 只读) | 装置信息、交流量、直流量、遥信量 | `kv_provider_t{ count(); get(i, name, val); }` | -| `form`(可编辑) | 整定/定值/控制字/软压板、遥测死区/系数、遥信类型/防抖/双点、装置维护、通讯参数、网口/SNTP | 直接绑定 `cfg_file` 描述符(**复用配置引擎的段注册表,无需新数据结构**) | -| `records`(分页日志) | SOE/事故/操作/保护告警/保护启动/遥控/自检/运行记录/运行报告 | `record_provider_t{ count(); get(idx, ts, text); }`(sys_log 已是这个形态) | -| `action`(确认型) | 强制复归、清除记录、手动录波 | 提示 → 二次确认 → 执行 → 结果日志 | +| 号 | 事项 | 状态 | 现在详述于 | +|---|---|---|---| +| N-01 | 配置引擎整文件驻留 | ✅ 已修(流式列为后续项) | `../子系统设计/配置文件设计说明.md` §6.2/§8 | +| N-02 / N-03 / N-03a / N-03b / N-03c | 存储与日志取证类条目 | ✅ 已闭环 | 存储架构说明 §10.1、本文变更记录 | +| N-06 | 注册白名单与 `--strict` | 🟡 残余随 B2 | 登记簿 N-23 残余 | +| N-07 / N-08 | 网络自愈与卷锁 | ✅ 已闭环 | `../网络/网络自愈架构设计.md` §4.10 | +| **N-09** | `NET_MAX_SOCKETS`/PHY/PASV 三项网络债 | ❌ 在策 | 本文 §4.2、登记簿 N-25 | +| N-10 ~ N-13 | 主机侧 nand/dhara 移植层单测动因 | ✅ 已建 | `test/host/nand_port/`(第 16 个单测钩子) | +| **N-14** | FTL 写回缓存无空闲/掉电强制 flush | ⏸ 暂缓 | 本文 §4.3、登记簿 N-22 | +| **N-15** | `nand_ftl` 三块 2 KB 页缓冲未合并 | 🟡 在策 | 本文 §5.2、登记簿 N-25 | +| N-16 / N-17 | 台架窗口项(task stall 归因等) | 🟡 待窗口 | 登记簿 N-16/N-17 | +| **N-18** | V0.1 双转换器无共同采样触发 | ❌ 待 V0.2 | 本文 §0.3、AGENTS 硬件引脚节 | +| N-19 ~ N-21 | 时钟/对时与已销项条目 | ✅ 已闭环 | 本文变更记录与去处表 | +| **D-1** | 外部资料与工程文档同仓同树 | ❌ 暂不处理 | 本文 §3.3、登记簿 N-29 | +| D-2 ~ D-7 | 文档治理条目(链接/同源/编号/BOM 等) | ✅ 多数已制度化为钩子 | 本文 §3.2、`../文档规范.md` | +| D-9 | 文档面残余观察 | 🟡 观察 | 本节即定义位 | +| **S-1 / S-2 / S-4** | 语义层错位 / 裸 `extern` / `app_cfg.h` 占位开关 | ❌ 在策 | 本文 §0.4、登记簿 C5 | +| **S-3** | `.ioc` 侧 USART1 配置 | ❌ 在策 | 登记簿 S-3 | +| **S-5** | 统一 `bsp_err_t` 错误码约定 | ❌ 未做 | 本文 §0.4、`docs/代码规范/`(待建) | +| R-1 ~ R-9 | 内存优化项(R-9 已落地、R-5 判定不可行、其余随簇暂缓) | 见 §5.3 | 本文 §5.3、`../内存管理/RAM预算台账.md` | +| F-1 / F-2 / F-3 | LVGL 死代码裁剪,省 64,576 B | ✅ 已落地 2026-10-11 | `../内存管理/RAM使用分析与优化方案.md` §2.6.4 | +| **F-4 / F-5 / F-6** | Flash 双精度与死代码簇 | ✅/🟡/❌ 见 §5.4 | 本文 §5.4、`App/algo/prot_set.h` 注释 | +| U1 ~ U5 | UI 标准化簇 | 见 §6.1 | 本文 §6.1、`../UI设计/UI_设计架构.md` | -**口径**:`ui_menu_tab.h:28 UI_MENU_TAB_COUNT` = 42(根 + 41 节点),**叶子实为 33 个**;已有内容页的叶子 **6 个** → 待落地 **27 页**。四类原型的分布按 27 页重算即可。这意味着**剩余页面工作量从"27 页 × 各自设计"降为"4 个原型 + 27 份数据绑定"**。 - -**L3 页面实例** — 目标形态(以"遥测死区"为例,全部新代码约 30 行): - -```c -static const ui_form_item_t s_items_deadzone[] = { - /* cfg 段/键, 显示名, 类型, 最小, 最大, 权限 */ - { "deadzone", "ia", "A 相死区", UI_T_UINT, 0, 1000, UI_PERM_MODIFY }, - { "deadzone", "ib", "B 相死区", UI_T_UINT, 0, 1000, UI_PERM_MODIFY }, - { "deadzone", "ic", "C 相死区", UI_T_UINT, 0, 1000, UI_PERM_MODIFY }, -}; -const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_deadzone); -``` - -**生成器补全(1.5 天,关键收益)** — 扩展 `ui_menu_tree.txt` 支持页绑定注解,让 `gen_menu.py` 一并产出注册表,**消灭步骤 4/5 的手工 switch**: - -``` -遥测死区 #P @page=deadzone @arch=form -``` - -产物:`ui_page_registry.c` 含 `node_index → (page_id, archetype, desc*)` 常量表 + `ui_page_registry_for(node)`;`ui_menu.c` 只需一次调用。生成器同时校验:`@page` 指向的描述符是否存在、是否有节点声明了页但无实例、是否有实例未被任何节点引用(编译期断言或生成期报错)。 - -**闭环门禁(1 天)**——未闭环 2 条: - -1. **golden 回归入门禁**:`simulator/tests/` 四脚本已建成,但未挂进 pre-commit / `ui-verify`。 -2. **字符集自动校验**:构建前扫描所有 `ui_*.c` 中的汉字,与 `ui_cn_chars.txt` 求差集,缺失即 FAIL(把 `font-regen` 从"记得跑"变成"漏跑就红")→ ❌ 未入门禁(`font-regen` skill 已把四步固化并写进 AGENTS.md,但**仍是约定不是门禁**)。 - -### 6.4 UI 侧的内存纪律(必须写进规范) - -| 规则 | 依据 | -|------|------| -| 页面**不得**新增静态大缓冲;文本缓冲统一用 `UI_BUF_STRLEN` | 现有 `s_ts/s_desc` 已是范式 | -| 每页**只用** `lv_style_const_prop_t`,禁止运行期 `lv_style_init` | 已验证动态样式每次泄漏 88 B | -| 优先自绘(`lv_draw_*` + `lv_text_get_width`),新控件需评审(会破坏 §5.4 F-2 **已落地**的裁剪,且须两端同步) | LVGL 现占镜像 **37.8%**(87,706 / 231,768 B,2026-10-22 构建) | -| 帧缓冲任何改动必须遵守 R-4(副本数 ≤ 2) | 当前 4 副本 12.8 KB | -| `LVGL 池`用量必须经 `lv_mem_monitor` / 池水位门禁实测后才允许扩大 | **16 KB**(R-9 于 2026-10-15 由 40 KB 降下;实测峰值 6,568 B = 池的 40%,余 ≈9.8 KB) | - -### 6.5 实施顺序(UI 专项,总计约 8 人日) - -| 步骤 | 内容 | 交付判据 | 状态 | -|------|------|---------|------| -| U1 | `ui_tokens.h` 令牌化(纯替换,不改行为) | `@build` PASS + 模拟器截图与替换前逐像素一致 | ✅ **已落地(2026-10-15)**:`ui_tokens.h` 20 个宏 + 15 个文件接令牌。**零行为变化证据**:`@build` 0 错 0 警且 `Program Size` 与改前**逐字节相同**(Code=213540 / RO=18740 / RW=1136 / ZI=131520);golden 菜单 8 状态全像素一致 + 内容页 **11 页逐字节一致**,且比对对象是改前备份的 sha256 基线(**不是 `--update` 刷出来的**);池水位 5368 / 5448 B 未变。⚠ 落地时修正本节 L0 清单 9 处与代码不符(见上) | -| U2 | `ui_w_records`(合并 runlog/selftest) | 两页行为不变,代码 −1 个文件 | ✅ **已落地(2026-10-20)**:见本节 `ui_w_records` 行。两页各留一个实例 + 两个薄跳板,`.count_cb/.refresh` 名不变 => 注册表与生成器零改动 || -| U3 | `ui_w_form` + `ui_page_desc_t` 原型化 + 生成器产注册表 | 手工 `menu_content_page_for()` 删除;新增一页只需"改树 + 加实例文件" | ✅ **已落地(2026-10-15)**:`ui_page_registry.{c,h}` + `ui_style.{c,h}`(原"U1 做不了"的共享 flat 样式一并落)+ 扩 `gen_menu.py` 产注册表**并自动写 `uvprojx`/`CMakeLists.txt`**;手工 `menu_content_page_for()` 已删。**实测单位成本:新增一页 = 3 处人工**(新 `.c` + 新 `.h` + 菜单树那一行加 ` #page=<名>`)**+ 1 条生成器命令,手工编辑注册文件 0 处**;生成器**双向对账**(增 `registered` / 删 `pruned`)且双向校验死代码。验收:`@build` 0/0、golden 菜单 8 状态 + 内容页 11 页逐字节一致、池水位 5368/5448 B 未变、`ER_IROM1` 232,052 B(较已提交 HEAD −228 B)、SRAM1 零增量。⚠ **本报告原口径"8 处降到 1 处"须修正**:那个 1 只在"1 处对既有文件的修改"意义下成立,页面自身 2 个文件本就要写,真实改动处数是 **3** | -| U4 | golden + 字符集检查 + 注册检查入 pre-commit | 三个新 hook 全绿 | ✅ **3/3 落地(2026-10-15 起,10-20 补齐)**:`font-charset` 新钩子(字符集差集,防缺字白框)+ `registration-check` 补第 ④ 类"UI 页面描述符表接线"检查。钩子数 34 → **35** 。钩子数 34 → **35**。⚠ **修正我自己上一版的错**:此处原写"3/3 落地",是把"golden 脚本早已存在"误当成"golden 已进自动门禁"。实况是**两个 golden 脚本至今未挂 pre-commit**(一直在人工跑,无自动门禁),故当时改回 **2/3**。⚠ **10-20 已补齐第三件**:新钩子 `ui-golden` 把"增量构建模拟器 + 两条 golden"串成一条自动门禁(实测 2~4 秒、注入 3 px 位移可 FAIL 并点名页面),故 3/3 此刻才真正成立——**这次是真的进了自动门禁,不是"脚本存在"**。⚠ **`--strict` 的验收口径已改**:判据是"**未知**冗余 = 0"而非"冗余 = 0"——`ui_cn_chars.txt` 里 异/常/片/芯 4 字属**刻意保留**(2026-09-14 `9eb1bd63` 写入的中文自检描述曾落盘 `/log/selftest.log`,定长 64 B 记录跨固件持久,删字形会使历史故障行渲染空白),按 `check_registration.py` 的 `KNOWN_GAPS` 同构做法进带理由的保留清单;上/电/重 3 字已证从未出现在任何字符串字面量(仅注释),照删。为几百字节牺牲已落盘审计记录的可读性不划算——同一权衡口径见 §5.3 内存簇暂缓裁定 | -| U5 | 用原型批量落剩余 **27 页**(每页 ≤30 行) | 菜单树全绿、无 `CONTENT_PAGE_COUNT` 哨兵命中 | ❌ **暂缓(等页清单)** | ⚠ **U5 暂缓(2026-10-20 用户裁定)**:剩余页面**尚未规划**,本轮不补页 —— 现存 7 个 `ui_page_*.c` 即当前全部内容,报告"27 页"是目标数不可当差集清单。U1–U4 已全部落地,故 U5 未来开工时是纯增量活(实测新增一页 = 3 处人工 + 1 条生成器命令,且 `ui-golden` 已在提交门禁兜像素)。 | - -> ✅ **2026-10-15 用户裁定启动**(覆盖 2026-10-11 的「本轮不排期」,登记簿 U 簇 / W6):已开智能体团队推进。**并行度受注册文件约束**——新增一个 `.c` 必须同时改 `MDK-ARM/STM32F407-Demo.uvprojx` 与 `simulator/CMakeLists.txt` 这两个共享文件,所以任何时刻只允许一个成员碰它们。实际排法:**第一波 U1(令牌层,不新增 .c)∥ U4 残余(只动 scripts/ 与门禁文档)真并行** → U3(原型化 + 生成器,独占注册文件,串行段)→ U2 → U5(页面批量,须等 U3 的生成器把注册自动化,否则无法并行)。⚠ 本节原写「27 页」是**目标页数**,现存 `ui_page_*.c` 实测只有 **7 个** → U5 开工前先对齐目标页清单差集。 - ---- - -## 7. 统一改进路线图 - -> **顺序的唯一权威 = [`../任务规划/README.md`](../任务规划/README.md) §2**(W1 采集保护收尾 → ~~W2 数据正确性~~ **✅ 已清空销项 2026-10-15** → ~~W3 预算与内存~~ **⏸ 已整簇暂缓** → W4 结构债 → W5 验证常态 → W6 UI 扩展 → W7 仓库决策;B2 RS-485 暂缓)。本节只保留"本报告条目 → 波次"的映射,不另立顺序。 - -| 波次 | 本报告条目 | 验收要点 | -|------|-----------|---------| -| ~~**W3 预算与内存**~~ ⏸ | **整簇暂缓(用户裁定 2026-10-15)**:R-9 ✅ 已落地不回退;R-2 后半 / R-3 / R-4 / R-6 不再排期 | 暂缓期间**无验收动作**;`mem-budget` 92% 门禁与台账、趋势、`stack_report.py` / `ram_report.py` 观测**照常跑**(它们是判断何时重开的输入)→ 重开条件见 §5.3 | -| **W4 结构债** | N-14、F-5(观察项)、S-1/S-2/S-4/S-5;`rs485_task.c` 随 B2 出注册白名单(~~F-4/F-6~~ 已于 2026-10-22 收口,见 §5.4) | `--strict` 默认化;`bsp_err_t` 进规范 | -| **W5 验证常态** | P1-10 收尾(构建实测链接 + `@test` 闭环) | 逢窗口顺手做 | -| **W6 UI 扩展** | U4 残余 → U1 → U2/U3 → U5(27 页) | 新增一页 ≤30 行;**2026-10-15 启动**(见 §6.5 注) | -| **W7 仓库决策** | D-1(登记簿 N-29) | **已决策暂不处理**;推送/协作前重评 | - -### 交付物清单(可直接排期) - -| 交付物 | 类型 | 来源条目 | 状态 | -|--------|------|---------|------| -| `test/host/nand_port/`(dhara 移植层单测) | 主机侧单测 + pre-commit hook | N-12/N-13 | ✅ **已建(2026-10-15,提交 `720f841`)**——原写 `test/host/dhara/` 是拟名,落地为 `nand_port/`。149 断言 / RAM 假芯片 / 真实 dhara map+journal 源 / 含"旧算法只看页首 64 B"**对照用例**,已挂为第 16 个 `-unit-test` 钩子。**这条正是"防同类缺陷再犯"的手段,现已到位** | -| `Drivers/BSP/UI/ui_tokens.h` | 令牌层 | U1 | ❌ 未建 | -| `Drivers/BSP/UI/ui_w_{list,kv,records,form,msgbox,title}.{c,h}` | 组件库(需双端注册) | U2/U3 | ❌ 未建 | -| `tools/menu_gen/gen_menu.py` 扩展 + `ui_page_registry.c` 生成 | 生成器 | U3 | ❌ 未建 | -| `check_coding_standard.py` 加 `malloc(` 正则 | 门禁补强 | §5.5 第 5 条 | ❌ 未建 | -| `scripts/check_font_charset.py`(字符集差集)+ `registration-check` 第 ④ 类 UI 接线检查 | 门禁补强 | U4 残余 | ✅ **已建(2026-10-15)**——提取与 cmap 解析复用 `tools/font_gen/` 同一套规则(`importlib` 直载,零第二套规则);牙齿实测三例(缺字 / 字体缺码位 / 稀疏 cmap 缺偏移)均 FAIL 并点名到具体字符 | - ---- - -## 附录 A 关键证据索引 - -| 结论 | 证据(2026-10-15 `.map`) | -|------|---------------------| -| SRAM1 57.4%、余 55,840 B;CCM 87.9%、余 7,904 B | map `Execution Region RW_IRAM1 Size: 0x00012600, Max: 0x00020000` / `RW_CCM Size: 0x0000e020, Max: 0x0000ff00`;`ram_baseline.tsv` 末行 | -| MicroLIB C 堆 = 512 B | `MDK-ARM/startup_stm32f407xx.s` `Heap_Size EQU 0x200`;map `Removing startup_stm32f407xx.o(HEAP), (512 bytes)`(未被引用 → 整段移除) | -| 任务栈全部动态分配 | `Src/freertos.c` 的 `osThreadAttr_t` 无 `.stack_mem`;8 条产品任务栈合计 9,792 words(R-6 未办) | -| DMA 可达性三层防线(原 N-11 的处置) | 编译期窗口断言 `gd5f2gq5ue.h:96-107`(负数组 typedef)→ 启动期自检 `nand_ftl.c:241-243` → 运行时守卫 `gd5f_dma_buf_ok()`(`gd5f2gq5ue.c:121-139`);规则族 R1~R4 → `../内存管理/内存内容分配策略.md` §4;负路径用例 TC-GD5F-704/705 | -| CH395F memcpy 自有缓冲 | `ch395f.c` SPI2 DMA 收发缓冲(`s_spi2_dma_tx/rx[2052]`)→ UI 缓冲仍可迁 CCM 的前提成立 | -| dhara 两处缺陷 | `nand_ftl.c:152-179`(`uint8_t buf[64]`)/ `:204-216`(read→prog 直传,无哨兵判断) | -| 日志逐条开关 | `sys_log_mcu.c:124-141`(`FA_WRITE | FA_OPEN_APPEND` → `f_write` → `f_close`,失败重建重试一次);`wdev.log` 同模式 `:486-489` | -| 端口分配 | `App/sync/time_sync.c` `TIME_SYNC_PORT 8888`;`Drivers/BSP/UI/ui_net_sink.h` `UI_NET_SINK_PORT 7003`;FTP 21 | -| 菜单 42 节点 / 33 叶子 / 6 个有内容页 | `ui_menu_tab.h:28 UI_MENU_TAB_COUNT 42`;`ui_content_page.c:84-92 s_defs[]`(7 页含 PASSWORD) | -| 手写映射 | `ui_menu.c:118-128 menu_content_page_for()`(`case 1u/3u/4u/30u/39u/40u:`) | -| 注册反向校验已机械化,仅剩一条刻意缺口 | `scripts/check_registration.py`(`KNOWN_GAPS` 只含 `App/task/rs485_task.c`,E-59/B2 刻意未接线)+ pre-commit `registration-check`;`lftpd_log.c` 已删除并入 `lftpd_auth.c` | -| 仓库体量 | 受控 2,466 文件 / 79.3 MB;`docs/` 56.9 MB;`docs/芯片手册/` 图片 703 张 / 36.3 MB;`.git` **235.3 MB** | - -## 附录 C 评审中确认"无需改动"的设计(避免后续误改) - -| 项 | 结论 | -|----|------| -| CH395F SPI 分频 = 8 | **PCB 约束,禁止"优化"**(AGENTS 铁律,实测一致) | -| 就绪屏障用 Event Flags 而非魔法延时 | 正确,保留(就绪位必须 `osFlagsNoClear`) | -| FatFs `FF_FS_REENTRANT=1` + 真实互斥 | 正确,保留(多数移植偷工) | -| 配置引擎属主 = defaultTask | 正确,任何新代码不得跨任务触碰 | -| UI 只用 `lv_style_const_prop_t` | 正确且必要,禁止"顺手改成动态样式" | -| `ffsystem.c` 引用 `sys_clock.h` | 有意为之(中间件需要真实时间),保留但在分层图中显式标注 | -| UI 自绘而非 LVGL 控件 | 正确(1 bpp + 小 RAM),并是 Flash 裁剪的前提 | -| `configMAX_PRIORITIES = 56` | **不可下调**:CMSIS-RTOS2 `freertos_os2.h` 硬性 `#error` 要求等于 56(原 R-5 建议因此关闭) | -| 两份 `lv_conf.h` 同一特性子集 | 必要(F-1/F-2/F-3 的前提),由 `lv-conf-sync` 钩子守 | - ---- - -**报告结束。** - -> 本报告按 `../README.md` 的定位是**体系层评审报告,不是待办池**——待办与顺序一律以 [`../任务规划/README.md`](../任务规划/README.md) 为准;本报告只保留**未闭环问题的发现 + 证据**,修复即销项。 - ---- ## 变更记录 @@ -664,6 +402,11 @@ const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_dea | **2026-10-20** | **补上 U4 缺的第三件:golden 像素回归入提交门禁(35→36 钩子)** | 先实测成本再定形态:改一个 UI 源文件后「增量重编模拟器 + 两条 golden」合计 **2~4 秒**,支持走自动门禁(若实测要几分钟,正确做法是明确不挂、改为文档化人工门禁——一个逼人 `--no-verify` 的门禁等于没有门禁)。新增 `scripts/check_ui_golden.py` 包装器(**有意忽略 argv 并 `pass_filenames: false`**:两条 golden 按整棵菜单树 + 全量参考图工作,不接受文件参数),新钩子 `ui-golden` 的 `files:` 覆盖 UI 源 + `ui_task.{c,h}` + 模拟器 tests/sim/src + `lv_conf.h` + 包装器与本配置文件自身。 ⚠ **端到端验的是钩子不是脚本**:干净树连跑 3 次 `rc=0` 全 Passed;注入 `ui_page_dc.c` 的 `.top_y +5` → **钩子 Failed** 并点名 `[dc] FAIL 与参考页不一致(1882 px 差异)`;还原后 `git diff` 干净、`simulator/tests/ref` 的 `git status` 为空(**全程未用 `--update` 刷基线**)。诚实记录一次瑕疵:首次实测在连续注入/还原的紧邻重建之间偶发失败一次,疑似 MSBuild 节点争用,已把"单次失败先复跑、连续两次才当真、不许直接绕过"写进钩子注释。 据此 §6.5 U4 由 **2/3 → 3/3**(这次是"真进了自动门禁",不是"脚本存在")、§6.1 golden 行转 ✅、§0.1 UI 行缺口描述更新、`AGENTS.md` 钩子数与文末索引 35→36、`pre-commit使用说明.md` 概览计数同步。**W6 剩余**:U2 合并两页记录、U5 落地目标页(须先对齐"目标页 vs 现存 7 个 `ui_page_*.c`"差集)。 +| **2026-10-22** | **v3.0 全量重写:删过时条目 + 按今天的仓库重新审计**(用户裁定"过时的都删除,重新写") | 旧正文按今天的实测重取数重判:六维评分旧表、UI「8 人日方案」正文、波次交付物清单、以及所有已闭环条目整段删除;**697 → 436 行**(−37%)。 +**重写依据(全部当日实测,不再引用旧数字)**:镜像 `ER_IROM1` 231,768 B / 22.1%(Code 213,188)、`RW_IRAM1` 75,264 B / 57.4%(余 55,808)、`RW_CCM` 57,376 B / **87.9%(余 7,904)**、堆 `min_ever` 8,464 B、任务栈 9/9 PASS(`data/ram_report_20261022.md`、`data/stack_report_20261022.md`);仓库 2,498 文件 / 80.0 MB、`.git` 248 MB、`docs/芯片手册` 717 文件 / 39.5 MB 占 docs 87%;37 个钩子、16 套主机单测、TC 593∶271 交集 265。 +**改判的三条旧结论**:① §6 的 U1/U3/U4 原写「❌ 未建」,实际已落地(`ui_tokens.h` 69 行、`ui_page_registry.c` 描述符注册表、`ui-golden`+`font-charset` 两枚钩子);U2 只有 `ui_w_records` 一个组件;"27 页未落地"实为 7 页。② "新增一页 ≤30 行"目标**未达**(最省 40 行),如实记为未达项。③ §0.4 与登记簿已漂开 → 本版把待办清单降级为**快照 + 指向登记簿**,权威只留一处。 +**刻意保留的三层**(删了会出事):§9 **编号族登记处**——本文是 `id_registry.json` 里 `N-RPT`/`D-RPT`/`S-RPT`/`R-RPT`(游标 R-9)/`F-RPT`/`U-RPT`(游标 U5) 六族的属主登记处,且外部存在「报告 N-14」「报告 F-4/F-5/F-6」「报告 D-1」「报告 R-2/R-3」式紧邻引用与 `check_dangling`,删定义位即触发编号门禁;§1.2/§1.3 方法与口径(数字可重跑);文末**变更记录 + 已销项去处表**原样冻结保留(52 行)。被外部点名的锚点(`§0.3`/`§3.1`/`§4.1`/`§4.2`/`§4.3`/`§5.3`×9/`§5.4`/`§5.5`×4/`§6.2`/`§6.3`/`§6.5`)逐个保留,**68 处入站引用零改动**。 +**门禁**:`check_doc_links.py --all --sections-strict` → 146 文档 0 问题(章节引用 2,499、可判定 1,483);`check_ids.py --strict` 增量拦截通过、**存量债 650 → 641(−9)**、可判定比例 82.98% → **82.79%**(仍高于基线 82.6%,但缓冲从 0.38pp 缩到 0.19pp——删掉的那批引用 99% 是可判定的,属预期副作用);`mem-numbers` / `ram_baseline --gate` / `task-table` / `cubemx-survival` 全 0。字节卫生:BOM 无 / CR 0 / U+FFFD 0 / 无行尾空格 / LF 结尾。 | | **2026-10-22** | **执行 §5.4:F-6 落地、F-4 判定不做、F-5 降级观察项**(登记簿 N-28 同步改写) | 动手前逐条对 `.map` 与源码核(本报告"建议栏不可照抄"总口径的又一次复现——三处误判全部出在**建议栏**,"发现"栏字节数全对)。**F-6 ✅**:`App/algo/prot_set.h` 两个 Q10 宏去掉显式 `(double)`、`1024.0`→`1024.0f` → `@build` 0 错 0 警、Code 213,520→**213,188(−332 B)**;`.map` 中 `dflti`、`d2f`、`f2d`、`dfixi`、`fepilogue` 共 300 B 消失,自研侧不再引用任何双精度 helper。**原报告"来自 `printf` 的 `%f`"是误判**:全库 0 处 `%f`,剩余 `dadd`+`dmul`+`ddiv`+`depilogue`+`dfixul` = 1,018 B 由 `mc_w.l(printfa.o)` 的 `_printf_core → _fp_digits` 无条件引用,MicroLIB 层面删不掉。**数值等价用穷举证明**(宏内 42 个字面量 + `int32` 全域 ÷1024 往返 4.49×10⁶ 例零差异;×1024 方向 2×10⁵ 例零差异);回归 = `prot_set` 90/90、`prot_elm` 47/47、`iec104` 97/97 主机单测全过。**F-4 ❌ 判定不做**:五项字节数逐字节仍准(合计 10,060 B)但**无一项有开关**,根因是 `lv_draw_sw.c` 的 `execute_drawing()` 无条件 switch 引用全部 task 类型;唯一总开关 `LV_DRAW_SW_COMPLEX` 被 `ui_deco.c` 的"浅 V"3 px 粗斜腿锁死(**2026-09-03 已实测失败并两端固化 =1**);**原"mask/grad 是文字必经路径"的理由同时证伪**(字形走 blend 层 `mask_buf`,不经 `lv_draw_sw_mask_*`)。**F-5 🟡 降级观察项**:uvprojx 实测 326 个 `.c` / LVGL 201 个 / `.map` 零产出 **127 个**,但增量构建实测 4~5 s、远程 CI 刻意不跑 `@build`,原"−40%"无基线记录。同步更正 §0.1 Flash 行、§0.4 Flash 行、注册表 uvprojx 行、§1 口径行、§5.1 快照注与要点行、§6.4 LVGL 占比分母、W4 波次行;台账受管区与内存快照已 `--write` 回填(`mem-budget` / `mem-numbers` 转绿)。**`@test` 台架 PASS**(固件戳 `fw build: Oct 22 2026 10:36:15 suite=NONE dstr=off`;无 `[ERR]`/`ERROR`/`FAIL`/Fault,708 ms 到 `net init: OK`,`protect task start`、`i104: listening on 2404`、`[STACK]` 三面水位齐;`adc`/`phasor`/`freq` 心跳持续) | | **2026-10-14** | **清理:删除已完成与过时内容** | ① **已修复项按销项删除**(去处见下表):N-02(FTP 鉴权)、N-03c(`Error_Handler` 降级)、**N-11(DMA 可达性三层防线 + 规则族 R1~R4)**、S-3(注册反向校验)、P2-5/P2-6(SD2506 负温度与 WRTC)、P2-8(`NET_RECV_TIMEOUT_MS` 已删)、P2-9(`mkdir` 幂等重试)、§5.5 前三条(内存治理三件套)、交付物清单已建项。② **删除过时层**:2026-09-16 / 2026-10-11 双列基线表与"复核期间未提交改动"口径说明、旧"组合结果预估"表、逐条"2026-10-11 复核"批注、历史变更记录行——内存数字改由 `../内存管理/RAM预算台账.md` 与 `scripts/ram_baseline.tsv` 持有。③ **按 2026-10-14 `.map` 与代码重测**全部保留结论的数字(含 `lftpd_log.c` 已删除并入 `lftpd_auth.c`,N-06 残余只剩 `rs485_task.c` 一条刻意缺口)。④ **修正三处错误**:R-5(`configMAX_PRIORITIES` 56→32)**判定不可行**(CMSIS-RTOS2 `#error`);原 §7 把报告 S-5(驱动统一错误码)与登记簿 S-5(卷锁覆盖面)混为一谈;原 §5.5 第 4 条建议的 `_Static_assert` 形态在 AC5 上取不到链接期地址,已被"窗口断言 + 启动自检 + 运行时守卫"三层取代。⑤ 报告自有编号**不回改、不重排**(`../编号规范.md`) |