506eccc68f
本次会话先后销项与关闭: - E-61 SD2506 清 INTAF:原实现只读取 CTR1,而按手册 ARST=0 时读操作 不清标志,属空操作;改为读-改-写 CTR1,并按手册订正 CTR1 位宏 (INTAF/INTDF/BLF 位置,ARST 归 CTR3) - E-16 cfg_file:新增 CFG_FILE_MAX_NAME_LEN(段名/键名 ≤15),注册时 拒绝超长键名;s_gen_document 两处 snprintf 改检查式;补 TC-CFG-024 用例(主机单测 149→151 PASSED) - E-23 TC704:PHASE7_MULTI_COUNT 8→5(并发上限 = 8 − 3 个常驻监听), accept 失败分支加网络代次检查(网络重建即重建监听) - E-26(UDP)按“保留 API + 文档定案”关闭;E-30 评估为不必要后关闭; N-4(ACCEPT 决定不实施)关闭 审计文档剩余 5 项 P2 + 2 项监督,经用户裁定“都不重要”后整体删除: - 删除 docs/问题记录/错误码传播审计.md(634 行) - 九类缺陷模式表迁入 docs/编号规范.md §4.2.1(PAT-01~09) - E-xx 转为纯历史编号族:编号规范 §4.1 属主改“无(原属主已删除)”, §4.3 保持冻结;全仓 26 处引用按“保留编号、去悬空文档名”处理 - 顺带修正 N-4 收口遗留:排期详述 C1 才是 E-25/E-27/E-28 的承载处 (审计 §4 曾误写为登记簿);README 两处 C1 旧名改回“net 层跨上下文收口” 一并提交此前遗留的体系层评审报告 2026-10-14 复核 WIP(六维评分复核、 未闭环 Top 5→3、三区水位实测刷新)。 校验:Keil 构建 0 错误 0 警告(Code=212344);check_doc_links --sections-strict 与 --all(142 文档)通过;内存双台账刷新后均 PASS。
651 lines
64 KiB
Markdown
651 lines
64 KiB
Markdown
# STM32F4-Base 项目审查与优化建议报告
|
||
|
||
| 项目 | 内容 |
|
||
|------|------|
|
||
| 审查日期 | 2026-09-18(初版) |
|
||
| **最近复核** | **2026-10-14**,基线 `HEAD = 803f771`(分支 `v2-on-ui`,工作树干净);固件基线 = 2026-10-14 22:55 链接的 `MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(0 Error / 0 Warning)。⚠ 2026-10-15 06:41 该 `.map` 被**含未提交改动**的一次重链接覆盖(Code +48 B),本报告数字**不追那次构建**——复跑 `ram_report.py` 会看到小差异,属正常 |
|
||
| **销项状态** | 本报告采用**修复即销项**:已修复项的正文与表格行已删除,去处见文末**已销项条目的记录去处**;未闭环清单见 **§0.2 / §0.4** |
|
||
| 审查范围 | 仓库全量(2,466 个受版本控制文件 / 79.3 MB);重点为自研代码 50,874 行(191 文件)与构建产物内存画像 |
|
||
| 审查方式 | 静态审查 + 链接图(.map)量化分析 + 构建脚本/门禁核对;**未做上机动态验证**(除特别标注外) |
|
||
| 目标 | 结构合理性、文档分布合理性、模块设计合理性、改进路线图、内存优化、UI 标准化 |
|
||
|
||
> **与既有文档的关系**:本报告不重复 [`../任务规划/README.md`](../任务规划/README.md)(逐条缺陷台账 + **推进顺序唯一权威**)与 `../内存管理/RAM使用分析与优化方案.md`(内存专项方案),而是做**体系层评审 + 量化补强 + 新增发现**。
|
||
>
|
||
> **编号口径**:本报告自有编号 `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`)。
|
||
>
|
||
> **销项约定**:问题修复后其分析正文与表格行**从本报告删除**,完整记录沉淀到对应模块文档。因此本报告**只呈现未闭环问题**,不随时间膨胀。
|
||
|
||
---
|
||
|
||
## 0. 执行摘要
|
||
|
||
### 0.1 六维评分(2026-10-14 复核)
|
||
|
||
| 维度 | 评分 | 一句话结论 | 仍开放的主要缺口 |
|
||
|------|------|-----------|-----------------|
|
||
| 项目结构 | **A−** | 六层分层真实可执行、命名一致、门禁到位;反向依赖控制在 4 处 | `NETCFG`/`UI` 语义层错位(S-1)、4 处裸 `extern`(S-2)、`rs485_task.c` 仍在注册白名单里等 B2 |
|
||
| 文档分布 | **A−** | 工程文档 **88 篇 / 31,416 行**,`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 全链均已落地 | **N-12/N-13** dhara 移植两处缺陷(数据丢失风险)、**N-16** 日志逐条开关、驱动层无统一错误码约定(S-5)、RS485 无协议栈(随 B2 暂缓) |
|
||
| 内存使用 | **B** | 堆迁 CCM + 栈/堆三面观测常开 + `mem-budget` 92% 门禁;SRAM1 **74.9%** | **LVGL 池 + UI 两份缓冲 54,146 B 仍在 SRAM1**,而 CCM 只剩 **7,904 B** → R-2 后半被堵死,须先 R-9(§5.3) |
|
||
| Flash 使用 | **A−** | F-1/F-2/F-3 落地省 64,576 B → 占用 **22.0%(231,036 B)** | F-4/F-5/F-6 ≈10 KB + 编译时长(§5.4) |
|
||
| UI 标准化 | **B** | golden 回归已建(`simulator/tests/` 四脚本,含池水位门禁) | 无令牌层 / 无组件库 / 无页注册表(手写 `case` 仍在)、**27 页**未落地、golden 与字符集差集未挂门禁(§6) |
|
||
|
||
### 0.2 未闭环问题(Top 3,2026-10-14)
|
||
|
||
| # | 编号 | 问题 | 影响 | 现状 |
|
||
|---|------|------|------|------|
|
||
| 1 | **N-12 / N-13**(登记簿 **N-21**,W2) | `dhara_nand_is_free()` 只查页首 64 B(`nand_ftl.c:152-179`);`dhara_nand_copy()` 不处理 `DHARA_NO_PAGE` 哨兵(`:204-216`) | 映射恢复误判 → **数据丢失风险**(低概率、后果重);哨兵页号被当真实页 → 未定义行为 | ❌ 两处原样;至今只能靠人工读代码发现(主机侧无 dhara 移植层测试) |
|
||
| 2 | **R-2 剩余 / R-9**(登记簿 **N-27**,W3) | LVGL 池 `work_mem_int` 40,960 B + `ui_port.o` 6,420 + `ui_net_sink.o` 6,766 = **54,146 B 仍在 SRAM1** | 堆 56 KB 占掉 CCM 后 **CCM 只剩 7,904 B**,这 54 KB 装不下 → R-2 后半段堵死 | 🟡 必须先 R-9(`LV_MEM_SIZE` 40→16 KB)+ 缩堆腾 CCM,解锁链 → `../内存管理/RAM预算台账.md` §3 |
|
||
| 3 | **N-16**(登记簿 **N-24**,W4) | 日志每条记录一次 `f_open + f_write + f_close`(`sys_log_mcu.c:124-141`) | 每条日志 2–4 次 NAND 页读写;UI 翻记录逐条 open/seek/close | ❌ 未办(仅 `FIL` static 化省 528 B 栈) |
|
||
|
||
⚠ 另有两条**不在上表但需知晓**:`.git` 已膨胀到 **235 MB**(D-1,§3.1,2026-10-11 决策"暂不处理、登记观察");`rs485_task.c` 仍不在两端构建清单里——**这是 E-59 门禁下的刻意缺口**(随 B2 暂缓),已登记在 `check_registration.py` 的 `KNOWN_GAPS`,不是漏注册。
|
||
|
||
### 0.3 两条主线建议
|
||
|
||
> **推进顺序的唯一权威是 [`../任务规划/README.md`](../任务规划/README.md) §2**;本节只是体系评审视角的两条结构性建议,不另立顺序。
|
||
|
||
1. **把内存从"卡死"变"富裕"**:先按 R-9 下调 LVGL 池、再缩堆腾出 CCM,把 LVGL 池与 UI 帧缓冲(≈54 KB)搬进 CCM,作为后续功能的预算来源(§5.3)。
|
||
2. **把 UI 从"每页手写"变"声明 + 原型库"**:令牌层 → 原型库 → 生成器补全注册 → golden 入门禁,使新增一页的成本从 8 处手工改动降到 1 处声明(§6)。
|
||
|
||
---
|
||
|
||
## 0.4 未闭环清单(2026-10-14 复核)
|
||
|
||
> 只列**未办 / 半办**项;已修项按「修复即销项」删除(去处见文末)。图例:❌ 未办 / 🟡 半办。
|
||
> **顺序权威在 [`../任务规划/README.md`](../任务规划/README.md) §2,本表为评审快照**;登记簿编号在括号内。
|
||
|
||
| 类别 | 编号 | 未闭环内容 | 下一步 |
|
||
|------|------|-----------|--------|
|
||
| **数据正确性** | **N-12 / N-13** ❌(N-21,W2) | `is_free` 只查页首 64 B;`copy` 不处理 `DHARA_NO_PAGE` | 整页(或首/中/尾)扫描 + 返回 `DHARA_E_UNSUPPORTED` + **必须配主机侧移植层单测** |
|
||
| **内存 RAM** | **R-2 剩余** 🟡(N-27,W3) | LVGL 池 + UI 两份缓冲 54,146 B 仍在 SRAM1;CCM 只剩 7,904 B | 先 R-9 + 缩堆腾 CCM,再迁(解锁链 → `../内存管理/RAM预算台账.md` §3) |
|
||
| 内存 RAM | R-3 / R-4 / R-6 / R-9 ❌(N-27,W3) | 页缓冲未合并 / UI 帧 4 副本 / 栈仍动态 / `LV_MEM_SIZE` 仍 40 KB | R-9 最省力(池水位门禁数据已具备);R-6 前置条件已满足。**R-5 已判定不可行**(§5.3) |
|
||
| 内存 RAM | N-14 🟡(N-22,W4) | 写回缓存无空闲/掉电强制 flush(整页写与换页已 flush) | 空闲 N ms 由 defaultTask flush |
|
||
| **Flash** | **F-4 / F-5 / F-6** ❌(N-28,W4) | 剩余可评估 ≈10 KB(`box_shadow` 3,246 / `draw_sw_img` 2,960 / `bin_decoder` 1,972 / `image_decoder` 952 / `triangle` 930)+ 双精度 `dadd` 等 ≈1 KB;uvprojx 仍挂 201 个 LVGL `.c`(F-2 后均为空 TU,省的是编译时长) | 逐项实测再关;⚠ `mask`/`mask_rect`/`grad` 是文字与矩形绘制的必经路径,**不可盲关** |
|
||
| **性能/资源** | 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 这类缺陷至今只能靠人工读代码发现 |
|
||
|
||
---
|
||
|
||
## 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`) | 高(门禁) |
|
||
|
||
### 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,否则中文名会被控制台代码页破坏)
|
||
```
|
||
|
||
> ⚠️ **踩坑记录**(供后续审查复用):
|
||
> ① 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,036 B(22.0% of 1 MB)**;`ROM` 再加 `RW-data` 初始化表 = 231,324 B |
|
||
| RAM 占用 | `RW-data + ZI-data`,**跨两个区域** = SRAM1 `RW_IRAM1` **98,144 / 131,072(74.9%)** + 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`。本报告只在支撑结论处引用,不与之竞争事实源。
|
||
|
||
---
|
||
|
||
## 2. 项目结构合理性
|
||
|
||
### 2.1 规模与分布(2026-10-14 实测)
|
||
|
||
| 分类 | 文件 | 行数 | 说明 |
|
||
|------|------|------|------|
|
||
| `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` 钩子守住,属"约束被机械化"的正例。
|
||
|
||
### 2.2 分层与依赖方向
|
||
|
||
实测自研层间的**全部反向依赖**(BSP → App)——2026-10-14 复核为 **4 处**:
|
||
|
||
| 位置 | 反向 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) | 中间件→应用;已在文件内注释声明为"④中间件层"有意为之 |
|
||
|
||
**评价:良好**。在 50k 行规模上把反向依赖控制在 4 处,说明分层不是墙上的图。但存在**语义层错位**:
|
||
|
||
- `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 兜底之后**。
|
||
|
||
依赖方向裸引用(绕过头文件契约的 `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** | 323 个 `.c`(含 201 个 LVGL) | LVGL 全量编译 → 编译时长;F-2 落地后未用控件类 TU 均为空目标文件(见 §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) |
|
||
|
||
### 2.5 结构结论
|
||
|
||
**总体判断:结构合理,无需重构**。要做的是三类**低成本纠偏**:S-1 层次定性、S-2 消裸 extern、S-4 让 `app_cfg.h` 开关真正生效或删除。
|
||
|
||
---
|
||
|
||
## 3. 文档分布合理性
|
||
|
||
### 3.1 体量画像(2026-10-14 实测,工作树)
|
||
|
||
| 对象 | 文件数 | 体积 | 占仓库 |
|
||
|------|--------|------|--------|
|
||
| 仓库受控文件总计 | 2,466 | **79.3 MB** | 100% |
|
||
| `docs/` 合计 | — | **56.9 MB** | **71.8%** |
|
||
| └ `docs/芯片手册/` 图片 | 703 张 | **36.3 MB** | **45.8%** |
|
||
| `.git` 目录 | — | **235.3 MB** | — |
|
||
| 工程文档(Markdown,自研) | 88 篇 / **31,416 行** | — | — |
|
||
| 外部资料(Markdown,datasheet 转录) | 14 篇 / 58,244 行 | — | — |
|
||
|
||
**关键失衡**:工程文档与外部资料的行数比已从 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 做得好的(应保留并显式制度化)
|
||
|
||
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` 等只读钩子守住"文档说的 == 代码做的"。
|
||
|
||
### 3.3 问题清单
|
||
|
||
#### D-1(P1)外部资料与工程文档同仓同树
|
||
|
||
**影响**: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),推送/协作前重评——届时未推送提交更少、重写代价最低。
|
||
|
||
### 3.4 文档结论
|
||
|
||
**内容质量高(A−)、分布形态差**。D-1 处理后可到 A:工程量约 1 人日(不含拆仓与历史重写),收益是 clone 时间与"文档可信度"。
|
||
|
||
---
|
||
|
||
## 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)落定前至少别让评审者误信"可裁剪" |
|
||
|
||
### 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 语义已主流化。
|
||
|
||
**问题**:
|
||
|
||
| 编号 | 问题 | 证据 | 建议 |
|
||
|------|------|------|------|
|
||
| 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" |
|
||
|
||
### 4.3 存储子系统(GD5F2GQ5UE + dhara + FatFs)
|
||
|
||
**设计良好**:
|
||
- 分层正确:`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`"。
|
||
|
||
**问题**:
|
||
|
||
| 编号 | 问题 | 证据 | 影响/建议 |
|
||
|------|------|------|-----------|
|
||
| **N-12** | `dhara_nand_is_free()` 只检查页首 64 B | `nand_ftl.c:152-179`(仍 `uint8_t buf[64]`) | dhara 依赖该判定做映射恢复;页首 64 B 全 FF 但后半页有数据时会误判为空闲 → 数据丢失风险(低概率但后果重)。建议整页扫描或至少首/中/尾三处采样 |
|
||
| **N-13** | `dhara_nand_copy()` 未处理 `DHARA_NO_PAGE` 语义(dst 为哨兵值时仍执行"读+编程") | `nand_ftl.c:204-216`(read→prog 直传) | dhara map 在回收/擦除路径会用 `DHARA_NO_PAGE` 试探;当前实现会把它当作真实页号 → 未定义行为。建议:`if (dst == DHARA_NO_PAGE) { dhara_set_error(err, DHARA_E_UNSUPPORTED); return -1; }` |
|
||
| 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` 收紧)。
|
||
|
||
**问题**:
|
||
|
||
| 编号 | 问题 | 证据 | 建议 |
|
||
|------|------|------|------|
|
||
| **N-16** | **每条记录一次 `f_open + f_write + f_close`**,读侧同样每条 `f_open/f_lseek/f_close` | `sys_log_mcu.c:124-141`(失败重建重试一次)、`wdev.log` 同模式 `:486-489` | 写侧:常驻 `FIL`(仅 defaultTask 持有)+ 批量 flush;读侧:打开一次 + 缓存目录偏移(UI 已有 `s_cache_valid/s_last_rec` 雏形,可升级为页缓存)。🟡 唯一改善 = `FIL` 已 static 化(省 528 B 栈) |
|
||
| P2-10 | 容量轮转 `f_unlink` 与 UI 读并发 | | 轮转只在写者、读者持版本号;卷锁覆盖面评估 → 登记簿 S-5 |
|
||
|
||
### 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 改动从此有逐像素/逐字节基线可依赖。
|
||
|
||
### 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/代码规范/`。
|
||
|
||
### 4.8 测试体系
|
||
|
||
**设计良好**:`test/board/test_config.h` 的 `TEST_SUITE_*` 互斥(`#error` 保护)、CH395F 硬件层/NET 层 Phase 互斥、`@test` 一键闭环(烧录→wait-idle→标记校验)、模拟器 `--cfgtest/--logtest`、主机侧单测 15 个钩子(含 `sha256` / `ftp_auth` / `vol_lock` / 采集算法族)。
|
||
|
||
> ⚠ 仍缺:**覆盖率概念**、**主机侧对 `dhara` 移植层的测试**(N-12/N-13 这类缺陷至今只能靠人工读代码发现)。
|
||
|
||
**问题**:
|
||
|
||
| 编号 | 问题 | 建议 |
|
||
|------|------|------|
|
||
| P1-10 | CH395F 硬件测试套件静态 RAM 预算(`L6406E` 历史:HW 套 +8.7 KB、NET 层固件 +28 KB) | 基线已重建、测试缓冲已瘦身、RAM 侧因堆迁 CCM 大幅缓解;**剩"套件构建实测链接确认 + `@test` 闭环"两步**(登记簿唯一 P 类收尾项,W5) |
|
||
|
||
---
|
||
|
||
## 5. 内存优化(量化)
|
||
|
||
### 5.1 实测基线(2026-10-14 `.map`)
|
||
|
||
| 指标 | 数值 | 上限 | 占用 |
|
||
|------|------|------|------|
|
||
| `Code + RO-data`(`ER_IROM1`) | 231,036 B | 1 MB Flash | **22.0%** |
|
||
| `ROM`(`Code + RO + RW` 初始化表) | 232,148 B | 1 MB | 22.1% |
|
||
| **SRAM1(0x20000000,128 KB)** `RW_IRAM1` | **98,144 B** | 131,072 B | **74.9%(余 32,928 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% |
|
||
|
||
> **权威源**:区域水位与子系统配额 → `../内存管理/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 40*1024`) | 40,960 | **SRAM1** | LVGL 池 | 纯 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 + 40,960 = 98,336 B)仍是 RAM 问题的核心**,且**分居两区**:堆在 CCM(占 CCM 的 87.9%)、LVGL 池在 SRAM1(占 SRAM1 的 42%)。→ 下一步收益点在**把 LVGL 池 + UI 两份缓冲(54,146 B)也搬进 CCM**,但 CCM 只剩 7,904 B → 必须先按 R-9 缩池并缩堆(解锁链 → `../内存管理/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,449 | ≈19.7% |
|
||
| **合计** | **231,036** | 100% |
|
||
|
||
**要点**:LVGL 的绝对量自 F-1/F-2/F-3 裁剪后**未再增长**(87,706 B),但仍是镜像最大单一子系统 → §5.4 的剩余裁剪(≈10 KB)与 F-5 的编译时长收益依旧成立。
|
||
|
||
### 5.2 结构性诊断(三条)
|
||
|
||
1. **CCM 已被堆占掉 87.9%(只剩 7,904 B)**,而 LVGL 池 + UI 两份缓冲 **54,146 B** 仍在 SRAM1。
|
||
→ R-2 的剩余部分**必须先做 R-9(下调 `LV_MEM_SIZE`)+ 缩堆**才有余地;顺序不可颠倒(`../内存管理/RAM预算台账.md` §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-2** | **启用 CCM 承载 LVGL 池与 UI 缓冲**:对 `work_mem_int` 与 UI 帧/绘制缓冲加段属性(`.sct` 已有 `RW_CCM`) | **−54,146 B** | 中:必须确保这些缓冲**不被任何 DMA 直接寻址**。已核实 UI 发送路径 memcpy 进 CH395F 自有 SRAM1 DMA 缓冲,LVGL 为纯 CPU;**NAND 路径(规则 R2/R3)绝对不可迁入** | **P1(W3)** | 🟡 **只做一半**:`RW_CCM` 只收 `heap_4.o`(57,376 B)→ SRAM1 已减 49 KB+。⚠ 前置条件:**CCM 只剩 7,904 B,这 54 KB 装不下** → 必须先 R-9 + 缩堆 |
|
||
| **R-3** | 存储页缓冲合并:`nand_ftl` 三块 2 KB 中 `s_page_buf`(dhara scratch) 与 `s_copy_buf` 不可并发,可复用 | **−2 KB** | 中:需严格审查 dhara 调用序列(copy 期间不会用 map scratch) | P2 | ❌ 未办(`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 | ❌ 未办(`ui_port.o` 6,420 + `ui_net_sink.o` 6,766 = 13,186 B,副本数未变) |
|
||
| **R-6** | 任务栈改**静态分配**(`stack_mem` 指向各自 `.bss` 数组),从堆里释放 ≈39 KB | 堆可用 +39 KB(**总量不变**,但把"池内碎片"变成"可核算静态区") | 低-中:静态后溢出检测更难发现;先测水位再定尺寸 | P2 | ❌ 未办。⚠ 前置已满足:观测三面常开 + `stack_report.py` 已给出每任务最坏链 → 可直接按实测值定尺寸 |
|
||
| R-9 | `LV_MEM_SIZE` 40 KB → 依实测峰值下调(既有方案预估 16 KB) | −16 ~ −24 KB(SRAM1) | 低(有池水位门禁兜底) | P2(**R-2 后半的前置**) | ❌ 未办(两份 `lv_conf.h` 仍 `(40 * 1024)`)。⚠ `simulator/tests/check_content_pages.py` 已带**池水位门禁** → 实测峰值数据已具备,本条只差改数值 + 真机复跑(**两端 `lv_conf.h` 必须同步改**,`lv-conf-sync` 钩子会拦) |
|
||
|
||
> ⚠️ **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,属实施风险关键点)。
|
||
|
||
### 5.4 Flash 优化(未闭环:F-4 / F-5 / F-6)
|
||
|
||
> **F-1 / F-2 / F-3 已落地并销项(2026-10-11)**:两份 `lv_conf.h` 对齐为同一特性子集 → **实测省 64,576 B**,Flash 26.3% → 20.1%(现 22.0%,增量来自 B4/B5 功能代码)。完整记录见 `../内存管理/RAM使用分析与优化方案.md` §2.6.4 与 `../UI设计/UI_设计架构.md` §1.4 第 10 条。
|
||
|
||
| 编号 | 措施 | 可省 Flash | 依据 | 状态 |
|
||
|------|------|-----------|------|------|
|
||
| **F-4** | 关闭未用的绘制子模块与 `LV_USE_SNAPSHOT`(MCU 端)/`LV_USE_OBJ_PROPERTY`/`LV_USE_FLOAT` 等 | 可评估部分 **≈10 KB** | 对象实测:`box_shadow` 3,246 + `draw_sw_img` 2,960 + `bin_decoder` 1,972 + `image_decoder` 952 + `triangle` 930 ≈ **10.1 KB**;工程无图片、无阴影、无变换 | ❌ 未办。⚠ **不可盲目关**:`mask` 3,862 / `mask_rect` / `grad` 是文字与矩形绘制的必经路径 |
|
||
| **F-5** | 从 uvprojx 中**移除未使用的 LVGL 控件源文件**(201 个 → 实际需要的子集) | 编译时长 −40% 左右 | 与 F-2 配合;`lv_conf` 关开关后文件仍参与编译,删文件才省编译时间 | ❌ 未办(uvprojx 现挂 **323 个 `.c`,其中 LVGL 仍 201 个**)。⚠ F-2 落地后这些 TU 现在编出来是**空目标文件**,删文件的收益更纯粹、风险更低 |
|
||
| **F-6** | 双精度软浮点:`dadd`/`dmul`/`ddiv`/`depilogue`/`dfixul`(`mf_w.l`),来自 `printf` 的 `%f`/double 提升 | −1 KB + 运行期性能 | 日志与 UI 全用定点;把 `%f` 改整数刻度 | ❌ 未办(新镜像仍链入 `dadd.o`) |
|
||
|
||
**剩余可回收 ≈ 10–11 KB Flash**(F-4 可评估部分 + F-6);F-5 省的是编译时长不是 Flash。
|
||
|
||
### 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`"的建议形态**已被这份更好的实现取代**。
|
||
|
||
仍开放一条:
|
||
|
||
5. **禁止新增 C 库 `malloc`**:写进 `docs/代码规范/`,并在 `check_coding_standard.py` 加正则检查(`App/`、`Drivers/` 内出现 `malloc(` 即告警)。
|
||
→ **🟡 前半已做、后半未做**:AGENTS.md 已有铁律(含模拟器照不出来的原因);❌ `check_coding_standard.py` **未含 `malloc(` 正则** → 目前只靠人评审,属机械检查缺口。
|
||
|
||
---
|
||
|
||
## 6. UI 界面标准化实现方案
|
||
|
||
### 6.1 现状(框架已有,但未"闭环")
|
||
|
||
| 层 | 现状 | 评价 |
|
||
|----|------|------|
|
||
| 内容页 | `ui_content_page.c` 描述符表 `s_defs[]` **7 页**(DEVICE_INFO/AC/DC/PASSWORD/NET_CFG/RUN_LOG/SELFTEST_LOG) | ❌ `ui_menu_tab.h:28` 42 节点(**叶子 33 个**),有内容页的叶子 **6 个**(PASSWORD 非叶子映射);`menu_content_page_for()`(`ui_menu.c:118-128`)仍是手写 `case 1u/3u/4u/30u/39u/40u:` |
|
||
| 设计令牌 | **无**(每页自带 `CP_SCREEN_W/H 160`、`CP_TITLE_Y 2`、`CP_PAGE_LINE_Y 143`、`CP_ROW_BUF_LEN 32` 等布局常量) | ❌ 魔法数字分散在各页,`Drivers/BSP/UI/ui_tokens.h` 不存在(U1 未办) |
|
||
| golden 回归 | `simulator/tests/` 四脚本:`check_menu_rows.py`(菜单 8 状态全像素)、`check_content_pages.py`(内容页黄金参考逐字节 + 文本断言 + **LVGL 池水位门禁**)、`regen_ref_png.py`、`check_remo_sink.py` | 🟡 **有基线但未入门禁**:两个脚本未挂 pre-commit / `ui-verify` |
|
||
|
||
### 6.2 新增一个页面要走多少步(当前)
|
||
|
||
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`。
|
||
|
||
**8 步里 5 步是纯机械劳动且 2 处易错(步骤 5 的魔法下标、步骤 6/7 漏注册)。** 步骤 6/7 如今有 `registration-check` 钩子兜底(存量走白名单),但**新页面落在新目录而没把目录加进 `uvprojx` 的 `<IncludePath>`** 时,报错落在**消费者 TU** 上(`cannot open source input file`),仍不等于"存在但未注册"的检查。
|
||
|
||
### 6.3 标准化目标:四层 + 一个闭环
|
||
|
||
```mermaid
|
||
graph LR
|
||
A[L0 设计令牌<br/>ui_tokens.h] --> B[L1 组件库<br/>ui_w_* 自绘控件]
|
||
B --> C[L2 页面原型<br/>list / detail / form / records]
|
||
C --> D[L3 页面实例<br/>ui_page_xxx.c 仅填数据]
|
||
D --> E[生成器 gen_page_registry.py]
|
||
E --> F[ui_page_registry.c<br/>菜单节点→页 映射]
|
||
F --> G[门禁: check_registration + golden]
|
||
```
|
||
|
||
**L0 设计令牌(1 天,零行为变化)** — 新建 `Drivers/BSP/UI/ui_tokens.h`:
|
||
|
||
```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) */
|
||
extern const lv_style_const_prop_t UI_STYLE_FLAT[];
|
||
```
|
||
|
||
要点:**保留现有"只用 `lv_style_const_prop_t` 常量样式"的纪律**(这是已被验证的必要约束:动态样式每个 88 B 泄漏);令牌化后所有页面自动获得统一观感,改一行即全局生效。
|
||
|
||
**L1 组件库(3 天)** — 把 `ui_menu_view.c` / `ui_content_page.c` 中重复的自绘逻辑抽成无状态组件(纯 `lv_layer_t` 绘制,不创建 LVGL 对象,保持零控件、零池压力):
|
||
|
||
| 组件 | 职责 | 从何处抽取 |
|
||
|------|------|-----------|
|
||
| `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` 缓存范式) | `ui_page_runlog.c`、`ui_page_selftest.c`(两文件结构几乎相同 → 直接合并) |
|
||
| `ui_w_form` | 键值 + 光标 + 可编辑(含十进制/IP 输入约束) | `ui_page_net_cfg.c` |
|
||
| `ui_w_msgbox` | 确认/错误提示("密码输入错误"等) | `ui_page_password.c` |
|
||
|
||
**L2 页面原型(4 类,覆盖全部剩余页面)**:
|
||
|
||
| 原型 | 适用菜单 | 数据契约 |
|
||
|------|---------|---------|
|
||
| `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`(确认型) | 强制复归、清除记录、手动录波 | 提示 → 二次确认 → 执行 → 结果日志 |
|
||
|
||
**口径**:`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 现占镜像 **38.0%**(87,706 / 231,036 B) |
|
||
| 帧缓冲任何改动必须遵守 R-4(副本数 ≤ 2) | 当前 4 副本 12.8 KB |
|
||
| `LVGL 池`用量必须经 `lv_mem_monitor` / 池水位门禁实测后才允许扩大 | 现 40 KB,预估可降到 16 KB(R-9) |
|
||
|
||
### 6.5 实施顺序(UI 专项,总计约 8 人日)
|
||
|
||
| 步骤 | 内容 | 交付判据 | 状态 |
|
||
|------|------|---------|------|
|
||
| U1 | `ui_tokens.h` 令牌化(纯替换,不改行为) | `@build` PASS + 模拟器截图与替换前逐像素一致 | ❌ 未办。🟡 **验收条件如今可机械执行**——golden 基线已建,"逐像素一致"从口头判据变成可跑断言 |
|
||
| U2 | `ui_w_records`(合并 runlog/selftest) | 两页行为不变,代码 −1 个文件 | ❌ 未办(两份结构几乎相同的代码仍在) |
|
||
| U3 | `ui_w_form` + `ui_page_desc_t` 原型化 + 生成器产注册表 | 手工 `menu_content_page_for()` 删除;新增一页只需"改树 + 加实例文件" | ❌ 未办(`ui_page_registry.c` / `gen_page_registry.py` 均不存在) |
|
||
| U4 | golden + 字符集检查 + 注册检查入 pre-commit | 三个新 hook 全绿 | 🟡 **1/3 落地**:golden 已建;❌ 字符集差集、❌ 注册检查未入门禁(注册检查钩子已存在,缺的是把它算作 UI 门禁的一环) |
|
||
| U5 | 用原型批量落剩余 **27 页**(每页 ≤30 行) | 菜单树全绿、无 `CONTENT_PAGE_COUNT` 哨兵命中 | ❌ 未动 |
|
||
|
||
> **本轮不排期**(2026-10-11 已确认,登记簿 U 簇 / W6):等功能主线出结果后按产品需求启动;U4 残余(两脚本入门禁)可逢窗口顺带。
|
||
|
||
---
|
||
|
||
## 7. 统一改进路线图
|
||
|
||
> **顺序的唯一权威 = [`../任务规划/README.md`](../任务规划/README.md) §2**(W1 采集保护收尾 → W2 数据正确性 → W3 预算与内存 → W4 结构债 → W5 验证常态 → W6 UI 扩展 → W7 仓库决策;B2 RS-485 暂缓)。本节只保留"本报告条目 → 波次"的映射,不另立顺序。
|
||
|
||
| 波次 | 本报告条目 | 验收要点 |
|
||
|------|-----------|---------|
|
||
| **W2 数据正确性** | N-12 / N-13(登记簿 N-21) | 整页扫描 + `DHARA_E_UNSUPPORTED` + **主机侧 dhara 移植层单测**(一并补覆盖率缺口) |
|
||
| **W3 预算与内存** | R-9 → R-2 后半 → R-3/R-4/R-6 逐项评估 | 按 `../内存管理/RAM预算台账.md` §3 解锁链走,每步前后跑 `stack_report.py` / `ram_report.py` |
|
||
| **W4 结构债** | N-16、N-14、F-4/F-5/F-6、S-1/S-2/S-4/S-5;`rs485_task.c` 随 B2 出注册白名单 | `--strict` 默认化;`bsp_err_t` 进规范 |
|
||
| **W5 验证常态** | P1-10 收尾(构建实测链接 + `@test` 闭环) | 逢窗口顺手做 |
|
||
| **W6 UI 扩展** | U4 残余 → U1 → U2/U3 → U5(27 页) | 新增一页 ≤30 行;**本轮不排期** |
|
||
| **W7 仓库决策** | D-1(登记簿 N-29) | **已决策暂不处理**;推送/协作前重评 |
|
||
|
||
### 交付物清单(可直接排期)
|
||
|
||
| 交付物 | 类型 | 来源条目 | 状态 |
|
||
|--------|------|---------|------|
|
||
| `test/host/dhara/`(移植层单测) | 主机侧单测 + pre-commit hook | N-12/N-13 | ❌ 未建——**这是唯一能防住"同类缺陷再犯"的手段** |
|
||
| `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 条 | ❌ 未建 |
|
||
| 字符集差集检查 + golden 挂 pre-commit | 门禁补强 | U4 残余 | ❌ 未建 |
|
||
|
||
---
|
||
|
||
## 附录 A 关键证据索引
|
||
|
||
| 结论 | 证据(2026-10-14) |
|
||
|------|---------------------|
|
||
| SRAM1 74.9%、余 32,928 B;CCM 87.9%、余 7,904 B | map `Execution Region RW_IRAM1 Size: 0x00017f60, 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) 为准;本报告只保留**未闭环问题的发现 + 证据**,修复即销项。
|
||
|
||
---
|
||
|
||
## 变更记录
|
||
|
||
| 日期 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| 2026-09-18 | 初版 | 全量审查;基线 `HEAD = 2d0ec35`、`.map` 链接于 2026-09-16;产出 N-01..N-18 / D-1..D-7 / S-1..S-5 / R-1..R-9 / F-1..F-6 / U1..U5 编号体系 |
|
||
| 2026-10-11 | 实施 + 销项:F-1/F-2/F-3 | 两份 `lv_conf.h` 对齐同一特性子集 → 省 64,576 B;验证链(`@build` / ui-verify / `@test`)与对象级对照见 `../内存管理/RAM使用分析与优化方案.md` §2.6.4 |
|
||
| **2026-10-15** | **登记簿同步销项** | 本报告的 N-03c / N-11 对应登记簿 **N-20 / N-26** 两行已按「完成即销项」从 `../任务规划/登记簿.md` §5 删除(并清 `../任务规划/README.md` §1/§2 与 `排期详述.md` §1 的引用,W4 链由 6 项减为 5 项、结构/健壮性计数 9→7)。删除前补齐去处记录:`../子系统设计/看门狗设计说明.md` §4.4 新增 `Error_Handler` 统一降级一节、`../内存管理/内存内容分配策略.md` §4 R2 补编译期窗口断言与启动期自检两层 + §8 落点索引两行。同批订正登记簿三处过时内容:N-23 残余去掉已删除的 `lftpd_log.c`、N-25 去掉已删除的 `NET_RECV_TIMEOUT_MS`、N-27 剔除不可行的 R-5 并把 CCM 余量改为实测 7,904 B、N-29 的 `.git` 由 149 MB 订正为实测 235.3 MB |
|
||
| **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`) |
|
||
|
||
### 已销项条目的记录去处
|
||
|
||
> 删除前已逐条核对:以下去处文档/代码均含完整记录,删了不丢信息。
|
||
|
||
| 已销项条目 | 去处 |
|
||
|-----------|------|
|
||
| N-01(配置引擎 C 堆必失败) | `../子系统设计/配置文件设计说明.md` §6.2 / §8 |
|
||
| N-02(FTP 无鉴权,登记簿 N-19) | `../子系统设计/网络配置说明.md` §3.4(`[ftp] user/pwd_hash/write`、恒定时间比较、5 败锁 60 s、出厂默认口令告警、明文嗅探边界)+ `../产品需求/产品需求设计文档.md` §FTP 鉴权 + `Drivers/BSP/NET/lftpd/lftpd_auth.c` + 主机侧 `test/host/sha256/`、`test/host/ftp_auth/` |
|
||
| N-03a/N-03b/N-07/N-08(IWDG、`configASSERT`、两个钩子、4 个 Fault 现场) | `../子系统设计/看门狗设计说明.md`(§4.4、§10.1/§10.2)+ AGENTS.md「平台约束」 |
|
||
| N-03c(`Error_Handler` 死循环,登记簿 **N-20**,2026-10-15 已销项) | `../子系统设计/看门狗设计说明.md` §4.4(统一降级:1 条自检 + `NVIC_SystemReset()`、三条实现约束含 `__builtin_return_address` 不可用的实证、内核前日志路径安全性、重生成存活三层)+ 实施代码 `Src/main.c` 的 `USER CODE Error_Handler_Debug` 区注释 + `scripts/check_cubemx_survival.py` 审计项 |
|
||
| N-06 主体(`crc.c`/`ringbuf.c`/`lftpd_log.c` 删除、`adc_task.c` 注册)+ S-3(注册反向校验,登记簿 N-23) | `scripts/check_registration.py`(`KNOWN_GAPS` 白名单 + `--strict`)+ `../代码规范/pre-commit使用说明.md`(`registration-check`);`lftpd_log.c` 并入 `Drivers/BSP/NET/lftpd/lftpd_auth.c` |
|
||
| **N-11**(NAND 直 DMA 调用方缓冲 / CCM 迁移硬边界,登记簿 **N-26**,2026-10-15 已销项) | `../内存管理/内存内容分配策略.md` §3(全板 DMA 使用方清点)+ §4 规则 R1~R4(**R3 堆/栈禁为 DMA 源/目的**,含"缓冲自身地址无法编译期断言"这条边界)+ §8 落点索引;代码三层 = `gd5f2gq5ue.h:96-107` 编译期窗口断言 → `nand_ftl.c:241-243` 启动期自检 → `gd5f_dma_buf_ok()` 运行时强制;负路径用例 TC-GD5F-704/705 |
|
||
| N-10(UI accept 门控) | `../UI设计/RemoDispBus协议与接收设计说明.md` §3 + 看门狗设计说明 §11.4 |
|
||
| N-17(栈/堆水位观测)+ R-8(观测常开) | `../内存管理/RAM使用分析与优化方案.md` + `../测试说明/实机测试实施指南.md`(`[STACK]` 水位 + `stack_report.py` + `ram_report.py`) |
|
||
| N-18(本报告口径:无 CI) | `../测试说明/测试开发标准流程.md` §9 + `../CI/README.md` |
|
||
| P2-5 / P2-6(SD2506 负温度解析、WRTC 解锁顺序) | `Drivers/BSP/SD2506/sd2506.c`(符号扩展 `val \| 0xF0`;WRTC1→WRTC2/3 顺序按手册,上电即写禁止态) |
|
||
| P2-8(`net_recv` 30 s 阻塞超时) | `Drivers/BSP/NET/net_config.h` 头注(2026-10-13 recv 语义主流化:删除 `NET_RECV_TIMEOUT_MS`,预算由调用方显式给)+ `../网络/Net层阻塞模型架构设计.md` |
|
||
| P2-9(`mkdir` 失败不再重试) | `App/log/sys_log_mcu.c`(`s_fs_ready` 幂等 + `mkdir /log fail (%d), will retry` + 上报自检) |
|
||
| P2-13 / P2-14 / P2-15 | AGENTS.md(`test-suite-switch`、`keil-build`、`stlink-flash`)+ `../测试说明/ST-LINK_CLI_复位使用说明.md` |
|
||
| D-2 / D-3 / D-4 / D-5 / D-6 / D-7 | `../文档规范.md` + `../代码规范/pre-commit使用说明.md`;断链由 `scripts/check_doc_links.py` 门禁接管 |
|
||
| U4 主体(golden 回归) | `simulator/tests/` 四脚本 + `../UI设计/UI_自动化测试流程.md`(残余 = 挂门禁,见 §6.3) |
|
||
| R-1 / R-7 | `../子系统设计/看门狗设计说明.md` + AGENTS.md 任务表(`scripts/gen_task_table.py` 生成区) |
|
||
| §5.5 前三条(92% 门禁 / 趋势基线 / 预算台账,登记簿 N-27 前半) | `../内存管理/RAM预算台账.md`(阈值与配额的**唯一权威**)+ `scripts/ram_baseline.py`(`--gate/--update/--trend/--write`)+ `../代码规范/pre-commit使用说明.md`(`mem-budget`) |
|
||
| F-1 / F-2 / F-3(LVGL 死代码裁剪) | `../内存管理/RAM使用分析与优化方案.md` §2.6.4;`../UI设计/UI_设计架构.md` §1.4 第 10 条(**两份 `lv_conf.h` 必须同一特性子集**);`../UI设计/UI_自动化测试流程.md` 变更记录(池水位基线) |
|