Files
STM32F4-Base/docs/UI设计/UI_界面制作流程.md
T
wandering1 4a4ad6bc5f chore(全仓): @build/@test 旧文档别名清退为技能调用名
现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名,
不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体
run_boot_test.py 的真实调用。

- AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改
  「编译(/keil-build)」「串口回归(/serial-boot-test)」
- keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree
  方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围)
- 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入
  等 41 行历史证据保留旧名
- gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑
- 顺手补并行方案同行漏网计数 51→52 钩子

验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all
rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52
钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。
2026-10-30 13:03:12 +08:00

393 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UI 界面制作流程(参考图 → LVGL)
> **用途**:规范"参考图 → 设计规格 → LVGL 代码 → 双端验证"的完整管线,规定每一步的**动作、产物、放行条件(gate)**,保证过程可重复、可交接。
> **适用**:所有 UI 新界面 / 界面大改;小改动走「小改动快速通道」一节(见文末第十节)。
> **配套文档**:`UI_设计架构.md`(系统如何工作)、`菜单页面设计.md`(菜单/导航行为与数据流设计)、`LVGL模拟器部署与使用说明.md`(模拟器)、`UI_中文字体生成与配置.md`(字体生成)、`UI_自动化测试流程.md`(P2/P3 回归门禁的可执行版本:脚本 + 池水位常量 + 黄金参考更新)。
> **首个实例**:界面 01 主界面(根页),规格 `mockups/01_主界面_说明.md`(本文所有示例数字均取自该试点)。
> **沿革**:旧管线含"HTML 样机"阶段(参考图 → HTML 样机 → 代码),2026-09-13 起移除——设计基准改为**文本规格(布局参数表)+ 参考图**,视觉基准改为**模拟器出图**,用户视觉确认点移到 P2 模拟器窗口/出图(沿革细节查 `git log`,本节只写现状)。
---
## 一、流程总览
**四阶段单向管线**:`参考图 → P0 → P1 → P2 → P3 → P4`。每阶段产出明确**产物**、满足**放行条件(Gate)**才进入下一阶段;其中 **P2 是"比对 → 修正"迭代环**,未收敛不得前进。
```mermaid
flowchart TD
R(["参考图"]) --> P0["P0 需求录入 + 设计规格<br/>→ 屏幕台账 + NN_说明.md(布局参数表/字符集/问题)"]
P0 -- "Gate: 界面作用/菜单位置/极性已确认 + 阻塞性问题有答案 + 布局验证等式成立 + 字符集核对" --> P1["P1 规格 → LVGL 代码<br/>→ Drivers/BSP/UI 代码 + 字体(重生成)"]
P1 -- "Gate: 参数逐项对账 + 字体重生成 + 内存预算 + 双端注册" --> P2["P2 模拟器验证<br/>→ shot.png(对照参考图 + 规格参数表)"]
P2 --> C2{"输出图 ↔ 参考图<br/>同构?"}
C2 -- "否: 按根因修正(修代码 / 回 P0 修规格)→ 再出图" --> P2
C2 -- "是(Gate: 导航符合按键语义 + 自动化回归 PASS)" --> P3["P3 MCU 验证<br/>→ 日志 + 上机目检结论"]
P3 -- "Gate: /keil-build 0 错 0 警 + /serial-boot-test PASS + 上机目检通过" --> P4(["P4 归档<br/>→ 受影响章节现状化/台账 + 随代码提交(代码+字体+字集+规格+文档)"])
```
> 小改动(文案/间距/纯交互/字体重生成)走「小改动快速通道」一节(见文末第十节),无需全流程。
### 1.1 各阶段职责分工
| 阶段 | 用户做 | AI 做 | 产物 |
|---|---|---|---|
| P0 需求录入 + 设计规格 | 提供图片(每批 1~3 屏);说明各屏作用与菜单位置;回答问题;确认布局参数表 | 图片分析、界面登记、字体度量核对、字符集核对、布局计算(验证等式)、规格制作 | 台账 + `_说明.md`(参数表/字符集/问题/映射/翻译要点) |
| P1 规格→代码 | (可选)抽查代码 | 逐参数翻译、字体重生成、内存预算、双端注册 | 代码 + 字体 |
| P2 模拟器 | **视觉确认**(窗口模式/出图),指出差异 | 构建、离线出图、出图与原图逐项比对、依规格改码 | 比对结论 |
| P3 MCU | **真机目检**(DTU-RemoteLCD) | /keil-build / /serial-boot-test 执行、日志解析 | 日志 + 结论 |
| P4 归档 | — | 把受影响章节改成改动后的现状 + 更新台账 | 文档 |
> 用户视觉确认点由旧"浏览器打开 HTML 样机"改为 **P2 模拟器窗口/出图**:P0 确认**参数表**(数值层面),P2 确认**渲染结果**(视觉层面);渲染结果与意图不符时按 §5.1 根因修正(代码错 → 修代码;参数表错 → 回 P0 修规格再修代码)。
---
## 二、设计约束(硬规则)
管线中一切设计必须满足以下约束;违反即需重做,不存在"真机上再凑合"的选项。
| # | 约束 | 值 | 来源 |
|---|---|---|---|
| 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 | 字符集 | 子集字体(当前 224 汉字/符号 + ASCII 0x20-0x7E,共 319 字形);**缺字显示空白**;新字必须重生成字体 | `tools/font_gen/` |
| 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/` 复制副本 | 架构文档 |
| C8 | 视觉禁区(1bpp) | 圆角 / 阴影 / 透明度 / 渐变 / 多色 / 抗锯齿 全部禁用;设计与代码都不得引入 | 物理限制 |
| C9 | 双端注册 | 新增 `Drivers/BSP/UI/*.c` 必须**两处**手动注册:Keil `.uvprojx` + `simulator/CMakeLists.txt` 的 `UI_SOURCES` 列表(均为显式列表,无通配扫描)。只注册一端 = 另一端静默缺文件 | 实测(2026-09-03) |
### 2.1 字体度量速查(2026-09-03 实测,字体变更时重测)
| 项 | 原始值 | = 设备像素 | 说明 |
|---|---|---|---|
| 汉字步进宽 | `adv_w = 192`(1/16 px) | **12 px/字** | 4 字标签 = 48px |
| ASCII 步进宽 | `adv_w = 96` | **6 px/字** | "OK" = 12px,"V1.0" = 24px |
| 汉字墨迹盒 | `box_w` 9~11,`box_h` 11~12 | 墨迹在盒内再内缩 1~2px | **字体自带侧边距**,贴边文字仍有视觉留白 |
| 行高 | `line_height = 13` | 13px | 布局按 13 算,不是 12 |
| 基线 | `base_line = 2`(自底部) | — | 垂直定位参考 |
> 设备字体的源字体为 **SimSun**(上述度量的来源);字符集由仓库根 `ui_cn_chars.txt` 管理。
---
## 三、P0:需求录入与设计规格
### 3.1 用户输入
1. 参考图(建议每批 1~3 屏,不必一次给全)
2. 每图一句话:是什么界面、干什么用、挂在菜单哪个位置
3. 对 AI 提出的问题的回答(能答则答,答不了的标"待定")
### 3.2 AI 动作:图片分析(检查单)
| 分析项 | 内容 |
|---|---|
| 总体结构 | 页头 / 主体(网格/列表/数值/曲线/表单)/ 页脚 / 状态栏 |
| 元素类型 | 网格、列表、数值、曲线、开关、进度条、图标(1bpp 小屏图标基本不可行 → 文本替代) |
| 文本内容 | **逐字抄录**(字符集核对的输入;漏一个字 = 真机一处空白) |
| 比例换算 | 参考图比例 → 160×160 设备像素(参考屏分辨率通常大于 160,做**比例适配**而非像素照搬) |
| 风格 | 极性(白底黑字/黑底白字)、边框样式、选中样式 |
| 信息缺失 | 截图裁切、遮挡、模糊处 → 转成待确认问题 |
### 3.3 界面台账
- 编号 `NN`(01 = 试点界面),名称、作用、菜单位置、状态(待做/进行中/已完成)
- 台账随 P4 归档更新
### 3.4 字体度量核对(字体每次变更时重做)
1. 读 `Drivers/BSP/UI/ui_font_cn_12.c` 头部 Opts 注释 → 字体源(宋体)、尺寸(12)、bpp(1)、**当前字符集**
2. 读 `glyph_dsc[]` 表 → `adv_w ÷ 16` = px(核对 CJK=12 / ASCII=6)
3. 读 `font_dsc` → `line_height=13`、`base_line=2`
4. 与 §2.1 速查不一致 → **全部规格参数重算**
### 3.5 字符集核对
1. 收集本屏全部文本中的**去重汉字**
2. 对照现有字符集(字体 `.c` 头部 `--symbols` 行为准;`ui_cn_chars.txt` 为生成物,重生成后一致)
3. 输出:命中列表(现有)/ 新增列表(标红)→ 记入配套 `_说明.md`
4. 新增列表决定 P1 字体重生成的范围
> **试点实例**:01 主界面 7 个菜单项共 22 个唯一汉字 → 命中 21(装置信息实时数据参数定值遥设维护通讯记录查询),**新增 1**(三);状态行"当前模式:无模式"新增 5(当前模式无);箭头 **▶**(U+25B6)为新增符号。合计**确定新增 7 个字形**(若状态行用全角冒号":"则再多 1,共 8)。
### 3.6 布局计算
- 所有值为**设备像素整数**
- 列宽:`cell_w = floor((W − 2×边距 − 间隙×(n−1)) / n)`,**1px 余量给最后一列**
- 行高:`cell_h = floor((H − 2×边距 − 间隙×(m−1)) / m)`
- 必须给出**验证等式**(每项相加 = 屏幕尺寸)
- 文字宽度检查:`字数 × 12px`(步进宽)vs 格内宽(格宽 − 2px 边框)。步进宽超出但**墨迹宽**(约 −1px/端)放得下即可;放不下 → 减字数 / 减列数 / 回 3.1 问用户
> **试点实例(7 行列表)**:
> `160 = 顶边距2 + 顶装饰5 + 隙4 + 状态行13 + 隙5 + 列表框128 + 底边距3`
> 列表框 `144×128`(1px 边框,x=8):内容高 `7×18 + 2(上下边框) = 128`,7 行 × 18px
> 行文字 4字 = 步进 48px,行内宽 144(左留白 4px)→ 富余,**左对齐**成立
>
> 推论:行通栏 144px,文字**左对齐**(左留白 4px)+ 字体自带侧边距 = 视觉留白;行尾 ▶ 右对齐(右留白 4px)。选中行整行反色(白底黑字)。
### 3.7 设计规格 `NN_界面名_说明.md`
**文件**:`docs/UI设计/mockups/NN_界面名_说明.md`(`mockups/` 目录仅存"原始参考图 + 设计规格";骨架见附录 A)
**规格(顺序固定)**:
1. **布局参数表** —— 每个 px 值都列出(P1 翻译的依据,必须与验证等式自洽)+ 验证等式
2. **行/元素内容与状态**
3. **字符集核对结果** —— 命中 / 新增
4. **待确认问题** —— 沿用 3.2 的 Q 编号
5. **映射建议**(菜单类界面:条目 → 子树)
6. **LVGL 翻译要点** —— 本屏具体的翻译提示(控件选择、状态、内存注意点)
> **规格是唯一设计基准**:P1 照参数表翻译代码;P2 出图与原图不符时,先查代码是否忠实实现参数表——代码错 → 修代码;参数表本身偏离原图/用户意图 → 回本阶段修规格,再修代码。
> 规格定稿(文件头标 `已定稿 (日期)`)后,改动须用户明确提出。
### 3.8 Gate(进入 P1)
- [ ] 界面作用与菜单位置已确认
- [ ] 阻塞性问题已回答;非阻塞项已标注默认值(如 "TBD")
- [ ] 布局计算完成,验证等式成立
- [ ] 字符集核对完成(新增字清单确定)
- [ ] 用户已确认规格(参数表/内容/状态)
> **试点实例**:01 主界面。用户给一张根页截图:顶部梯形装饰 + 状态行"当前模式:无模式" + 7 行列表(装置信息/实时数据/参数定值/三遥设置/装置维护/通讯参数/记录查询),黑底白字(与现系统同极性)。AI 分析出:7 项与现有 `g_menu_tab` 的映射,其中 3 项疑似重命名(参数定值/三遥设置/通讯参数)→ Q1;"三遥设置"对应现有"遥测量"还是新增"三遥"节点 → Q2;旧菜单"厂家设置/版本信息"未列入 7 项,入口在哪 → Q3;"当前模式"指什么、数据从哪来 → Q4。
---
## 四、P1:规格 → LVGL 代码
### 4.1 翻译对照表(规格参数 → LVGL v9)
| 规格 / 设计描述 | LVGL(设备端) | 备注 |
|---|---|---|
| 160×160 白底画布 | display(I1)+ 屏幕背景色 | 极性随全局决策(Q2 类问题) |
| 元素位置 `x, y` | `lv_obj_set_pos(o, x, y)` | 整数 px,绝对坐标 |
| 元素尺寸 `w×h` | `lv_obj_set_size(o, w, h)` | |
| 横向/纵向排布 | `lv_obj_set_flex_flow(o, LV_FLEX_FLOW_ROW[_WRAP])` | |
| 间距 `gap` | `lv_obj_set_style_pad_gap(o, g, 0)` | |
| 1px 边框 | `lv_style_set_border_width(st,1)` + `border_color` | |
| 仅底边框 | `lv_style_set_border_side(st, LV_BORDER_SIDE_BOTTOM, 1)` | v9 分边设置 |
| 黑/白背景 | `lv_style_set_bg_color` + `lv_style_set_bg_opa` | 1bpp 仅纯黑/纯白 |
| 文本 | `lv_label_create` + `lv_label_set_text_static` | 静态字符串(C5) |
| 文字对齐 | 标签定位 / `lv_label_set_long_mode` | 默认左对齐(见 §3.6 推论) |
| 选中反色 | `LV_STATE_CHECKED` 样式(bg/文字色互换) | 条目对象**自身**带 CHECKED(防白底白字,`UI_设计架构.md` §5.5) |
| 隐藏状态 | `LV_OBJ_FLAG_HIDDEN` | 页面/状态切换 |
| 圆角/阴影/透明/渐变 | **禁止**(C8) | 规格里就不该有 |
### 4.2 文件策略与双端注册
- **首选原地扩展** `ui_menu.c`(共用源,改一处两端生效)
- 确需新文件(模块独立,如曲线页渲染器)时,**两处注册**(C9):
1. Keil:`MDK-ARM/*.uvprojx` 手动添加
2. 模拟器:`simulator/CMakeLists.txt` 的 `UI_SOURCES` 列表加一行
- 新**页面类型**(内容页/编辑页)扩展现有"容器持久、条目动态"架构,**禁止**另起第二套架构
- 字体文件 `ui_font_cn_12.c` 两端均已注册 → 重生成是纯内容替换,无需注册
### 4.3 翻译纪律
1. **逐参数翻译**:代码中每个 px 值必须能在规格"布局参数表"中找到出处;禁止"看着办"
2. 样式沿用现有 `s_style_flat` 模式(静态样式、一次性初始化、后加优先覆盖主题)
3. 焦点对象恒为**页面容器**;条目不入输入组(`UI_设计架构.md` §5.4)
4. 容器**永不删除**(indev 释放事件 use-after-free,`UI_设计架构.md` §5.2);只建/毁框体(行自绘、无对象)
5. 编码纪律(C6):Edit 工具精确替换;文件保持 UTF-8 BOM 不变
6. 注释按代码规范 V1.0:文件头模块块 + 函数头块(功能/参数/返回/限定/说明)
### 4.4 字体重生成(固定顺序,不得跳过或调换)
```
1. 代码落库(新界面字符串已写入 ui_menu.c 等 UI 源码)
2. python tools/font_gen/extract_cn.py # 扫描 UI 源码字符串 → 仓库根 ui_cn_chars.txt(自动含新字;纯标准库,任意 python3)
3. & '.venv\Scripts\python.exe' tools/font_gen/make_font.py # 宋体 12px 1bpp + ASCII + 字符集 → ui_font_cn_12.c(全量重生成 + 自动补回宋体缺失的 ▶ 箭头,幂等;需 fontTools)
4. 生成物 diff 检查:新增字在位、旧字不变、Opts 行字符集与预期一致
```
- 一次性环境准备:`npm i -g lv_font_conv`;fonttools/pillow 统一装进仓库 `.venv`(`.venv\Scripts\python.exe -m pip install -r requirements.txt`,见 AGENTS.md「Python 环境」)。PATH 默认 python 是 msys64 的、无第三方库,勿用它跑 make_font.py
- `ui_cn_chars.txt`(仓库根,受 git 跟踪)与 `ui_font_cn_12.c` **随代码一起提交**
- 双端 `LV_FONT_DEFAULT` 均指向 `&ui_font_cn_12` → 重生成后两端自动生效
- P0 已知的新增字若含生僻字(宋体可能无此字形)→ 在写码前先手动验证字形,避免"代码写完才发现是空白"
- **已处理实例:▶(U+25B6)箭头**——宋体无此字形,`lv_font_conv` 静默跳过 → 真机方框。`make_font.py` 已内置 `merge_arrow_glyph()` 自动从 Segoe UI Symbol 补回(幂等),重生成字体后箭头不丢。换用自带 ▶ 的字体时可忽略
### 4.5 内存预算
- 估算:`对象数 × 单价`(`lv_obj` ≈ 150B、`lv_label` ≈ 200B 量级,以实测为准)
- **实测点**:建页前后打 `lv_mem_monitor` 池水位(沿用现有 `menu build start/done` 日志模式)
- 红线:**最坏路径**(根页 + 最深下钻 + 当前内容页)< 40KB 且留 ≥25% 余量
- 超预算 → 回到 P0 减元素 / 改按需建毁策略;不得靠加大 `LV_MEM_SIZE` 硬扛(RAM 总预算紧张)
### 4.6 Gate(进入 P2)
- [ ] 代码与规格参数逐项一致(自查对照表)
- [ ] 字体已重生成且 diff 检查通过
- [ ] 内存预算通过(估算 + 代码中埋好水位日志)
- [ ] 新文件(如有)双端注册完成
---
## 五、P2:模拟器验证
### 5.1 动作
1. `cd simulator; cmake --build build --config Debug`(改过 `CMakeLists.txt` 才需重新 `cmake -B`)
2. **离线出图**(模拟器页面渲染完成后):
- `.\build\Debug\lvgl_sim.exe --screenshot shot.bin` → 离屏渲染首帧,输出 I1(1bpp) 位图
- `python i1_to_png.py shot.bin -o shot.png --scale 4` → 160×160 PNG(放大便于比对)
3. **读两图比对**:模拟器 `shot.png` vs **参考图**(`mockups/NN_..._原始图片.png`),按 §5.2 检查单逐项核对(参考图比例可能不同:几何以规格参数表的设备 px 为准,不照参考图像素)
4. **不符 → 根因修正**(本阶段核心迭代环):
- 代码与规格参数表不一致(位置/尺寸/颜色错)→ 修代码
- 代码忠实于参数表、但参数表本身偏离参考图/用户意图 → 回 P0 修规格,再修代码
- 重构建 → 重新出图 → 再比对,循环至收敛
5. 交互行为(导航/滚动/选中)在窗口模式 `.\build\Debug\lvgl_sim.exe` 内按键验证(离线出图只覆盖首帧静态画面)
6. **自动化回归**(任何 UI 改动必跑,脚本已入库无需重写):仓库根目录执行
`python simulator/tests/check_menu_rows.py` + `python simulator/tests/check_content_pages.py`,
全部门禁与常量说明见 `UI_自动化测试流程.md`
> **循环目的**:消除"AI 读图不准"的风险——参数表是 AI 对参考图的理解,出图与原图比对才能暴露读图错误(此时是参数表问题而非代码问题,回 P0 修)。
### 5.2 比对检查单
| 项 | 通过标准 |
|---|---|
| 几何 | 位置/尺寸/边框与规格布局参数表逐格一致(容差 1px);整体结构/比例与原图一致 |
| 文本 | 内容完整、**无空白块**(空白 = 缺字 → 回 §4.4)、宽度/位置正确 |
| 状态 | 选中反色正确(极性正确:黑底白字↔白底黑字不反) |
| 导航 | ↑↓ 移动(环绕)、→/Enter 进入、←/Esc 返回;滚动条行为正常 |
| 日志 | `menu build done: used=…` 在预算内;无 ERROR/FAIL |
### 5.3 已知不阻塞差异(不修)
- 参考图为高分辨率截图/手工图,字形与线宽与设备 1bpp 渲染不完全一致(形状近似即可,几何以参数表为准)
- 系统 DPI 缩放导致的窗口显示大小差异
### 5.4 Gate(进入 P3)
- [ ] 模拟器出图 `shot.png` 与规格参数表逐项一致、与参考图结构一致(§5.2,忽略 5.3)
- [ ] 导航行为符合键位语义表(`UI_设计架构.md` §3.4)
- [ ] 自动化回归 PASS:`check_menu_rows.py` + `check_content_pages.py`(`UI_自动化测试流程.md`)
---
## 六、P3:MCU 验证
1. `/keil-build` → **0 错误 0 警告**(AGENTS.md 强制;有警告看 `MDK-ARM/build_log.txt`)
2. `/serial-boot-test`(一键 `.dsh/skills/serial-boot-test/scripts/run_boot_test.py`):
- 采集窗口**先于烧录武装**(脚本 `--flash-cmd` 已保证顺序)
- Required:`UI port init: OK;UI menu init: OK` + 本界面定制标记(按屏增补,如页头标题字样)
- Forbidden:`FAIL;ERROR;HardFault`
3. **真机目检**(用户执行,DTU-RemoteLCD @ TCP 7003):极性 / 边框 / 文字 / 选中态 / 滚动
4. 串口日志:池水位、任务栈高水位(`uxTaskGetStackHighWaterMark`,uiTask 预算 16384 words)
### Gate(进入 P4)
- [ ] /keil-build 0错0警
- [ ] /serial-boot-test PASS
- [ ] 真机目检通过(用户确认)
---
## 七、P4:归档
1. 更新 `docs/UI设计/UI_设计架构.md`:只改**受影响章节**(文件地图 / 关键参数 / 菜单架构 / 键位语义),把它们写成改动后的现状;**不再记流水**(2026-10-28 裁定,`docs/文档规范.md` §5.1)
2. 设计规格 `NN_界面名_说明.md` **保留**于 `mockups/`(定稿版 = 设计档案,后续改界面须先改规格再改代码)
3. 台账:该界面状态 → 已完成(日期)
4. 提交检查:代码 + 字体(`ui_font_cn_12.c`)+ `ui_cn_chars.txt` + 规格 + 文档 一起进 git
---
## 八、批量生产策略
1. **试点先行**:01 主界面走完全流程,校准各阶段耗时、翻译损耗点、内存基线(试点已完成 P1,后续阶段数据回填本文档)
2. **按界面族批量**:
- 列表页(复用现有级联框自绘架构)→ 数据显示页(数值行框架)→ 参数编辑页(编辑/确认交互)→ 曲线/状态页
- 每族先做**族内第一屏**产出可复用的页面框架,再批量铺同族界面
3. **每批收尾**:字体统一重生成(各屏字符集**并集**,一次生成)→ 全量回归(已完成界面重跑 /serial-boot-test)
4. 字符集只增不减;字体文件每次**全量重生成**,禁止手工拼合
### 8.1 规格投入分流(按屏型)
> **两条正交轴**:设计规格(布局参数表)只解决"**长什么样**"(静态外观);"**怎么动 / 数据从哪来**"(行为 / 导航 / 数据驱动)靠设计文档(`菜单页面设计.md`)+ LVGL 模拟器(动态 oracle)。两轴互补——规格是"设计先行"的确定性工具,**不是代码生成器**,别指望它加速行为代码。
按屏型决定 P0 规格投入(收益从高到低):
| 屏型 | 例 | P0 规格投入 | 行为 / 数据靠什么定 |
|---|---|---|---|
| 静态、元素多的内容页 | 数据显示 / 参数定值 / 装置信息 | **完整**参数表逐项(收益最大:几何钉死→抄写 + 缺字/1bpp 排雷前移 + P2 客观视觉基准) | 数据绑定 + 少量交互 → 设计文档 |
| 导航 / 交互重的屏 | 菜单(横向级联框)/ 参数编辑 | 参数表只做**静态首帧**样子;交互重头放**设计文档** | 设计文档定状态机/级联框/键位语义 + 模拟器验证行为 |
- **规格的真实价值**(确定但偏窄,集中在 P0/P2):① 几何从"推导"降为"抄写" ② 缺字 / 1bpp 禁区在 P0 排雷(比 P3 真机发现便宜得多) ③ P2 提供客观几何基准 ④ 用户可在参数表层面廉价确认设计。
- **规格帮不上**(本工程 C 的复杂度大头,与规格无关):LVGL 管线(离屏渲染 / 输入组 / 焦点 / 容器持久)、导航状态机、数据驱动(`g_menu_tab` 一表驱动)、运行时内存水位。这些靠 LVGL 功底 + `UI_设计架构.md` + 模拟器。
---
## 九、常见坑速查
| 坑 | 现象 | 对策 |
|---|---|---|
| PowerShell 重写含中文文件 | 整文件 U+FFFD 乱码(不可逆) | 只许 Edit 工具(C6) |
| 用了字体子集外的字 | 该字显示空白 | P0 字符集核对 + P1 字体重生成 |
| 字体缺字形(如宋体无 ▶) | 该字符显示成方框 □ | `make_font.py` 已内置 `merge_arrow_glyph()` 自动补回;其余缺字形同法(从有该字的字体提取位图合并,§4.4) |
| 新 .c 只注册了 Keil(或只改了 CMake) | 单端编译缺函数/静默缺失 | 双端注册(C9) |
| 模拟器主循环调 `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 池爆 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) |
| 斜线用细 `line_width`(如 2px) | 1bpp 阈值吃掉斜线边缘,腿偏细、比设计稿细 | 斜线加粗(腿 3px),或按段拆 `lv_line` 分粗细(如"浅 V"两腿 3 / 水平线 2) |
| 规格引入翻译不出来的效果 | P1 翻译不出来 | 1bpp 禁区(C8),P0 阶段就拦下 |
| UI 源文件(UTF-8 BOM)里 `snprintf` 格式串含中文 | 模拟器 cl.exe 报 C4819(代码页 936 误读 UTF-8 字节) | 格式化输出用 `lv_snprintf`(LVGL 自带,双端一致),勿用标准库 `snprintf` |
| 常驻样式用动态 `lv_style_t` + set_prop | LVGL 池永久占用:首次 set_prop 分配 `values_and_props` 且永不释放(x64 实测 88 B/个) | 常驻样式一律常量样式:`LV_STYLE_CONST_INIT(var, props[])` + `lv_style_const_prop_t[]`(属性存 Flash、零池分配,见`UI_设计架构.md` §5.6) |
---
## 十、小改动快速通道(简化流程)
| 改动类型 | 流程 |
|---|---|
| 文案修改 | 改规格 → 改代码 → **字符集核对**(有新字则重生成字体)→ P2 比对 |
| 间距/尺寸调整 | 改规格参数表 → 改代码 → P2/P3 合并为一轮验证 |
| 纯交互调整(键位/滚动) | 改规格/设计文档 → 改代码 → P2 验证行为 |
| 字体重生成 | 任何引入新字的改动后强制执行 §4.4(**最常见的漏项**) |
---
## 附录 A:设计规格模板骨架(NN_界面名_说明.md)
```markdown
# 界面 NN:<名称>(设计规格)
> 状态:制作中 / 已定稿 (YYYY-MM-DD)
> 参考图:mockups/NN_<名称>原始图片.png
> 本文件为设计基准:P1 照参数表翻译代码;P2 出图对照参数表 + 原图
## 一、布局参数表
| 元素 | x | y | w | h | 边框 | 内容/状态 |
|---|---|---|---|---|---|---|
| 页头 | … | … | … | … | … | … |
验证等式:`160 = 顶边距2 + … + 底边距3`(各项相加 = 屏幕尺寸)
## 二、行/元素内容与状态
…
## 三、字符集核对
- 命中(__):…
- 新增(__):…
## 四、待确认问题
- Q1:…
## 五、映射建议(菜单类界面)
…
## 六、LVGL 翻译要点
…
```
## 附录 B:单界面生产检查单(复制使用)
```
界面 NN:<名称> 负责人:____ 开始:____
P0 [ ] 台账登记(编号/作用/菜单位置)
[ ] 图片分析(结构/元素/文本逐字/比例/缺失)
[ ] 字体度量核对(与 §2.1 一致)
[ ] 字符集核对:__ 命中 / __ 新增
[ ] 布局计算(验证等式:____________)
[ ] NN_名称_说明.md 规格产出(参数表/内容/字符集/问题/映射/LVGL 要点)
[ ] 用户确认规格(日期:____)
P1 [ ] 代码翻译(与规格参数逐项一致)
[ ] 字体重生成(extract → make_font → diff 检查)
[ ] 新文件双端注册(如适用,C9)
[ ] 内存预算(实测水位:____KB / 40KB)
P2 [ ] 模拟器构建 0 错误
[ ] 离线出图:--screenshot shot.bin → i1_to_png.py → shot.png
[ ] shot.png 与参考图/规格参数表逐项比对(几何/文本/状态);不符按根因(修码/修规格)再出图(§5.1)
[ ] 导航行为验证(窗口模式按键)
[ ] 自动化回归:check_menu_rows.py + check_content_pages.py PASS
P3 [ ] /keil-build 0 错 0 警
[ ] /serial-boot-test PASS(Required:____ / Forbidden:FAIL;ERROR;HardFault)
[ ] 真机目检通过
P4 [ ] UI_设计架构.md 受影响章节已改成现状(不记流水)
[ ] 台账状态 → 已完成(代码+字体+字符集+规格+文档 一并提交)
```