取值依据是实测水位,不是预估:菜单根框 5,368 B、开页-关页往返
5,448 B、最重页面峰值 6,568 B(占 16 KB 的 40%)、真机开机
used=3,192 B。注意 16,384 B 建池后 lv_mem_monitor 报的 total 是
15,020 B(TLSF 控制块与对齐元数据 ≈1,364 B),核算余量用后者。
两端 lv_conf.h 同步改值(lv-conf-sync 生效值比对 163 项 PASS),
Lib/lv_conf.h 的 UTF-8 BOM 已按原样补回(Edit 会静默剥掉)。
验证:
- @build 0 错 0 警;ZI 154,456 → 129,880,精确 −24,576 B
- SRAM1 98,144/74.9% → 73,568/56.1%(余 57,504 B);CCM 不变 87.9%
- 模拟器菜单回归 PASS(8 状态像素 + 池基线 5,368 B 未变);11 页
黄金参考逐字节 10/11 一致,往返池水位 5,448 B 分毫未变
- 唯一差异 netcfg_save 保存态:用 40 KB 旧池做 A/B 复测同样失败,
确认与本改动无关(属 506eccc 的 cfg_file 行为变化),未刷黄金
参考掩盖,留给该改动属主处置
- @test PASS:fw build Oct 15 2026 07:31:34,无 [ERR]/Fault,
开机 622 ms 到 net init: OK,menu build done: used=3192
- 本地 CI 全绿(L0 + L0.5,15 runner / 955 断言)
- 台账受管区与内存快照区已 --write 回填,mem-budget / mem-numbers
由红转绿
连带结论:R-2 后半待迁体量 54,146 → 29,570 B,瓶颈由 SRAM1 换到
CCM(只剩 7,904 B);解锁链第 ① 步完成,下一步只差缩堆 56→32 KB
(B5 是反向扩容,缩回前须重跑 stack_report.py + 上板 min_ever)。
文档同步 20 处:本报告按修复即销项删除 R-9 行,并改写 §0.2/§0.3/
§0.4/§5.1/§5.2/§5.3/§6.4 与变更记录、去处表;RAM使用分析 §2.6.2
新增实测水位表、阶段 1.1 标已执行;台账 §3 解锁链、登记簿 N-27、
任务规划 README W3 行与链路图;UI 四篇、产品需求、内存内容分配
策略、运行与自检记录、项目部署环境要求、测试开发标准流程、
simulator/README、ui-verify skill 常量。顺带订正三处与脚本不符的
旧值:池水位常量 4784/4864 → 5368/5448、FreeRTOS 堆 48 → 56 KB。
13 KiB
LVGL PC 模拟器部署与使用说明
适用于:
simulator/目录(LVGL v9.3.0 SDL2 模拟器) 用途:在 PC 上预览/调试 160×160 1bpp 单色菜单 UI,无需烧录 MCU
一、方案概述
1.1 背景
STM32F4-Base 的 UI 是 160×160 1bpp 单色菜单(基元控件页面导航:lv_obj 页面 + 返回按钮切换),使用自定义中文字体 ui_font_cn_12。在 MCU 上开发 UI 需要反复烧录调试,效率低。本模拟器在 PC 上直接编译运行相同的 UI 代码(ui_menu.c),实现所见即所得。
1.2 架构
simulator/main.c ──→ lv_init → sim_port_init(SDL 窗口 + 键盘)→ ui_menu_init(共享 MCU 代码)
│
└── 主循环:SDL 事件 → lv_timer_handler → lv_refr_now
| 模块 | 文件 | 说明 |
|---|---|---|
| 入口 | simulator/main.c |
SDL 初始化、主循环、键盘事件 |
| 端口 | simulator/sim/sim_port.c |
SDL 显示 + 键盘输入 + mono 主题初始化(替代 MCU 端 ui_port.c) |
| 配置 | simulator/lv_conf.h |
模拟器专用 LVGL 配置(1bpp + SDL 启用) |
| 共享 UI | Drivers/BSP/UI/ui_menu.c |
与 MCU 端共用,不做任何修改 |
| 共享字体 | Drivers/BSP/UI/ui_font_cn_12.c |
与 MCU 端共用 |
| LVGL 源码 | Lib/lvgl/ |
项目自带 v9.3.0,新增最小 CMakeLists.txt |
| SDL2 SDK | simulator/SDL2/SDL2-2.32.10/ |
本地预编译,无需安装 |
1.3 关键设计决策
- SDL2:LVGL 官方
lv_port_pc_vscode同款显示方案 - CMake + MSVC:
build_sim.ps1按本机 VS 版本自动选生成器(16=2019 / 17=2022 / 18=2026,取已装最高;-A x64)。不同机器 VS 版本不同,勿把生成器写死 - 仅 UI 预览:模拟器只跑 UI,不包含网络(netTask/ftpTask)等 MCU 业务
- 共享代码不复制:UI 源码直接引用
Drivers/BSP/UI/,MCU 与 PC 同步维护
二、环境要求
| 依赖 | 版本 | 说明 |
|---|---|---|
| CMake | ≥ 3.15 | 本机验证 4.3.0-rc1 |
| MSVC | VS2019 / 2022 / 2026 任一 | build_sim.ps1 自动探测生成器(16=2019 / 17=2022 / 18=2026,取已装最高),Hostx64/x64 |
| SDL2 | 2.32.10 | 已解压到 simulator/SDL2/,无需安装 |
| 操作系统 | Windows | 已验证 win32 |
说明:
simulator/SDL2/SDL2-2.32.10/是SDL2-devel-2.32.10-VC.zip解压后的目录,已包含include/、lib/x64/。
三、首次部署(一次性)
3.1 下载 SDL2(如未包含)
若 simulator/SDL2/ 为空,需手动下载解压:
- 下载
SDL2-devel-2.32.10-VC.zip(官方 GitHub Releases 或官网) - 解压到
simulator/SDL2/,确保目录结构为:simulator/SDL2/SDL2-2.32.10/ include/ ← SDL.h 及各头文件 lib/x64/ ← SDL2.lib, SDL2main.lib, SDL2.dll
⚠️ 重要:LVGL SDL 驱动内部写死
#include "SDL2/SDL.h"。原版 SDL2 包的头文件直接平铺在include/下(没有SDL2/子目录),因此需要先把头文件复制一份到include/SDL2/子目录:$inc = "simulator\SDL2\SDL2-2.32.10\include" New-Item -ItemType Directory -Path "$inc\SDL2" -Force Copy-Item "$inc\*.h" "$inc\SDL2\"如未执行此步骤,编译报
fatal error C1083: cannot open include file: "SDL2/SDL.h"。
3.2 配置 CMake 并构建
# 仓库根目录执行(自动探测本机 VS 版本:2019/2022/2026)
& "simulator\build_sim.bat"
手动等效:
cd simulator; cmake -B build -G "Visual Studio <NN> <YYYY>" -A x64; cmake --build build --config Debug,生成器号须与本机 VS 大版本一致(16=2019 / 17=2022 / 18=2026)。
生成的可执行文件:simulator/build/Debug/lvgl_sim.exe(SDL2.dll 由 POST_BUILD 自动复制到同目录)。
3.3 运行
cd simulator\build\Debug
.\lvgl_sim.exe
或使用 CMake 自定义目标:cmake --build build --config Debug --target run。
四、日常使用
4.1 构建 + 运行
# 仓库根目录执行(增量构建;生成器变化时自动删 build/ 重建)
& "simulator\build_sim.bat"
.\simulator\build\Debug\lvgl_sim.exe # 运行
改 CMakeLists.txt 后再跑一次
build_sim.bat即可(缓存不匹配会自动重建)。 遇到奇怪问题时加-Clean全量重建:& "simulator\build_sim.bat" -Clean
4.2 键盘操作
| 按键 | 功能 |
|---|---|
| ↑ / ↓ | 菜单项上下移动 |
| ← / → | 展开/折叠 / 导航 |
| Enter | 确认/进入子页 |
| Escape | 返回上级 |
说明:模拟器未创建鼠标,纯键盘导航(与 MCU 端 keypad 一致)。
4.3 修改 UI 代码
- 修改
Drivers/BSP/UI/ui_menu.c直接生效(重新编译模拟器即可预览) - 修改
Drivers/BSP/UI/ui_font_cn_12.c同样直接生效 - 不要在
simulator/下复制 UI 源码——保持共享,避免两份代码不同步
五、文件详解
5.1 simulator/CMakeLists.txt
| 配置项 | 作用 |
|---|---|
SDL2_DIR |
SDL2 SDK 根目录 |
LVGL_DIR |
指向 ../Lib/lvgl(项目自带源码) |
UI_DIR |
指向 ../Drivers/BSP/UI(共享 UI 代码) |
SDL2_INCLUDE_DIR |
必须在 add_subdirectory(lvgl) 之前定义,供 LVGL SDL 驱动找到 SDL2/SDL.h |
LV_CONF_DIR |
指向 simulator/ 自身,供 LVGL 找到模拟器版 lv_conf.h |
add_subdirectory("${LVGL_DIR}" lvgl_build) |
编译 LVGL 静态库 |
| UI 源文件 | 直接编译 ui_menu.c + ui_font_cn_12.c |
| POST_BUILD | 自动复制 SDL2.dll 到输出目录 |
5.2 Lib/lvgl/CMakeLists.txt(新增)
LVGL 官方源码原本没有 CMakeLists.txt(只有 src/),本工程新增了一个最小化版本:
file(GLOB_RECURSE ...)收集src/**/*.c编译成静态库lvgl- 通过
LV_CONF_DIR/SDL2_INCLUDE_DIR两个可选变量注入 include 路径 - 这些变量由上层
simulator/CMakeLists.txt在add_subdirectory前设置 - MSVC 下关闭警告(
/W0)
⚠️ 这个文件只被模拟器使用,不影响 Keil 的 MCU 编译(Keil 用自己的
.uvprojx工程)。
5.3 simulator/lv_conf.h(模拟器专用)
与 MCU 端 Lib/lv_conf.h 的差异:
| 配置 | MCU 端 | 模拟器 |
|---|---|---|
LV_USE_SDL |
0 | 1(SDL 驱动) |
LV_SDL_RENDER_MODE |
- | LV_DISPLAY_RENDER_MODE_PARTIAL(1bpp 必须) |
LV_SDL_INCLUDE_PATH |
- | "SDL2/SDL.h" |
LV_USE_THEME_MONO |
0 | 1(否则 1bpp 下文字黑底黑字不可见) |
LV_FONT_DEFAULT |
&ui_font_cn_12 |
同 |
LV_MEM_SIZE |
16KB | 16KB(必须与 MCU 端同值,lv-conf-sync 比对生效值;2026-10-15 由 40KB 同步收缩,PC 侧不再"放宽") |
5.4 simulator/sim/sim_port.c
MCU 端 ui_port.c 的 PC 替代实现:
lv_sdl_window_create(160,160)→ SDL 窗口lv_theme_mono_init(disp, true, LV_FONT_DEFAULT)→ 必须,否则黑屏lv_sdl_keyboard_create()→ 键盘输入(关联默认 group)- 无鼠标/触摸设备(纯键盘导航)
5.5 simulator/dbg_log.h
MCU 端 Drivers/BSP/dbg_log.h 会引用 sys_clock.h(RTC 时间戳),PC 上无法编译。simulator/dbg_log.h 是一个 stub:DBG_INFO/ERROR/DEBUG 直接映射到 printf,且 simulator/ 在 include 路径最前会优先生效,覆盖 MCU 版本。
若 UI 代码新增
DBG_*输出,会打印到模拟器控制台,方便调试。
六、注意事项与已知坑
6.1 构建相关
SDL2/SDL.h找不到:需先执行 3.1 的include/SDL2/头文件复制步骤。lv_conf.h找不到:LV_CONF_DIR必须在add_subdirectory(lvgl)之前定义(CMake 变量顺序问题)。当前 CMakeLists 已正确处理,勿随意调整顺序。- 改 CMakeLists.txt 后不生效:MSVC 生成器可能缓存旧配置,先删
build/重建。 - 中文字符串文件编码:源码是 UTF-8,CMake 已加
/utf-8编译选项消除 C4819 警告。不要用 PowerShellSet-Content等重写含中文的文件(参照 AGENTS.md 编码铁律)。
6.2 渲染相关
- 黑屏排查顺序:
LV_USE_THEME_MONO是否 = 1 且在sim_port_init中调了lv_theme_mono_init(1bpp 无主题 = 黑底黑字)- 主循环是否调用
lv_refr_now(NULL)强制首帧输出(PARTIAL 模式下保证) - 是否重复设置 tick:SDL 驱动内部
lv_tick_set_cb(SDL_GetTicks),不要再SDL_AddTimer+lv_tick_inc
- 中文字体缺字:
ui_font_cn_12只覆盖 83 个汉字(ASCII + 指定符号集),完整菜单需要 186 个唯一汉字。当前菜单部分文字会显示为空白,需重新生成字体(见下文 6.3)。 LV_SDL_RENDER_MODE必须为PARTIAL:1bpp + SDL 若非 PARTIAL,lv_sdl_window.c会#error编译失败。
6.3 字体生成(如需补全中文字符)
用 LVGL 官方 lv_font_conv(Node.js)重新生成:
# 生成包含全部菜单字符 + ASCII 的 12px 1bpp 字体
lv_font_conv --font simsun_regular.ttf --size 12 --bpp 1 \
-r 0x20-0x7E \
--symbols "<全部菜单用到的汉字,用引号包裹>" \
--format lvgl --lv-font-name ui_font_cn_12 \
-o Drivers/BSP/UI/ui_font_cn_12.c
菜单字符集合:
主界面 装置信息 实时数据 交流量 直流量 开入量 定值参数 定值 遥调 零压 电压 查看 遥测量 测量值 遥调系数 遥测系数 双字节 装置遥调 装置维护 时间设置 强制复归 手动录波 自动录波 通讯设置 通讯参数 网络配置 SNTP设置 记录查询 SOE记录 事故记录 操作记录 告警记录 变位告警 遥信记录 自检记录 定值记录 录波记录 厂家设置 元件设置 恢复默认 全部恢复 默认参数 默认定值 默认系数 元件参数 版本显示 测量遥调 直流遥调 开出遥调 装置记录 遥调记录 信号复归 版本信息(去重后 76 字)注意:字体符号集是有损的——
lv_font_conv对不在符号集内的字符不生成字形,运行时该字符渲染为空白。这是当前缺字的根因。
6.4 与 MCU 端的差异
| 项 | MCU(Keil) | 模拟器(CMake+MSVC) |
|---|---|---|
| LVGL 编译 | Keil 工程添加源文件 | 新增 Lib/lvgl/CMakeLists.txt |
| lv_conf.h | Lib/lv_conf.h |
simulator/lv_conf.h(独立,互不影响) |
| 显示端口 | ui_port.c(离屏 framebuffer) |
sim_port.c(SDL 窗口) |
| 输入 | 远端 keypad | SDL 键盘 |
| 业务任务 | netTask/ftpTask 等 | 无,纯 UI |
6.5 其他
run目标:CMake 定义的--target run会执行build/Debug/lvgl_sim.exe。- 窗口无法关闭:点击窗口关闭按钮会触发
SDL_QUIT正常退出;Escape 由 LVGL 键盘驱动处理(返回上级页)。 - 多显示器/高 DPI:SDL 窗口固定 160×160 物理像素,Windows 缩放可能放大显示,属正常现象。
七、常见问题(FAQ)
Q1: 编译报 cannot open include file "SDL2/SDL.h"?
A: 未执行 include/SDL2/ 头文件复制,见 3.1。
Q2: 编译报 cannot open include file "lv_conf.h"?
A: LV_CONF_DIR 变量未在 add_subdirectory(lvgl) 前定义,检查 simulator/CMakeLists.txt 变量顺序。
Q3: 运行后窗口全黑?
A: 按 6.2 黑屏排查顺序检查:mono 主题 → lv_refr_now → tick 不重复。
Q4: 菜单某些中文显示空白?
A: 中文字体缺字(覆盖 83 字,需要 186 字),见 6.3 重新生成字体。
Q5: 修改了 UI 代码但模拟器没变化?
A: 确认修改的是 Drivers/BSP/UI/ui_menu.c 而非 simulator/ 内的副本(本工程无副本);然后重新 cmake --build。
Q6: 能调试网络/存储等功能吗?
A: 不能。模拟器仅预览 UI,不含 CH395F/NAND/ADC 等硬件业务。
八、文件清单
| 路径 | 状态 | 说明 |
|---|---|---|
simulator/main.c |
新增 | 入口 |
simulator/sim/sim_port.c / .h |
新增 | SDL 端口 |
simulator/lv_conf.h |
新增 | 模拟器 LVGL 配置 |
simulator/dbg_log.h |
新增 | printf 日志 stub |
simulator/CMakeLists.txt |
新增 | 构建脚本 |
simulator/build_sim.bat / .ps1 |
新增 | 一键构建(VS2019/2022/2026 生成器自适应;.bat 绕过 PowerShell 执行策略) |
simulator/SDL2/SDL2-2.32.10/ |
新增 | SDL2 SDK(含 include/SDL2/ 子目录) |
Lib/lvgl/CMakeLists.txt |
新增 | LVGL 最小 CMake(仅模拟器用) |
Drivers/BSP/UI/ui_menu.c |
未改 | 共享 UI 代码 |
Drivers/BSP/UI/ui_font_cn_12.c |
未改 | 共享字体(缺字待补) |
simulator/build/ |
生成物 | 构建输出,可随时删除重建 |