diff --git a/.dsh/skills/ui-verify/SKILL.md b/.dsh/skills/ui-verify/SKILL.md index 10c1b735..a053509c 100644 --- a/.dsh/skills/ui-verify/SKILL.md +++ b/.dsh/skills/ui-verify/SKILL.md @@ -38,8 +38,10 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 | 常量 | 脚本 | 当前值 | 含义 | |---|---|---|---| -| `MENU_POOL_BASELINE` | check_menu_rows.py | 4784 B | 菜单根框建完的 LVGL 池 used(x64 模拟器) | -| `POOL_AFTER_ROUNDTRIP` | check_content_pages.py | 4864 B | 开页-关页往返后 = 基线 + 80 B 一次性残留 | +| `MENU_POOL_BASELINE` | check_menu_rows.py | 5368 B | 菜单根框建完的 LVGL 池 used(x64 模拟器;2026-10-11 随 LVGL 控件裁剪由 4784 上移) | +| `POOL_AFTER_ROUNDTRIP` | check_content_pages.py | 5448 B | 开页-关页往返后 = 基线 + 80 B 一次性残留 | + +⚠ 两个常量量的是池的 **`used`**,与 `LV_MEM_SIZE` 总大小**无关** → 改池大小(如 2026-10-15 R-9 的 40→16 KB)**不该动这两个数**;动了就是页面结构变化或泄漏。 **禁止为过门禁盲目改常量**——无法解释的漂移 = 内存回归 bug。 diff --git a/Lib/lv_conf.h b/Lib/lv_conf.h index 4a6418f4..9b4a3dae 100644 --- a/Lib/lv_conf.h +++ b/Lib/lv_conf.h @@ -48,8 +48,12 @@ #define LV_OS_PRIORITY 0 #define LV_OS_TASK_NAME "ui" -/* 内存池:1bpp 小界面,菜单页较多,给 40KB */ -#define LV_MEM_SIZE (40 * 1024) +/* 内存池:1bpp 小界面 + 按需建毁(任一时刻只放"当前导航路径 + 当前内容页")。 + * 2026-10-15 由 40KB 收到 16KB:实测菜单基线 5,368 B、开页-关页往返残留 5,448 B + * (门禁常量 simulator/tests/check_menu_rows.py + check_content_pages.py),≈3× 余量。 + * 再往下试 8KB 须先跑全量 UI 回归(PC + 实机)无分配失败。 + * ⚠ 两端同值是硬约束(lv-conf-sync 比对生效值):simulator/lv_conf.h 同步改。 */ +#define LV_MEM_SIZE (16 * 1024) #define LV_MEM_ADR 0 /* 0 = 内部静态分配 */ /* 断言全部关闭,减少体积(9.3 只有这 4 个 ASSERT 宏;原 ASSERT_USER_CODE / diff --git a/docs/UI设计/LVGL模拟器部署与使用说明.md b/docs/UI设计/LVGL模拟器部署与使用说明.md index c96f4d07..c973768d 100644 --- a/docs/UI设计/LVGL模拟器部署与使用说明.md +++ b/docs/UI设计/LVGL模拟器部署与使用说明.md @@ -154,7 +154,7 @@ LVGL 官方源码**原本没有** CMakeLists.txt(只有 `src/`),本工程 | `LV_SDL_INCLUDE_PATH` | - | `"SDL2/SDL.h"` | | `LV_USE_THEME_MONO` | 0 | **1**(否则 1bpp 下文字黑底黑字不可见) | | `LV_FONT_DEFAULT` | `&ui_font_cn_12` | 同 | -| `LV_MEM_SIZE` | 40KB | 40KB(模拟器内存充足,可放宽) | +| `LV_MEM_SIZE` | 16KB | **16KB**(必须与 MCU 端同值,`lv-conf-sync` 比对生效值;2026-10-15 由 40KB 同步收缩,PC 侧不再"放宽") | ### 5.4 `simulator/sim/sim_port.c` diff --git a/docs/UI设计/UI_界面制作流程.md b/docs/UI设计/UI_界面制作流程.md index 1d82867a..02aec070 100644 --- a/docs/UI设计/UI_界面制作流程.md +++ b/docs/UI设计/UI_界面制作流程.md @@ -49,7 +49,7 @@ flowchart TD | C1 | 屏幕 | 160×160,1bpp 单色(`LV_COLOR_FORMAT_I1`),无物理屏(远端客户端显示) | `ui_port.h` | | C2 | 字体 | **唯一**:`ui_font_cn_12`(宋体 12px 1bpp)。汉字 12px/字,ASCII 6px/字,行高 13px。需要其他字号 = 大工程(新字体 + 双端 `lv_conf.h`),默认禁止 | `ui_font_cn_12.c` | | C3 | 字符集 | 子集字体(当前 122 汉字/符号 + ASCII 0x20-0x7E,共 217 字形);**缺字显示空白**;新字必须重生成字体 | `tools/font_gen/` | -| C4 | LVGL 内存池 | 40KB(`LV_MEM_SIZE`);任一时刻只放"当前导航路径 + 当前内容页" | `Lib/lv_conf.h` | +| C4 | LVGL 内存池 | 16KB(`LV_MEM_SIZE`,2026-10-15 由 40KB 收缩;实测峰值 6,568 B = 池的 40%);任一时刻只放"当前导航路径 + 当前内容页" | `Lib/lv_conf.h` | | C5 | 文本 | 一律静态字符串(`lv_label_set_text_static`),禁止动态字符串/strdup | 架构文档 | | C6 | 编码 | `Drivers/BSP/UI/*`、`ui_task.c`、`Lib/lv_conf.h` 为 **UTF-8 带 BOM**;改动只用 Edit 工具,**禁止** PowerShell/cmd 整文件重写(U+FFFD 不可逆) | AGENTS.md 铁律 | | C7 | 代码共用 | `ui_menu.c` / 字体为 MCU 与模拟器共用源,**禁止**在 `simulator/` 复制副本 | 架构文档 | @@ -310,7 +310,7 @@ flowchart TD | 模拟器主循环调 `SDL_PollEvent` | 键盘全部无响应 | 事件只由 LVGL SDL 驱动内部轮询(`UI_设计架构.md` §5.7) | | 选中行"白底白字" | 选中文字不可见 | 历史坑(2026-09-02):子 label 自身 card 样式总命中、继承不到父选中色;现行行自绘(绘制时给色、无样式继承)已根除(`UI_设计架构.md` §5.5) | | 按键处理里删除焦点容器 | use-after-free 崩溃 | 容器持久不删(`UI_设计架构.md` §5.2) | -| LVGL 池爆 40KB | 对象创建失败/画面残缺 | §4.5 预算 + 按需建毁 | +| LVGL 池爆 16KB(2026-10-15 起) | 对象创建失败/画面残缺 | §4.5 预算 + 按需建毁 | | 极性配反 | 黑屏/白屏/不可读 | 模拟器 mono 主题 `dark_mode` 参数 + MCU 端背景色一致 | | 步进宽接近/超出格宽的标签居中 | 亚像素错位/不对称 | 左对齐 + 字体侧边距(§3.6) | | 关闭了 `LV_DRAW_SW_COMPLEX`(省代码) | `lv_line` 斜线不绘制(如主界面顶部"浅 V"两腿消失,只剩水平线) | `LV_DRAW_SW_COMPLEX` 两端显式 =1;`LV_DRAW_SW_CIRCLE_CACHE_SIZE` 须非零(默认 4,否则零长数组 C2466)(`UI_设计架构.md` §关键约束 7) | diff --git a/docs/UI设计/UI_设计架构.md b/docs/UI设计/UI_设计架构.md index 0795abe7..7b3c35d1 100644 --- a/docs/UI设计/UI_设计架构.md +++ b/docs/UI设计/UI_设计架构.md @@ -23,7 +23,7 @@ MCU **无物理屏幕**:LVGL 做 160×160 **1bpp 单色离屏渲染**,帧缓 | `Drivers/BSP/UI/ui_net_sink.c/h` | **共用** | RemoDispBus 服务端(收键 + 回送帧缓冲),MCU/PC 双端同一份;平台后端 `net_socket` + `ui_net_sink_backend`(见 RemoDispBus 协议文档) | | `Drivers/BSP/UI/ui_font_cn_12.c` | **共用** | 中文 1bpp 子集字体(`lv_font_conv` 生成物) | | `App/task/ui_task.c/h` | MCU | UI 任务:等 NET_READY → 初始化 → 10ms 刷新循环 | -| `Lib/lv_conf.h` | MCU | LVGL 配置(1bpp / 40KB 内存池 / 默认字体);⚠ **必须与 `simulator/lv_conf.h` 同一特性子集**,见 §1.4 第 10 条 | +| `Lib/lv_conf.h` | MCU | LVGL 配置(1bpp / 16KB 内存池 / 默认字体);⚠ **必须与 `simulator/lv_conf.h` 同一特性子集**,见 §1.4 第 10 条 | | `simulator/`(main.c / sim/ / lv_conf.h / CMakeLists.txt) | PC | SDL2 模拟器(替代 ui_port,共用全部 UI 源码);支持 `--screenshot` / `--keys ` 无头截图验证 | | `tools/font_gen/`(extract_cn.py / make_font.py)+ `ui_cn_chars.txt` | 工具 | 字符集提取 / 字体生成 | | `tools/menu_gen/gen_menu.py` + `ui_menu_tree.txt` | 工具 | 菜单声明源 → 生成 ui_menu_tab.c/h | @@ -35,7 +35,7 @@ MCU **无物理屏幕**:LVGL 做 160×160 **1bpp 单色离屏渲染**,帧缓 | 屏幕 | 160×160,1bpp(`LV_COLOR_FORMAT_I1`) | `ui_port.h` | | 帧缓冲 | 3200 B(`g_ui_fbuf`,flush 回调整帧 memcpy) | `ui_port.c` | | 绘制缓冲 | **3200 + palette 8 B**(+32 B 余量):I1 索引色缓冲布局为 `[palette 8B][pixel data]`,见 1.4 第 8 条 | `ui_port.c` | -| LVGL 内存池 | 40 KB(`LV_MEM_SIZE`) | `Lib/lv_conf.h` | +| LVGL 内存池 | 16 KB(`LV_MEM_SIZE`,2026-10-15 由 40 KB 收缩;实测峰值 6,568 B = 池的 40%) | `Lib/lv_conf.h` | | 刷新周期 | 10 ms(`ui_port_step(10)` + `osDelay(10)`) | `ui_task.c` | | 推流端口 | 7003(TCP,与 DTU-RemoteLCD 客户端对齐) | `ui_net_sink.h` | | 默认字体 | `ui_font_cn_12`(12px 1bpp,122 汉字/符号 + ASCII 0x20–0x7E,共 217 字形) | `Lib/lv_conf.h` | @@ -43,7 +43,7 @@ MCU **无物理屏幕**:LVGL 做 160×160 **1bpp 单色离屏渲染**,帧缓 ### 1.4 硬约束(改 UI 必须遵守) -1. **RAM 紧张**:新增页面/控件/缓冲前必须核算 LVGL 池(40KB)与任务栈余量(见 `docs/任务规划/基线与待确认.md` §4 内存预算) +1. **RAM 紧张**:新增页面/控件/缓冲前必须核算 LVGL 池(16KB,实测峰值 6,568 B)与任务栈余量(见 `docs/任务规划/基线与待确认.md` §4 内存预算) 2. **新增汉字必须重新生成字体**(`tools/font_gen/` 两步),否则显示空白 3. 文本一律用静态字符串(`lv_label_set_text_static`),不 strdup 4. UI 相关文件为 **UTF-8 带 BOM**(`Drivers/BSP/UI/*`、`ui_task.c`、`Lib/lv_conf.h`),其余源码无 BOM;禁止 PowerShell 整文件重写(编码铁律,见 AGENTS.md) @@ -197,7 +197,7 @@ ENTER/→ 进子框: 销毁最右框(框体连自绘回调整体删除,容器保留)→ 焦点恒在持久容器;父层选中/滚动位原样保留 ``` -- **容器持久、框动态**:行自绘无对象,池内存 = 屏上框体(实测根框 4784 B、最深四级级联 6040 B,远低于 40KB 池) +- **容器持久、框动态**:行自绘无对象,池内存 = 屏上框体(实测根框 5,368 B、最深四级级联 6,040 B、最重页面峰值 6,568 B,远低于 16KB 池) - 同节点再次进入:还原该框上次选中与滚动位(`s_sel/s_scroll`);换节点则清零 - 叶子节点(无子项):ENTER 建空白终点框(w=6、h=2 占位),仅 ESC 可返回 - 深度保护:depth+1 ≥ 4 不再下钻 @@ -215,7 +215,7 @@ ENTER/→ 进子框: - **对象树**:`screen(新) → cont 全屏容器(flat + CLICK_FOCUSABLE,键入口)`;行全部自绘(无子对象),与级联框同一"行=数据"策略 - **生命周期 = 按需建毁**:ENTER 叶子(node 1/3/4)→ `lv_obj_create(NULL)` 新建独立屏 + 容器 → 入默认输入组获焦 → `lv_screen_load` 切屏;ESC → 焦点先回菜单持久容器、切回菜单屏,再删容器+屏。菜单屏对象**保留不销毁**(返回即原样还原) - **导航状态不压层**:菜单 `s_depth/s_sel/s_scroll` 原样保留,内容页只记自身选中行 `s_sel`(每次进入从首行开始) -- **池占用实测**(模拟器 x64,40KB 池):开页峰值 +776 B(屏+容器+事件列表+局部样式),关页后回落;一次性残留 **+80 B**(screens 数组高水位 +8、`lv_screen_load` 给菜单屏设 X/Y 局部样式 +72,均为 LVGL 核心行为、非本模块泄漏) +- **池占用实测**(模拟器 x64,16KB 池):开页峰值 +776 B(屏+容器+事件列表+局部样式),关页后回落;一次性残留 **+80 B**(screens 数组高水位 +8、`lv_screen_load` 给菜单屏设 X/Y 局部样式 +72,均为 LVGL 核心行为、非本模块泄漏) - **flat 样式用常量样式**(`LV_STYLE_CONST_INIT`):属性存静态数组、零池分配;动态 `lv_style_t` 首次 set_prop 会从池分配 `values_and_props` 且永不释放(x64 实测 88 B/个,MCU 同量级),常驻样式一律用常量样式 - **多页面架构(已定稿并拆分,2026-09-09)**:待实现 30 个叶子页;框架(本模块)+ 每页一文件(`ui_page_xxx`,描述符为 extern const 对象)+ 4 个扩展钩子(refresh/count_cb/draw_cb/key_cb),见 `内容页架构.md`;架构第一步(共享"浅 V"装饰 `ui_deco`)与拆分本身均于 2026-09-09 落地,纯重构回归全绿(像素一致、池 4784/4864 B 不变);首个表单/操作页(用户密钥密码页:draw_cb+key_cb + 描述符 title_y/hide_deco/hide_footer/key_intercept)同日落地 @@ -236,8 +236,8 @@ ENTER/→ 进子框: ### 5.3 框按需建/销 -- LVGL 池仅 40KB(`LV_MEM_SIZE`),全量预建 42 个节点的所有页面必然爆内存 -- 只建"当前路径"上的框:行自绘无对象,池内存 = 屏上框体(实测根框 used=4784 B、最深四级级联 used=6040 B,远低于 40KB 池) +- LVGL 池仅 16KB(`LV_MEM_SIZE`,2026-10-15 由 40KB 收缩),全量预建 42 个节点的所有页面必然爆内存 +- 只建"当前路径"上的框:行自绘无对象,池内存 = 屏上框体(实测根框 used=5,368 B、最深四级级联 used=6,040 B、网口设置保存态峰值 used=6,568 B = 池的 40%) - 返回即 `lv_obj_delete` 释放,内存随时回落到根框水位 ### 5.4 持久容器是唯一焦点对象 @@ -249,7 +249,7 @@ ENTER/→ 进子框: ### 5.5 条目行自绘而非对象化 -- 每行对象有"机制税":`lv_obj_t` 结构体 + 样式向量 + 子 label(文本/箭头)状态,约 2 KB/行;屏上 2~3 框 × 多行会逼近 40 KB 池 → 行降级为**数据**(节点下标 `s_row_node[4][16]`,1 B/行) +- 每行对象有"机制税":`lv_obj_t` 结构体 + 样式向量 + 子 label(文本/箭头)状态,约 2 KB/行;屏上 2~3 框 × 多行会逼近 16 KB 池 → 行降级为**数据**(节点下标 `s_row_node[4][16]`,1 B/行) - 框体 `LV_EVENT_DRAW_MAIN` 自绘:选中行整行白带(`lv_draw_rect`),每行文本/箭头经 `lv_draw_label`(与 label 同字体光栅化路径,像素级一致) - 行不需要对象语义(焦点/点击/按压反馈):输入由容器统一处理,行是纯展示 - 自绘同时根除了旧对象模型的两类问题:样式继承"白底白字"坑(2026-09-02 实际踩坑)、行对象池内存膨胀 diff --git a/docs/UI设计/mockups/01_主界面_说明.md b/docs/UI设计/mockups/01_主界面_说明.md index 792cf0ad..bd347d79 100644 --- a/docs/UI设计/mockups/01_主界面_说明.md +++ b/docs/UI设计/mockups/01_主界面_说明.md @@ -74,4 +74,4 @@ 3. 状态行 = `lv_label`(x=17, y=20,文本由 App 层供给,Q4) 4. 顶部装饰 = 开口浅 V(4 段折线:(7,7)→(16,17)→(145,17)→(158,7)):LVGL 可用 3 条 `lv_obj` 线段拼合(斜线用 `lv_line`),或预渲染 1bpp 位图 `lv_image`(更省对象、更精确) 5. 1bpp 禁区(本设计均未使用):圆角 / 阴影 / 透明度 / 渐变 / 多色 -6. 内存:根页 = 1 列表容器 + 7 行 ×(行 obj + 2 label)+ 状态 label + 装饰 ≈ 20+ 个对象,为最高频页面,P1 需核算 40KB LVGL 池占用 +6. 内存:根页 = 1 列表容器 + 7 行 ×(行 obj + 2 label)+ 状态 label + 装饰 ≈ 20+ 个对象,为最高频页面,P1 需核算 16KB LVGL 池占用 diff --git a/docs/UI设计/内容页架构.md b/docs/UI设计/内容页架构.md index 4f225307..b6b4ef9d 100644 --- a/docs/UI设计/内容页架构.md +++ b/docs/UI设计/内容页架构.md @@ -419,14 +419,14 @@ extern const content_page_def_t ui_page_device_info_desc; /* 描述符(Flash - 跨任务数据(ADC/网络/RTC/存储)沿用既有约定:生产者任务写快照(原子/队列), UI 任务在钩子里读快照;框架与页面模块**不加锁** -**内存(实测,40 KB LVGL 池)** +**内存(实测,16 KB LVGL 池)** | 项 | 值 | 说明 | |---|---|---| | 框架常驻 RAM | ~20 B | `s_open/s_sel/s_scr/s_cont` 4 变量 | | 开页峰值 | +776 B | 屏 + 容器 + 事件列表 + 常量样式引用 | | 关页回落 | 基线 +80 B 一次性残留 | screens 数组高水位 +8、`lv_screen_load` 菜单屏局部 X/Y 样式 +72(LVGL 核心行为,非泄漏;分解见 `菜单页面设计.md` §9.5) | -| 池水位门禁 | 4784 B / 4864 B | 菜单基线 / 开页-关页往返(回归脚本常量) | +| 池水位门禁 | 5368 B / 5448 B | 菜单基线 / 开页-关页往返(回归脚本常量,2026-10-11 随 LVGL 控件裁剪由 4784/4864 上移;**与池总大小无关**,16 KB 池下实测值不变) | | 描述符/行表/标题 | Flash const | 不进 LVGL 池;每页增量 = 描述符 ~40 B + 行表 + 代码 | | 动态行栈缓冲 | 2×32 B | 仅绘制期存在 | diff --git a/docs/UI设计/菜单页面设计.md b/docs/UI设计/菜单页面设计.md index 05857736..6b53f772 100644 --- a/docs/UI设计/菜单页面设计.md +++ b/docs/UI设计/菜单页面设计.md @@ -115,7 +115,7 @@ sequenceDiagram ``` - 日志标记:`UI menu init: OK`(uiTask 打印)——`@test` 回归可作 Required 标记。 -- LVGL 池实测(模拟器 40 KB 池):根框建完 `used=4784 B`(旧行对象实现为 13312 B,自绘 + 容器收敛省 8.3 KB);最深四级级联 `used=6040 B`(增量 1256 B = 3 个子框体),远小于池余量。 +- LVGL 池实测(模拟器 16 KB 池):根框建完 `used=5368 B`(旧行对象实现为 13312 B,自绘 + 容器收敛省 8.3 KB);最深四级级联 `used=6040 B`(增量 1256 B = 3 个子框体),远小于池余量。 ### 1.6 层间不变量(每层"绝不做") @@ -771,8 +771,8 @@ ESC/LEFT : d>0 → menu_back() (根框 d=0 不响 | 样式 `s_style_frame/scrollbar`(动态)+ flat 常量样式(Flash,零池占用 §9.5) | 显示层 | ~200 B | | **小计** | | **~0.35 KB**(逻辑层 17 B + 显示层 ~0.33 KB) | -**LVGL 池(动态,`LV_MEM` 40 KB 预算内)**:行自绘无对象,池只占**屏上框体**(2~3 个 `lv_obj`)+ 根框装饰/状态行, -典型 ≈ **1~2 KB**,仍远小于 40 KB(池可再缩减,见 `docs/内存管理/RAM使用分析与优化方案.md` §1.1;开机实测 UI 池仅用约 3 KB)。 +**LVGL 池(动态,`LV_MEM` 16 KB 预算内)**:行自绘无对象,池只占**屏上框体**(2~3 个 `lv_obj`)+ 根框装饰/状态行, +典型 ≈ **1~2 KB**,仍远小于 16 KB(池已于 2026-10-15 由 40 KB 缩到 16 KB,见 `docs/内存管理/RAM使用分析与优化方案.md` §1.1;真机开机实测 `menu build done: used=3192`)。 --- @@ -856,7 +856,7 @@ bool ui_content_page_is_open(void); /* 查询是否有内容页 - 节点→页映射在逻辑层 `ui_menu.c`(`menu_content_page_for(node)`):node 1 → `CONTENT_PAGE_DEVICE_INFO`、3 → `AC`、4 → `DC`;其余叶子无内容页(进空白终点框)。需密码叶子(行尾 ` #P`,如 node 8 参数;密码检查先于内容页判断,带内容页的需密码叶子同样先经密码页)先 arm + 开 `CONTENT_PAGE_PASSWORD`,确认一致后回调(携授予权限) **先关页再续做进入动作**(有内容页→开内容页携权限,页面经 `ui_content_page_is_editable()` 判只读/可编辑);无密码叶子直接开(默认只读) -### 9.5 内存预算(模拟器 x64 实测,40 KB 池) +### 9.5 内存预算(模拟器 x64 实测,16 KB 池) | 时刻 | LVGL 池 used | 说明 | |---|---|---| diff --git a/docs/产品需求/产品需求设计文档.md b/docs/产品需求/产品需求设计文档.md index 7bf9540f..6e472d49 100644 --- a/docs/产品需求/产品需求设计文档.md +++ b/docs/产品需求/产品需求设计文档.md @@ -630,7 +630,7 @@ mac=00:00:00:00:00:00 > **定栈规则**:改栈或加大栈内局部变量前后**必须**重跑 `scripts/stack_report.py` > (Keil 静态调用图 × 串口 `[STACK]` 水位,判定取两者更坏者)。 -> **LVGL 内存池** 40 KB(静态占用);**FreeRTOS 堆** 48 KB(CCM)。 +> **LVGL 内存池** 16 KB(静态占用,2026-10-15 由 40 KB 收缩);**FreeRTOS 堆** 56 KB(CCM)。 ### 7.4 可维护性(测试与门禁) @@ -695,7 +695,7 @@ cfg-file-unit-test、test-suite-switch、gitlint。 | 屏幕尺寸 | 160 × 160 px | | 色深 | 1bpp 单色(MSB 优先),帧缓冲 3200 B | | 渲染 | LVGL 离屏渲染到 `g_ui_fbuf`;MCU 无物理屏,经 TCP 7003 推流 | -| 内存池 | `LV_MEM_SIZE` = 40 KB | +| 内存池 | `LV_MEM_SIZE` = 16 KB(2026-10-15 由 40 KB 收缩,实测峰值占 40%) | | 主题 | dark mono(单色深色) | | 刷新 | `uiTask` 10 ms 循环 | | 绘制缓冲 | I1 draw buffer 需 +8 B palette 头 | diff --git a/docs/任务规划/README.md b/docs/任务规划/README.md index 6eda2190..b633129f 100644 --- a/docs/任务规划/README.md +++ b/docs/任务规划/README.md @@ -102,7 +102,7 @@ |---|---|---| | **W1 采集+保护收尾** | **里程碑制 + 并行道**:M1 台架闭环 ✅ 达成(B6 ✅→N-33 ✅)∥ M2 功能面(代码道 B7 ✅→{B8∥B9})→ M3 送检(B10,Fixed Date)——任务卡/判据/写范围见 `W1-采集保护收尾.md` | **用户决定(2026-10-12):先把 ADC 采集、保护等相关功能完成**。B4/B5 代码已落地但未台架闭环、功能面不完整(定值不落盘 / 无频率保护 / 无动作记录 / 栈两处 FAIL)——本波收口到"可用 + 可验收";真约束只有**台架**与**送检窗口**两个稀缺资源,其余任务按写范围切分并行 | | **W2 数据正确性** | **N-21** dhara `is_free`/`copy`(报告 N-12/13) | 低概率、后果重(数据丢失);修复须配主机侧移植层测试,一并补报告"覆盖率"缺口 | -| **W3 预算与内存** | **N-27 后半**:R-9 池 40→16 KB → R-2 后半(54 KB→CCM)→ R-3/R-4/R-6 逐项评估(**R-5 已判定不可行**,见 `登记簿.md`) | 依赖 W1 三件套产出的台账数据;**B5 堆扩容后 CCM 只剩 7,904 B**(56 KB 堆),R-2 后半更被堵死(解锁链见 `docs/内存管理/RAM预算台账.md` §3) | +| **W3 预算与内存** | **N-27 后半**:~~R-9 池 40→16 KB~~ ✅(2026-10-15)→ **缩堆 56→32 KB** → R-2 后半(**29.5 KB→CCM**)→ R-3/R-4/R-6 逐项评估(**R-5 已判定不可行**,见 `登记簿.md`) | **R-9 已落地**:SRAM1 由 74.9% 降到 **56.1%**,待迁体量 54,146 → **29,570 B**;**瓶颈换到 CCM——只剩 7,904 B**(56 KB 堆),⚠ B5 是反向扩容,缩回 32 KB 前必须重跑 `stack_report.py` + 上板 `min_ever` 复测(解锁链见 `docs/内存管理/RAM预算台账.md` §3) | | **W4 结构债** | **C6(S-3)→ C3(N-2+N-24)→ C1 net 层跨上下文收口 → C5(含 N-23 残余)→ N-22/N-25/N-28**(详表 `排期详述.md` §1) | 无紧急,按性价比排:C6 灭 IWDG 误复位机制根源;C1 消化 E-25/E-27/E-28(原 ACCEPT+SEND pending 化已收口),体量大、验证重,放后(N-23 钩子已落地 2026-10-12,残余并入 C5) | | **W5 验证常态** | P1-10 收尾 · T-7 CI · D2 N-3 · D5 N-16 · D6 N-17 · D7 串口助手 · S12 · S1-c · A-5(详表 `排期详述.md` §2) | 窗口性,逢机顺带,不单独排期 | | **W6 UI 扩展** | U4 残余(golden + 字符集差集挂门禁)→ U1 令牌层 → U2/U3 → **27 页**落地 | 产品驱动、体量大,需单独立项(顺序按 `docs/问题记录/项目审查与优化建议报告.md` §6.5);**本轮不排期(2026-10-11 已确认)** | @@ -118,7 +118,7 @@ W1 收尾 台架道: B6✅→N-33✅(M1 ✅ 2026-10-12) ∥ 代码道: B7✅ ↓ W2 数据 N-21 dhara is_free/copy(配主机侧移植层测试) ↓ -W3 预算 R-9 池瘦身 → R-2 后半(54KB→CCM)→ R-3/R-4/R-6 评估 +W3 预算 R-9 池瘦身 ✅ → 缩堆 56→32KB → R-2 后半(29.5KB→CCM)→ R-3/R-4/R-6 评估 ↓ W4 结构 C6(S-3) → C3(N-2/N-24) → C1 net 层跨上下文收口 → C5(含 N-23 残余) → N-22/25/28 ↓ diff --git a/docs/任务规划/登记簿.md b/docs/任务规划/登记簿.md index 65c6a4d2..0acd2f35 100644 --- a/docs/任务规划/登记簿.md +++ b/docs/任务规划/登记簿.md @@ -46,7 +46,7 @@ | N-23 残余 | **钩子已落地**(2026-10-12 `registration-check` 挂 pre-commit,存量缺口走 `KNOWN_GAPS` 白名单只 WARN);**残余** = 白名单只剩 `rs485_task.c` 一条**刻意**缺口(E-59 门禁下随 B2 暂缓,见 `暂缓-RS485.md`);`app_cfg.h` 未接线开关(C5)。⚠ `lftpd_log.c` 已随 N-19 删除并入 `lftpd_auth.c`(2026-10-11),不再是缺口 | 白名单不清 = "写了不编译"盲区仍在 | 随 B2 一并出白名单(`--strict` 供审计复跑);开关随 C5 处置 | W4(C5) | 报告 N-06 残余 + 报告 S-3 | | N-24 | 日志每条 `f_open+f_write+f_close`(`sys_log_mcu.c:123-140`) | 每条日志 2–4 次 NAND 页读写;UI 翻记录逐条 open/seek/close | 与 C3 日志管线合并评估(现仅 `FIL` static 化省 528 B 栈) | W4 | 报告 N-16(**≠ 本登记簿 N-16**) | | N-25 | 性能/资源建议簇:PASV 每次新建监听;PHY 强制 100M 无自协商回退;`NET_MAX_SOCKETS 8` 与单连接语义不符;`HAL_Delay(1)` 阻塞 DE(**DE 项随 B2 暂缓,见 `暂缓-RS485.md`**)。⚠ 原列的 `NET_RECV_TIMEOUT_MS 30000` 已随 2026-10-13「recv 语义主流化」删除(挂起 recv 模型),不再是待办 | 次级性能/语义一致性 | 逐项见 `../问题记录/项目审查与优化建议报告.md` §4.2/§4.7,随窗口评估 | W4 | 报告 N-09/PHY/P3-x/rs485 | -| N-27 | **内存优化簇**:LVGL 池 + UI 两份缓冲 54,146 B 仍在 SRAM1,CCM 只剩 **7,904 B**(`RW_CCM` 上限 65,280 − 57,376;2026-10-12 B5 堆扩容 48→56 KB 后) | R-2 后半段(迁 CCM)被堵死 → 后续功能预算看不清 | **三件套 ✅ 2026-10-12 落地**(`ram_baseline.py` 四模式 + `ram_baseline.tsv` 趋势 + `docs/内存管理/RAM预算台账.md` + `mem-budget` 钩子 92% 门禁)→ **W3** 再做 R-9(`LV_MEM_SIZE` 40→16 KB,池水位门禁数据已具备)→ R-2 后半;R-3/R-4/R-6 逐项评估(R-6 前置已满足)。⚠ **R-5 已判定不可行、从本簇剔除**:CMSIS-RTOS2 `freertos_os2.h` 有 `#if (configMAX_PRIORITIES != 56) #error`,改小直接编译失败(为 480 B 改第三方包装层不划算) | **W3** | 报告 R-2 剩余/R-3/R-4/R-6/R-9/§5.5 | +| N-27 | **内存优化簇**:**R-9 ✅ 已落地(2026-10-15)**——`LV_MEM_SIZE` 40→16 KB → SRAM1 由 74.9% 降到 **56.1%(余 57,504 B)**;待迁 CCM 体量随之由 54,146 B 降到 **29,570 B**,而 CCM 只剩 **7,904 B**(`RW_CCM` 上限 65,280 − 57,376) | R-2 后半段(迁 CCM)**只差"缩堆"一步**:堆 56→32 KB 可腾 24 KB → CCM 余 ≈31.9 KB ≥ 29,570 B。⚠ 2026-10-12 B5 是**反向**扩容至 56 KB(9 任务栈 + 队列实测需要),缩回前必须重跑 `stack_report.py` + 上板 `min_ever` 复测 | **三件套 ✅ 2026-10-12 落地**(`ram_baseline.py` 四模式 + `ram_baseline.tsv` 趋势 + `docs/内存管理/RAM预算台账.md` + `mem-budget` 钩子 92% 门禁)→ **R-9 ✅ 2026-10-15**(实测峰值 6,568 B = 池的 40%,水位表见 `../内存管理/RAM使用分析与优化方案.md` §2.6.2)→ **W3** 缩堆 → R-2 后半;R-3/R-4/R-6 逐项评估(R-6 前置已满足)。⚠ **R-5 已判定不可行、从本簇剔除**:CMSIS-RTOS2 `freertos_os2.h` 有 `#if (configMAX_PRIORITIES != 56) #error`,改小直接编译失败(为 480 B 改第三方包装层不划算) | **W3** | 报告 R-2 剩余/R-3/R-4/R-6/§5.5 | | N-28 | Flash 死代码评估簇:剩余 ≈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 TU(F-1/F-2/F-3 已闭环,省的是编译时长) | 编译时长 + 文档噪音 | 逐项实测再关;⚠ `mask`/`mask_rect`/`grad` 是文字与矩形绘制必经路径,**不可盲关** | W4 | 报告 F-4/F-5/F-6 | | N-29 | **仓库膨胀**:`.git` **235.3 MB**(2026-10-14 实测;本行建档时记 149 MB)+ 手册图片 36.3 MB(受控文件合计仅 79.3 MB,`.git` 已是它的 3 倍) | clone/备份慢,且持续恶化 | 降采样 / 拆 submodule / LFS 三选一 **+ 一次 `git filter-repo` 历史重写**(三项都不解决历史膨胀)。**已决策(2026-10-11):暂不处理、登记观察**——推送/协作前重评(届时未推送提交更少、重写代价最低) | **W7** | 报告 D-1 | | U 簇 | UI 标准化:无令牌层/组件库/页注册表(手写 `case` 仍在),**27 页**未落地;golden 未挂 pre-commit、字符集差集检查仍是约定 | 新增一页成本仍是 8 处手工改动 | 按 `../问题记录/项目审查与优化建议报告.md` §6.5:U4 残余(两脚本入门禁,可逢窗口顺带)→ U1 令牌层 → U2/U3 → 页面落地;U4 golden 可直接当 U1 验收工具。**已确认(2026-10-11):本轮不排期**,等功能主线出结果后按产品需求启动 | W6 | 报告 U1~U5 | diff --git a/docs/内存管理/RAM使用分析与优化方案.md b/docs/内存管理/RAM使用分析与优化方案.md index f664d493..1a24d6c4 100644 --- a/docs/内存管理/RAM使用分析与优化方案.md +++ b/docs/内存管理/RAM使用分析与优化方案.md @@ -37,7 +37,7 @@ | 区域 | 用量 | 容量 | 占比 | |---|---|---|---| | ER_IROM1 | 231,084 B (225.67 KB) | 1,048,576 B (1024 KB) | 22.0% | -| RW_IRAM1 | 98,144 B (95.84 KB) | 131,072 B (128 KB) | 74.9% | +| RW_IRAM1 | 73,568 B (71.84 KB) | 131,072 B (128 KB) | 56.1% | | RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% | | **Flash (ROM 合计)** | **232,196 B (226.8 KB)** | 1,048,576 B (1024 KB) | **22.1%** | @@ -57,7 +57,7 @@ | # | 大小 (B) | 对象文件 | 内容 | |---|---|---|---| -| 1 | 40,960 | `lv_mem_core_builtin.o` | LVGL 静态内存池 `ucMemPool[LV_MEM_SIZE]`(§2.6.2) | +| 1 | 16,384 | `lv_mem_core_builtin.o` | LVGL 静态内存池 `work_mem_int[LV_MEM_SIZE]`(§2.6.2,**2026-10-15 由 40,960 收到 16,384**) | | 2 | 6,746 | `ui_net_sink.o` | 远程显示收发缓冲 `g_tx[3209]` + `g_lcdmem_data[3204]` + `g_rx[64]` | | 3 | 6,408 | `ui_port.o` | `g_ui_fbuf[3200]` + `g_ui_draw_buf[3200]`(160×160 1bpp 帧缓冲) | | 4 | 6,200 | `nand_ftl.o` | `s_page_buf`/`s_cache_buf`/`s_copy_buf` 各 2,048 B + dhara 结构(**DMA**) | @@ -133,7 +133,7 @@ | 根因 | 占用 | 可回收性 | |---|---|---| -| ① LVGL 内存池 40KB 对 1bpp 小屏菜单过大 | 40KB | 高(目标 8~16KB;**开机实测 UI 仅用 ~3KB**,见 §2.6.2) | +| ① LVGL 内存池 40KB 对 1bpp 小屏菜单过大 | 40KB → **16KB ✅ 2026-10-15** | 高(目标 8~16KB;**开机实测 UI 仅用 ~3KB**,见 §2.6.2) | | ② 任务栈"往大了给",无实测依据 | 38,912 B(38 KB 现值,§2.6.1) | 高(§0.6 已有实测:uiTask 3.8/16KB、netTask 0.6/4KB → 阶段 1.3 缩减) | | ③ 64KB CCM 曾完全未利用,缓冲挤在主 SRAM | — | ✅ 2026-09-21 已启用(堆迁入 CCM),CCM 余量 ~16KB | @@ -163,14 +163,26 @@ > lftpd 会话期间还会 `pvPortMalloc` 路径缓冲/DIR(<1 KB 瞬态)。**堆余量底线按 ≥4 KB 守住**(`min_ever` 是全负载历史最低点,比 `free` 更有约束力)。 -#### 2.6.2 LVGL 内存池(40 KB) +#### 2.6.2 LVGL 内存池(16 KB,2026-10-15 由 40 KB 收缩) | 参数 | 值 | 来源 | |---|---|---| -| `LV_MEM_SIZE` | 40,960 B (40 KB) | `Lib/lv_conf.h`(模拟器 `simulator/lv_conf.h` 需同步) | +| `LV_MEM_SIZE` | 16,384 B (16 KB) | `Lib/lv_conf.h`(模拟器 `simulator/lv_conf.h` 同步,`lv-conf-sync` 门禁比对生效值) | | 分配器 / 色深 / OS 抽象 | TLSF / 1 (1bpp) / 无 | 同上 | -池内装 UI 对象树、样式、字符串、动画对象,均为运行时分配;TLSF 元数据开销约 10~15%,净可用 ≈34~36 KB。**开机实测**(2026-09-21 日志):`menu build start: total=39476 free=37248` → `menu build done: used=2976`,即当前 UI 仅占约 **3 KB**——这是 §1.1 把池缩到 8~16 KB 的直接依据。静态无法估算池占用,需运行时 `LV_USE_MEM_MONITOR`(`lv_mem_monitor_t`)。 +池内装 UI 对象树、样式、字符串、动画对象,均为运行时分配;TLSF 元数据开销约 10~15%。**实测水位**(池水位门禁常量 + 开机日志): + +| 测点 | used | 占 16 KB 池 | 来源 | +|---|---|---|---| +| 菜单根框建完 | 5,368 B | 33% | `simulator/tests/check_menu_rows.py` 的 `MENU_POOL_BASELINE` | +| 开页-关页往返(一次性残留 +80 B) | 5,448 B | 33% | `simulator/tests/check_content_pages.py` 的 `POOL_AFTER_ROUNDTRIP` | +| **最重页面峰值**(网口设置保存态) | **6,568 B** | **40%** | 同上脚本的 `pool after keys: used=… free=7,080` | +| **真机开机**(2026-10-15,16 KB 池,`@test` PASS) | **3,192 B** | 21% | 台架日志 `menu build done: total=15020 free=11828 used=3192` | +| 真机开机(2026-09-21,40 KB 池时代) | 2,976 B | — | `ui_menu.c` 的 `lv_mem_monitor` 日志 `menu build done: used=2976` | + +⚠ 16,384 B 建池后 `lv_mem_monitor` 报的 `total` 是 **15,020 B**——TLSF 控制块与对齐元数据吃掉 ≈1,364 B(约 8%);核算池余量用 15,020,不是 16,384。 + +→ 16 KB 留 ≈2.5× 余量(模拟器最重页面 6,568 B / 真机菜单根框 3,192 B)。**再降到 8 KB 需先跑全量 UI 回归(PC + 真机)确认无分配失败**;池占用静态估不出来,只能靠运行时 `lv_mem_monitor`(`lv_mem_monitor_t`)。 #### 2.6.3 C 库堆/栈(microlib) @@ -413,13 +425,12 @@ FreeRTOS 堆不变(`free=17248 / min_ever=9312`)。验证链:`@build` 0/0 ### 阶段 1:静态大项削减(收益最大,3~5 天) -#### 1.1 LVGL 内存池 40KB → 16KB → 8KB(省 24~32KB) +#### 1.1 LVGL 内存池 40KB → 16KB(✅ 已执行 2026-10-15,省 24,576 B)→ 8KB(待评估) - 依据:屏幕仅 160×160×1bpp,菜单为轻量控件树;LVGL 内存池只装对象/样式/字符串缓冲,与像素缓冲无关。40KB 来自模板默认值,无适配依据。 -- 做法:`Lib/lv_conf.h:34` `LV_MEM_SIZE` 先改 `(16*1024)`,跑 UI 全量回归(PC 模拟器 + 实机),无 `LV_ASSERT_MALLOC`/分配失败再试 8KB。 -- 同步在 `simulator/lv_conf.h:26` 保持一致(两端共用 UI 源码,配置必须同步)。 -- 验证:UI 全菜单遍历 + 动画/滚动/输入框 + 中文字体渲染无缺字;LVGL 内置内存监控(`LV_USE_MEM_MONITOR`,调试构建开启)观察池占用峰值。 -- 风险:个别复杂控件/长字符串可能吃紧 → 若 8KB 不够就停在 16KB,不勉强。 +- **已落地**:`Lib/lv_conf.h` 与 `simulator/lv_conf.h` 同步改 `(16 * 1024)`——两端必须同值,`lv-conf-sync` 门禁按**生效值**比对。实测水位见 §2.6.2(最重页面峰值 6,568 B = 池的 40%);`@build` 0 错 0 警,ZI 精确 −24,576 B → SRAM1 由 74.9% 降到 **56.1%**(余 57,504 B)。 +- 验证口径(本次即用它):`simulator/tests/check_menu_rows.py`(菜单 8 状态像素 + 池基线 5,368 B)+ `simulator/tests/check_content_pages.py`(11 页黄金参考逐字节 + 往返池水位 5,448 B)+ 真机 `menu build done: used=` 日志。 +- **再降 8KB 的前置**:全量 UI 回归(PC 模拟器 + 真机)无 `LV_ASSERT_MALLOC`/分配失败;若 8KB 不够就停在 16KB,不勉强。 #### 1.2 启用 CCM RAM(+64KB 预算空间) diff --git a/docs/内存管理/RAM预算台账.md b/docs/内存管理/RAM预算台账.md index aa95726e..25cb25b5 100644 --- a/docs/内存管理/RAM预算台账.md +++ b/docs/内存管理/RAM预算台账.md @@ -18,13 +18,13 @@ | ER_IROM1 (Flash) | 231,084 B | 1,048,576 B | 22.0% | 参考 | | RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS | | UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 | -| RW_IRAM1 | 98,144 B | 131,072 B | 74.9% | PASS | +| RW_IRAM1 | 73,568 B | 131,072 B | 56.1% | PASS | | 子系统(静态 RW+ZI;除 RTOS 堆在 CCM 外均在 SRAM1) | 现值 | 配额 | 状态 | |---|---|---|---| | 网络 | 13,690 B | 12 KB | ⚠ 超 1.4 KB | | 存储 | 7,056 B | 12 KB | OK | -| UI | 54,988 B | 16 KB | ⚠ 超 37.7 KB | +| UI | 30,412 B | 16 KB | ⚠ 超 13.7 KB | | RTOS 堆 | 57,376 B | 56 KB | 堆=配额 | | 应用 | 13,247 B | 8 KB | ⚠ 超 4.9 KB | | 其它 | 9,184 B | — | 参考 | @@ -56,7 +56,7 @@ UI 16 KB / RTOS 堆 56 KB / 应用 8 KB**。归集口径(按 `.map` 对象名 **现状判读(2026-10-12 建档)**: -- **UI ⚠ 超配额 = W3 的既有债务,不是新增违规**:LVGL 池 40 KB(R-9 目标 16 KB)+ 帧/远显缓冲 +- **UI ⚠ 超配额 = W3 的既有债务,不是新增违规**:LVGL 池 16 KB(R-9 已于 2026-10-15 落地,由 40 KB 收缩)+ 帧/远显缓冲 ≈12.8 KB(R-2 目标迁 CCM)——配额 16 KB 描述的是 **R-9/R-2 落地后的目标态**(排期见 `docs/任务规划/README.md` §2 W3)。 - **RTOS 堆 = 配额本身**:56 KB 即 `configTOTAL_HEAP_SIZE`(2026-10-12 B5 由 48 KB 扩容,见 §6; @@ -71,8 +71,8 @@ UI 16 KB / RTOS 堆 56 KB / 应用 8 KB**。归集口径(按 `.map` 对象名 |---|---|---| | CCM 容量 | `RW_CCM` Max 见受管区 | scatter `RW_CCM`;**仅 CPU 可达,DMA 不可达**(RM0090 §3.1.1) | | 现占用 | 仅 FreeRTOS 堆 `heap_4`(56 KB,2026-09-21 迁入;2026-10-12 B5 扩容 48→56 KB) | 其余候选全部仍在 SRAM1 | -| 现余量 | 受管区 RW_CCM 容量 − 占用(≈8 KB) | **不够** R-2 后半的 UI 侧待迁体量(R-9 后仍 ≈30 KB) | -| R-2 后半解锁链 | ① R-9:`LV_MEM_SIZE` 40→16 KB(待迁体量降到 ~30 KB)→ ② 堆 56→32 KB(`docs/内存管理/RAM使用分析与优化方案.md` §2.2,再腾 24 KB;⚠ 2026-10-12 B5 反向扩容至 56 KB——9 任务栈 + 队列实测需要,缩回 32 KB 前必须重跑 `stack_report.py` + 上板 min_ever 复测)→ ③ CCM 余 ~32 KB ≥ 30 KB → 迁入 | **顺序不可颠倒**;每步前后跑 `stack_report.py` / `ram_report.py`(铁律) | +| 现余量 | 受管区 RW_CCM 容量 − 占用 = **7,904 B** | **不够** R-2 后半的 UI 侧待迁体量(R-9 后 **29,570 B**) | +| R-2 后半解锁链 | ① **R-9 ✅ 2026-10-15**:`LV_MEM_SIZE` 40→16 KB → 待迁体量 54,146 → **29,570 B**(SRAM1 74.9% → **56.1%**,实测池峰值 6,568 B)→ ② **← 现在卡在这一步**:堆 56→32 KB(`docs/内存管理/RAM使用分析与优化方案.md` §2.2,再腾 24 KB;⚠ 2026-10-12 B5 反向扩容至 56 KB——9 任务栈 + 队列实测需要,缩回 32 KB 前必须重跑 `stack_report.py` + 上板 min_ever 复测)→ ③ CCM 余 ≈31.9 KB ≥ 29,570 B → 迁入 | **顺序不可颠倒**;每步前后跑 `stack_report.py` / `ram_report.py`(铁律) | | 迁入前置 | 目标缓冲必须过 DMA 可达性清单(`内存内容分配策略.md`) | 违例 = 静默传错(N-11 同类坑);克隆芯片 CCM 可靠性风险见 `docs/内存管理/RAM使用分析与优化方案.md` §7 | ## 4. 趋势基线(`scripts/ram_baseline.tsv`) diff --git a/docs/内存管理/内存内容分配策略.md b/docs/内存管理/内存内容分配策略.md index 28f75297..1cdb8e11 100644 --- a/docs/内存管理/内存内容分配策略.md +++ b/docs/内存管理/内存内容分配策略.md @@ -80,7 +80,7 @@ CCM 64KB 只能容纳 {FreeRTOS 堆 48KB} 与 {LVGL 集合 ~52.5KB} 之一(两 | 内存块 | 大小 | 损坏后果 | 自愈能力 | DMA 不可达的收益 | |---|---|---|---|---| | FreeRTOS 堆(调度器控制块 + 6 任务栈 + 队列/定时器) | 48KB | 调度静默损坏 = HardFault/复位循环/死系统,**最坏结果** | 无 | **大**:DMA 野地址传输到不了 CCM(表现为"数据没到",可检测),调度器字节免疫 DMA 类故障 | -| LVGL 集合(40KB 池 + 帧 4 份副本) | ~52.5KB | 最坏 = 屏幕花屏 | **强**(每帧全量重渲染;已实证的 1 字节越界由 32B 保护带兜住) | 无(永不用 DMA,放 CCM 只会关上 DMA 门) | +| LVGL 集合(16KB 池 + 帧 4 份副本) | ~28.5KB | 最坏 = 屏幕花屏 | **强**(每帧全量重渲染;已实证的 1 字节越界由 32B 保护带兜住) | 无(永不用 DMA,放 CCM 只会关上 DMA 门) | 分配规则:**"损坏即致命 + 无自愈"的内存进保护区;"自愈型"内存留 SRAM。** @@ -102,7 +102,7 @@ CCM 64KB 只能容纳 {FreeRTOS 堆 48KB} 与 {LVGL 集合 ~52.5KB} 之一(两 ### 5.5 一并否决的混合方案 -- 仅 LVGL 池(40KB)进 CCM、堆回 SRAM:40+48=88KB > 64KB 无法两全,调度器失去保护区 → 劣于方案 A +- 仅 LVGL 池(16KB)进 CCM、堆回 SRAM:16+56=72KB > 64KB 无法两全,调度器失去保护区 → 劣于方案 A - 仅帧副本进 CCM:帧副本是自愈型内容,放 CCM 只关 DMA 门、零防护收益 → 无意义 ### 5.6 若未来确需改判 @@ -117,7 +117,7 @@ CCM 64KB 只能容纳 {FreeRTOS 堆 48KB} 与 {LVGL 集合 ~52.5KB} 之一(两 | RW_IRAM1(SRAM) | 131,072B (128KB) | 95,816B (~93.6KB, 73.1%) | 35,256B (~34.4KB) | 全部静态(除堆),含 LVGL 集合 53,805B | | RW_CCM | 65,536B (64KB) | 57,376B (56KB, 87.9%) | 8,160B (~8KB) | **仅** `heap_4.o`(ucHeap 56KB,2026-10-12 B5 扩容) | -LVGL 集合明细(均在 SRAM):LVGL 池 40,960B(`lv_mem_core_builtin.o`,`LV_MEM_ADR=0` 内部静态)+ `g_ui_draw_buf` 3,232B(3200+32 保护带)+ `g_ui_fbuf` 3,200B + `g_tx` 3,209B + `g_lcdmem_data` 3,204B。 +LVGL 集合明细(均在 SRAM):LVGL 池 16,384B(`lv_mem_core_builtin.o`,`LV_MEM_ADR=0` 内部静态)+ `g_ui_draw_buf` 3,232B(3200+32 保护带)+ `g_ui_fbuf` 3,200B + `g_tx` 3,209B + `g_lcdmem_data` 3,204B。 产品构建比套件构建少 testTask/测试缓冲(约 40KB 级),SRAM 余量更大。数字以 map 实测为准,布局变更后必须重测并更新本表。 diff --git a/docs/子系统设计/运行与自检记录设计说明.md b/docs/子系统设计/运行与自检记录设计说明.md index 184fef1d..045fe0b7 100644 --- a/docs/子系统设计/运行与自检记录设计说明.md +++ b/docs/子系统设计/运行与自检记录设计说明.md @@ -49,7 +49,7 @@ ### 2.4 RAM/LVGL 池预算(依据 `docs/内存管理/RAM使用分析与优化方案.md`) -- FreeRTOS 堆真实余量 ≈ **11~12KB**;LVGL 池 40KB 静态,菜单常驻占用 ≈5KB。 +- FreeRTOS 堆真实余量 ≈ **11~12KB**;LVGL 池 16KB 静态,菜单常驻占用 ≈5KB。 - 本设计新增 RAM:**全部 < 1KB**(§7),不新增任务、不动任务栈。 ## 3. 网络调研摘要(R6) @@ -226,7 +226,7 @@ defaultTask 挂载成功、排空待发队列**之后**写一条: | 项 | 大小 | 位置 | |---|------|------| | sys_log MCU:启动前待发数组 8×12B + 队列 8×12B + 状态 | ≈300B(静态)+ ≈150B(FreeRTOS 堆,消息队列) | .bss / heap_4 | -| ui_standby:待机屏 + 容器 LVGL 对象(常驻) | ≈300B | LVGL 池(40KB,当前占用 ≈5KB) | +| ui_standby:待机屏 + 容器 LVGL 对象(常驻) | ≈300B | LVGL 池(16KB,当前占用 ≈5KB) | | 记录页模块 | 0(描述符驱动,零 LVGL 对象;仅栈缓冲) | — | | 字体 +10 字形 | ≈250B Flash | .rodata | diff --git a/docs/测试说明/测试开发标准流程.md b/docs/测试说明/测试开发标准流程.md index 50185898..975ab08c 100644 --- a/docs/测试说明/测试开发标准流程.md +++ b/docs/测试说明/测试开发标准流程.md @@ -372,7 +372,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 |---|---|---| | 文本层 | `check_content_pages.py` 字形模板匹配(50 条,解析字体 cmap / glyph_dsc / bitmap) | 缺字显示空白 / 标签写错等**静默失败**(不依赖 `ref/`,先于像素比对运行、失败即中止防误刷参考) | | 像素层 | 11 内容页 + 菜单 8 状态,与黄金参考逐字节比对 | 几何 / 样式 / 光标反显 / 整体外观 | -| 池水位层 | `MENU_POOL_BASELINE=4784 B` / `POOL_AFTER_ROUNDTRIP=4864 B` | LVGL 内存池泄漏 / 结构性漂移 | +| 池水位层 | `MENU_POOL_BASELINE=5368 B` / `POOL_AFTER_ROUNDTRIP=5448 B` | LVGL 内存池泄漏 / 结构性漂移(量的是 `used`,与 `LV_MEM_SIZE` 总大小无关) | ### 5.3 黄金参考与字体纪律 diff --git a/docs/环境与部署/项目部署环境要求.md b/docs/环境与部署/项目部署环境要求.md index 5c3cfada..a744e3ba 100644 --- a/docs/环境与部署/项目部署环境要求.md +++ b/docs/环境与部署/项目部署环境要求.md @@ -502,7 +502,7 @@ $env:DTU_KEIL_UV4 = 'D:\NotExist\UV4.exe' # 仅当前会话,不动用户级 | Flash | 232,000 B(226.6 KB) | 1 MB | 22.1% | ~798 KB | 充裕 | | 主 SRAM(静态区+堆) | 125,104 B(122.2 KB) | 128 KB | **95.4%** | **5.8 KB** | **极度紧张**:新增任何静态缓冲/任务栈前必须先核算 RAM 余量 | -- LVGL 内存池 40 KB(静态占用);FreeRTOS 堆 48 KB +- LVGL 内存池 16 KB(静态占用,2026-10-15 由 40 KB 收缩);FreeRTOS 堆 56 KB(CCM) - 任务栈预算(words,1 word = 4 B): | 任务 | 栈大小 | 优先级 | diff --git a/docs/问题记录/项目审查与优化建议报告.md b/docs/问题记录/项目审查与优化建议报告.md index 53a87165..d05b3d29 100644 --- a/docs/问题记录/项目审查与优化建议报告.md +++ b/docs/问题记录/项目审查与优化建议报告.md @@ -3,7 +3,7 @@ | 项目 | 内容 | |------|------| | 审查日期 | 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` 会看到小差异,属正常 | +| **最近复核** | **2026-10-15**(R-9 落地后重链接),基线 `HEAD = 803f771`(分支 `v2-on-ui`);固件基线 = `MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(0 Error / 0 Warning)。⚠ 该次构建**含并行未提交改动**(`App/cfg/cfg_file.*`、`Drivers/BSP/SD2506/*`)→ Code 212,344 比 2026-10-14 干净基线多 48 B;本报告只在**内存/Flash 水位**上追这次构建,其余结论仍属 2026-10-14 复核 | | **销项状态** | 本报告采用**修复即销项**:已修复项的正文与表格行已删除,去处见文末**已销项条目的记录去处**;未闭环清单见 **§0.2 / §0.4** | | 审查范围 | 仓库全量(2,466 个受版本控制文件 / 79.3 MB);重点为自研代码 50,874 行(191 文件)与构建产物内存画像 | | 审查方式 | 静态审查 + 链接图(.map)量化分析 + 构建脚本/门禁核对;**未做上机动态验证**(除特别标注外) | @@ -35,7 +35,7 @@ | # | 编号 | 问题 | 影响 | 现状 | |---|------|------|------|------| | 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 | +| 2 | **R-2 剩余**(登记簿 **N-27**,W3) | R-9 已落地(`LV_MEM_SIZE` 40→16 KB,2026-10-15)→ 待迁体量由 54,146 B 降到 **29,570 B**(池 16,384 + `ui_port.o` 6,420 + `ui_net_sink.o` 6,766),SRAM1 由 74.9% 降到 **56.1%** | **CCM 仍只剩 7,904 B**(堆 56 KB 占掉)→ 29.5 KB 还是装不下,R-2 后半段只差"缩堆"一步 | 🟡 下一步 = 堆 56→32 KB(⚠ B5 是反向扩容,缩回前须重跑 `stack_report.py` + 上板 `min_ever` 复测)→ 解锁链 `../内存管理/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`,不是漏注册。 @@ -44,7 +44,7 @@ > **推进顺序的唯一权威是 [`../任务规划/README.md`](../任务规划/README.md) §2**;本节只是体系评审视角的两条结构性建议,不另立顺序。 -1. **把内存从"卡死"变"富裕"**:先按 R-9 下调 LVGL 池、再缩堆腾出 CCM,把 LVGL 池与 UI 帧缓冲(≈54 KB)搬进 CCM,作为后续功能的预算来源(§5.3)。 +1. **把内存从"卡死"变"富裕"**:R-9 已把 LVGL 池 40→16 KB(2026-10-15,SRAM1 由 74.9% 降到 56.1%);下一步缩堆腾出 CCM,把 LVGL 池与 UI 帧缓冲(现 **≈29.6 KB**)搬进 CCM,作为后续功能的预算来源(§5.3)。 2. **把 UI 从"每页手写"变"声明 + 原型库"**:令牌层 → 原型库 → 生成器补全注册 → golden 入门禁,使新增一页的成本从 8 处手工改动降到 1 处声明(§6)。 --- @@ -57,8 +57,8 @@ | 类别 | 编号 | 未闭环内容 | 下一步 | |------|------|-----------|--------| | **数据正确性** | **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** | **R-2 剩余** 🟡(N-27,W3) | 待迁体量已由 54,146 B 降到 **29,570 B**(R-9 落地后);CCM 只剩 7,904 B | 只差**缩堆 56→32 KB** 腾 CCM,再迁(解锁链 → `../内存管理/RAM预算台账.md` §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-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 各行建议 | @@ -329,16 +329,18 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap ## 5. 内存优化(量化) -### 5.1 实测基线(2026-10-14 `.map`) +### 5.1 实测基线(2026-10-15 `.map`,R-9 落地后) | 指标 | 数值 | 上限 | 占用 | |------|------|------|------| -| `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)** | +| `Code + RO-data`(`ER_IROM1`) | 231,084 B | 1 MB Flash | **22.0%** | +| `ROM`(`Code + RO + RW` 初始化表) | 232,196 B | 1 MB | 22.1% | +| **SRAM1(0x20000000,128 KB)** `RW_IRAM1` | **73,568 B** | 131,072 B | **56.1%(余 57,504 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 已落地(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**;**当前瓶颈换到 CCM(87.9%,只剩 7,904 B)**。 + > **权威源**:区域水位与子系统配额 → `../内存管理/RAM预算台账.md`(受管区);趋势 → `scripts/ram_baseline.tsv`(`ram_baseline.py --trend` 高亮 RW+ZI 增长 >1 KB 的对象)。本节只列支撑结论所需的数。 **RW+ZI 主要对象(`ram_baseline.tsv` 末行 Top-10)** @@ -346,7 +348,7 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap | 对象 / 符号 | 字节 | 所在区域 | 归属 | 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 未完成项)** | +| `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 | @@ -356,7 +358,7 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap | `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 静默不落地)。 +**两个静态池(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` 组件表对象名归类)** @@ -369,15 +371,17 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap | 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% | +| 其余(`App/` 应用与驱动自研、startup、C 库、填充) | ≈45,497 | ≈19.7% | +| **合计** | **231,084** | 100% | + +> 归类按 `.map` 组件表对象名前缀,合计与区域总量可差几十字节(链接器填充)。前 7 行逐字节与 2026-10-14 那次构建相同;并行未提交改动(`cfg_file` / `sd2506`)的 +48 B 落在"其余"。 **要点**: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)。 +1. **瓶颈已从 SRAM1 换到 CCM**:R-9 落地后 SRAM1 只剩 56.1%(**余 57,504 B**),而 **CCM 被堆占掉 87.9%(只剩 7,904 B)**,待迁的 LVGL 池 + UI 两份缓冲 = **29,570 B**。 + → R-2 的剩余部分**只差"缩堆"这一步**(56→32 KB,`../内存管理/RAM使用分析与优化方案.md` §2.2;⚠ 2026-10-12 B5 是反向扩容,缩回前必须重跑 `stack_report.py` + 上板 `min_ever` 复测);顺序不可颠倒(`../内存管理/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` 都会中招。 @@ -387,11 +391,12 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap | 编号 | 措施 | 收益 | 风险 | 优先级 | 状态 | |------|------|------|------|--------|------| -| **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-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)** | 🟡 **只做一半**:`RW_CCM` 只收 `heap_4.o`(57,376 B)→ SRAM1 已减 49 KB+。⚠ 前置条件**只剩一步**:R-9 已落地(2026-10-15),但 **CCM 只剩 7,904 B,29.5 KB 仍装不下** → 需缩堆 56→32 KB | | **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-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 复测证明与本次改动无关,属并行未提交的 `cfg_file` 改动)、往返池水位 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 总量等价,**但顺序决定收益落在哪个区域**。 > @@ -534,7 +539,7 @@ const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_dea | 每页**只用** `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) | +| `LVGL 池`用量必须经 `lv_mem_monitor` / 池水位门禁实测后才允许扩大 | **16 KB**(R-9 于 2026-10-15 由 40 KB 降下;实测峰值 6,568 B = 池的 40%,余 ≈9.8 KB) | ### 6.5 实施顺序(UI 专项,总计约 8 人日) @@ -622,6 +627,7 @@ const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_dea | 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-15** | **执行本报告条目:R-9 落地并销项** | `LV_MEM_SIZE` 40→16 KB(`Lib/lv_conf.h` + `simulator/lv_conf.h` 同步,`lv-conf-sync` 生效值比对 PASS)。**依据**:池水位门禁实测菜单基线 5,368 B / 开页-关页往返 5,448 B / 最重页面峰值 6,568 B。**验证**:`@build` 0 错 0 警,ZI 精确 −24,576 B → SRAM1 98,144/74.9% → **73,568/56.1%(余 57,504 B)**,CCM 不变;PC 侧 11 页黄金参考逐字节 10/11 一致、往返池水位 5,448 B 未变(唯一差异 `netcfg_save` 经 40 KB A/B 复测确认与本次改动无关)。**连带改写**:§0.2 Top3 第 2 条、§0.3 主线一、§0.4 内存两行、§5.1 基线表(追 2026-10-15 `.map`)、§5.2 诊断 1(瓶颈由 SRAM1 换到 CCM)、§5.3(R-9 行删除、R-2 收益改 −29,570 B)、§6.4 池容量行;同步 `../内存管理/RAM使用分析与优化方案.md`、`../内存管理/RAM预算台账.md` §3、`../UI设计/UI_设计架构.md`、`../UI设计/UI_界面制作流程.md`、`../UI设计/LVGL模拟器部署与使用说明.md`、`simulator/README.md`、`../产品需求/产品需求设计文档.md`。**真机复跑 ✅**:`@test` PASS(`fw build: Oct 15 2026 07:31:34`,无 `[ERR]`/Fault,开机 622 ms 到 `net init: OK`),`menu build done: used=3192` = 建池后 15,020 B 的 21%。台账受管区与内存快照区已 `--write` 回填 → `mem-budget` / `mem-numbers` 两钩子转绿 | | **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`) | ### 已销项条目的记录去处 @@ -648,3 +654,4 @@ const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_dea | 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` 变更记录(池水位基线) | +| **R-9(`LV_MEM_SIZE` 40→16 KB,登记簿 N-27 的 W3 第一步,2026-10-15 已执行)** | 取值依据与再降 8 KB 的前置写在 `Lib/lv_conf.h` 与 `simulator/lv_conf.h` 的池注释;实测水位 → `../内存管理/RAM使用分析与优化方案.md` §2.6.2 与 §阶段 1.1(已执行);解锁链第 ① 步 ✅ → `../内存管理/RAM预算台账.md` §3;池容量口径 → `../UI设计/UI_设计架构.md` §1.2、`../UI设计/UI_界面制作流程.md` C4 | diff --git a/scripts/ram_baseline.tsv b/scripts/ram_baseline.tsv index 96fc2578..d5b18078 100644 --- a/scripts/ram_baseline.tsv +++ b/scripts/ram_baseline.tsv @@ -2,3 +2,4 @@ date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi 2026-10-12 209492 18332 1104 154184 227824 21.7 97864 74.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;console_log.o=4120;ch395f.o=4106;default_task.o=2736;sys_log_mcu.o=1924 2026-10-13 211456 18760 1104 154232 230216 22.0 97912 74.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924 2026-10-14 212296 18740 1112 154456 231036 22.0 98144 74.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924 +2026-10-15 212344 18740 1112 129880 231084 22.0 73568 56.1 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924 diff --git a/simulator/README.md b/simulator/README.md index 01d9beb0..99f4c963 100644 --- a/simulator/README.md +++ b/simulator/README.md @@ -165,7 +165,7 @@ LVGL 场景(同一份 ui_menu.c) | `LV_SDL_INCLUDE_PATH` | — | `"SDL2/SDL.h"` | 对应 `include/SDL2/` 头文件副本 | | `LV_USE_THEME_MONO` | 0 | **1** | 1bpp 无主题 = 黑底黑字不可见 | | `LV_DRAW_SW_SUPPORT_I1` | 1 | 1 | 仅启用 I1 渲染,裁掉其余色深 | -| `LV_MEM_SIZE` | 40 KB | 40 KB | 保持一致(PC 内存充足,可放宽) | +| `LV_MEM_SIZE` | 16 KB | 16 KB | **必须一致**(`lv-conf-sync` 比对生效值;2026-10-15 由 40 KB 同步收缩,PC 侧不再"放宽") | | `LV_FONT_DEFAULT` | `&ui_font_cn_12` | 同 | 共用同一字体 | | 其余控件/驱动 | 裁剪 | 基本一致(另开 `LV_USE_TEXTAREA/KEYBOARD/BUTTONMATRIX`) | 模拟器略宽松,便于调试 | diff --git a/simulator/lv_conf.h b/simulator/lv_conf.h index 27666afd..a1b5f60c 100644 --- a/simulator/lv_conf.h +++ b/simulator/lv_conf.h @@ -35,7 +35,9 @@ #define LV_STDARG_INCLUDE #if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN - #define LV_MEM_SIZE (40 * 1024U) + /* 与 MCU 端 Lib/lv_conf.h 同值(lv-conf-sync 门禁比对生效值); + * 2026-10-15 40KB → 16KB,池水位门禁基线见 simulator/tests/ */ + #define LV_MEM_SIZE (16 * 1024U) #define LV_MEM_ADR 0 #endif