d64bbdc57f
数据面(App/algo,纯逻辑,双端编译) - wave_ring:8 路 × 320 点列优先环(1 kHz,预触发 160 + 后触发 160),adcTask 唯一写者;触发/冻结/封存/冷却四态,冻结持续到落盘回执才解冻,滞留 2 s 自弃 兜底——下一次真故障无波可录比丢一段更贵 - wave_store:段头编解码 + 拉模式打包游标 + 自带无表位运算 CRC-16/MODBUS(带 锚点自检)+ 段名与整段长度等式;格式不依赖平台类型宽度 - 段头只存原始码值与封存瞬间生效系数,工程量只当单位标签、不进任何算式 - 三套主机单测(环 113 / 编解码 170 / 触发状态机 110),全部无桩、生产源原样 纳入;新文件补 stddef 是 ARMCC 与主机差异,主机侧照不出这条分叉 - test/host/wave_read.py 是按 §6.2 的独立第二实现(dump/fft/shape/plot),92 条 自洽断言;三份合成段互读双校验全过、哈希钉死,格式面双向闭合 - 三套新单测不加专属钩子,由覆盖率总闸全量真跑(沿用既有裁定) 接线(任务层) - adc_cal:新增生效系数的 32 位定点原值入口(与既有浮点入口共用生效视图), 段头按最低有效位精确记账,不经浮点回算 - adcTask:每拍压 8 路语义原始码值并推进状态机,封存那一拍同拍交棒封存瞬间事实; 台架定周期自抓走双层编译门,产品镜像里零存在;串口新增一条 wave: 周期行 - defaultTask:卷空闲后一次心跳整段搬完(try 锁 + 256 字节分片 + 一次完整开档到 关档),成败都回执;失败先删半成品再计错,段号只在成功时推进;持锁超 50 ms 补一条告警并记历史峰值(喂狗余量可核) - Lead 复核补第四道并发陷阱:采集任务优先级高于落盘道,搬段中途它可能正好把滞留 超时的本段自弃、甚至封出下一段——数据区校验和是对「实际读到的字节」算的, 半新半旧的段照样自洽,故关档后复检环侧身份判废:删文件、不补账、段号不推进 - Lead 复核抓到触发挂号不是原子操作(旧实现分三个字节写,消费者可在窗口里读到 上一段的触发源与元件号):已把载荷压进一个字节,越界当场拒且不记任何计数; 段文件字节布局零改动,三份合成段哈希逐字节不变 - protectTask:保护启动上升沿同点请求录波,一拍一次、元件号取最低位;判据本体 零改动 人机(160×160 单色屏) - 菜单树节点 26「手动录波」绑页面 + 记录查询组末新增「录波回放」叶,重跑生成器 得 43 节点 / 34 叶 / 30 已绑,页面映射 28 → 30 - 回放页一屏一路、触发点前后各一周波共 40 点、每点 3 像素阶梯线(只用水平与 垂直两种线段——单色屏斜线会被阈值裁掉);零点虚线、触发光标、窗口峰值、 单位徽标与形状受限徽标;空态与坏段各一句显式文案,绝不用半截波形冒充数据 - ui_data 新增录波注入通道(六件操作表、三态返回);执行侧胶水带段头缓存、 手动请求后连缓存一起作废、逐点走全仓唯一的码值换算入口 - 模拟器接真实环与编解码合成两段(含坏段与空态旗标),6 张新黄金参考,既有 60 张逐字节零漂移未用更新模式;字库 218 → 224 字 / 319 字形 - 两份图形配置里图表组件仍为关;零堆分配;页面池水位不变 - 回放读路径的 uiTask 阻塞上界按实码写清:一次渲染 = 段表刷新 + 未缓存段头最坏 22 次小读;已验段只剩一次开窗读 + 40 点解码;无实测数不编,实测归台架、 仪器用已在跑的单轮工作耗时探针,不新加埋点 台账与文档 - 内存台账:录波环与落盘/回放静态逐条入册,三档对照 81,536 → 88,296 → 89,936 字节(62.2% → 68.6%),累计 +8,400;预估 5.9 KB 与实测 +6,760 的差额点名; 散列文件头注释同步;任务栈表核对后无需抬栈;体积趋势行与同码凭证由权威构建 重打(0 错误 0 警告,四项体积与上一轮相同——本轮改动只有注释与声明位置) - 设计说明:§5 补第四条并发陷阱;§6.3 定字段口径(两份独立实现的共同依据); §7 回填实测;§9 周期行写实;§10 台架判据改两段式且不比绝对相位;§12 补读路径 风险行;抬头状态改成「代码面已交付、未做完的只有板级那一轮」 - 卡片:软件闭环一栏列证据;台架三条按用户裁定保持未完成,不做虚假销项 - 口径同步:数据可信、UI 补齐、页面补齐规划、产品需求、采集短板评测、任务规划 索引与两份 README;陈旧现状句清扫 16 处(带日期戳的历史取数行一律不动) - 门禁说明:nested-comment 记仓库根探测必须兼容 linked worktree 的 gitdir 文件 形态(修前该钩子在任务树里恒报环境错、静默不可用);coding-standard 记 「函数头注释超过 24 行就把函数功能挤出回看窗口」这条机制与本次现场 - 覆盖率基线登记两个新覆盖模块(套件 41 → 44 套);环单测重打后该文件有效行 227 → 226、覆盖行 226 → 225,比率仍 99.6%,是分母变小不是覆盖率回退 为什么是单一提交:文档链接、双端注册、生成器幂等(菜单与字库)、内存数字与 体积事实源、编号门禁这五道整仓门禁要求代码、页面、菜单树、字库、文档与台账 同处一个提交才自洽,按数据面/接线/人机/台账拆成四笔必然红其中至少四道 编号门禁新增行 14 处命中已按语义改写:魔数常量按字符命名、段名样例改由格式串 生成、频谱表头改 f / 2x / 5x 并加一行图例;改写后自洽断言 92 条仍全过
393 lines
28 KiB
Markdown
393 lines
28 KiB
Markdown
# 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: @build 0 错 0 警 + @test PASS + 上机目检通过" --> P4(["P4 归档<br/>→ 受影响章节现状化/台账 + 随代码提交(代码+字体+字集+规格+文档)"])
|
||
```
|
||
|
||
> 小改动(文案/间距/纯交互/字体重生成)走「小改动快速通道」一节(见文末第十节),无需全流程。
|
||
|
||
### 1.1 各阶段职责分工
|
||
|
||
| 阶段 | 用户做 | AI 做 | 产物 |
|
||
|---|---|---|---|
|
||
| P0 需求录入 + 设计规格 | 提供图片(每批 1~3 屏);说明各屏作用与菜单位置;回答问题;确认布局参数表 | 图片分析、界面登记、字体度量核对、字符集核对、布局计算(验证等式)、规格制作 | 台账 + `_说明.md`(参数表/字符集/问题/映射/翻译要点) |
|
||
| P1 规格→代码 | (可选)抽查代码 | 逐参数翻译、字体重生成、内存预算、双端注册 | 代码 + 字体 |
|
||
| P2 模拟器 | **视觉确认**(窗口模式/出图),指出差异 | 构建、离线出图、出图与原图逐项比对、依规格改码 | 比对结论 |
|
||
| P3 MCU | **真机目检**(DTU-RemoteLCD) | @build / @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. `@build` → **0 错误 0 警告**(AGENTS.md 强制;有警告看 `MDK-ARM/build_log.txt`)
|
||
2. `@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)
|
||
- [ ] @build 0错0警
|
||
- [ ] @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. **每批收尾**:字体统一重生成(各屏字符集**并集**,一次生成)→ 全量回归(已完成界面重跑 @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 [ ] @build 0 错 0 警
|
||
[ ] @test PASS(Required:____ / Forbidden:FAIL;ERROR;HardFault)
|
||
[ ] 真机目检通过
|
||
P4 [ ] UI_设计架构.md 受影响章节已改成现状(不记流水)
|
||
[ ] 台账状态 → 已完成(代码+字体+字符集+规格+文档 一并提交)
|
||
```
|