a7f526569a
取值依据是实测水位,不是预估:菜单根框 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。
286 lines
20 KiB
Markdown
286 lines
20 KiB
Markdown
# LVGL PC 模拟器(simulator/)
|
||
|
||
> 在 PC 上编译运行与 MCU **同一份** UI 源码(`Drivers/BSP/UI/ui_menu.c` + `ui_font_cn_12.c`),
|
||
> 实时预览 160×160 1bpp 单色菜单界面,**无需烧录 MCU**。
|
||
>
|
||
> 部署细节与完整 FAQ 见 `docs/UI设计/LVGL模拟器部署与使用说明.md`;本文聚焦**本目录的内容与架构**。
|
||
|
||
---
|
||
|
||
## 一、目录内容
|
||
|
||
```
|
||
simulator/
|
||
├── CMakeLists.txt # 构建脚本(CMake + MSVC,x64)
|
||
├── build_sim.bat / .ps1 # 一键构建入口(自动探测 VS2019/2022/2026 生成器,x64;.bat 绕过执行策略)
|
||
├── main.c # 入口:SDL 初始化 → LVGL → 菜单 → 主循环
|
||
├── sim/ # 平台层(PC 专用实现;MCU 端对应实现在各 BSP 目录)
|
||
│ ├── sim_port.c / .h # 显示/输入端口(SDL 窗口 + 键盘 + mono 主题),替代 MCU 端 ui_port.c
|
||
│ ├── net_socket_sim.c # 网络后端:Winsock 实现 net_* BSD API,替代 MCU 端 net_socket.c(CH395F)
|
||
│ ├── ui_net_sink_backend_sim.c # RemoDispBus 平台 backend:取帧/注键(配合共享 ui_net_sink.c)
|
||
│ └── cfg_file_store_sim.c # 配置文件存储后端:本地 sim_config.ini(旧档 sim_netcfg.bin 已废弃,不读不删)
|
||
├── lv_conf.h # 模拟器专用 LVGL 配置(与 MCU 端 Lib/lv_conf.h 相互独立)
|
||
├── i1_to_png.py # 截图转 PNG:把 --screenshot 的 I1(1bpp) 原始位图转成 PNG(见 §3.5)
|
||
├── tests/ # 无头回归脚本 + 本地黄金参考(见 §3.6,流程文档 docs/UI设计/UI_自动化测试流程.md)
|
||
│ ├── check_menu_rows.py # 菜单 8 状态像素回归 + 池基线门禁
|
||
│ ├── check_content_pages.py # 文本断言 + 内容页黄金参考回归 + 往返池门禁(--update 刷参考)
|
||
│ ├── check_remo_sink.py # RemoDispBus 远端推流协议回归(起窗口模拟器 + TCP 取帧 + 参考比对)
|
||
│ ├── regen_ref_png.py # 由 ref/*.bin 批量重生成目检 PNG(--scale 6 --invert)
|
||
│ └── ref/ # 黄金参考 shot_*.bin + 目检 *.png(**本地生成物,不入库**)
|
||
├── dbg_log.h # 调试日志 stub:DBG_* 映射到 printf(覆盖 MCU 版头文件)
|
||
├── SDL2/
|
||
│ └── SDL2-2.32.10/ # SDL2 预编译 SDK(随仓库提供,无需安装)
|
||
│ ├── include/ # SDL2 头文件
|
||
│ │ └── SDL2/ # ★ 头文件副本(LVGL SDL 驱动写死 #include "SDL2/SDL.h")
|
||
│ └── lib/
|
||
│ ├── x64/ # SDL2.lib / SDL2main.lib / SDL2.dll(构建使用)
|
||
│ └── x86/ # 32 位库(当前未使用)
|
||
└── build/ # CMake 生成物(可整体删除重建,勿提交)
|
||
└── Debug/lvgl_sim.exe # 可执行文件(SDL2.dll 由 POST_BUILD 自动复制到同目录)
|
||
```
|
||
|
||
### 文件职责
|
||
|
||
| 文件 | 说明 |
|
||
|------|------|
|
||
| `main.c` | 程序入口。`SDL_Init(TIMER\|EVENTS)` → `lv_init()` → `sim_port_init(160,160)` → `ui_menu_init()`(共享代码)→ 主循环(SDL 事件 + `lv_timer_handler` + `lv_refr_now`) |
|
||
| `sim/sim_port.c` | 创建 SDL 窗口显示设备(`lv_sdl_window_create`)、初始化 **mono 主题**(`lv_theme_mono_init`,1bpp 必需)、创建键盘输入设备并挂默认输入组。**不创建鼠标**(纯键盘导航,与 MCU 端 keypad 一致)。`sim_port_get_screen_mono()` 经 `SDL_RenderReadPixels` 读回屏幕像素转 1bpp,供推流取帧 |
|
||
| `sim/net_socket_sim.c` | 网络后端:Winsock 实现 `net_*` BSD Socket API(`socket`/`bind`/`listen`/`accept`/`recv`/`send`/`close`),替代 MCU 端 `Drivers/BSP/NET/net_socket.c`(CH395F) |
|
||
| `sim/ui_net_sink_backend_sim.c` | RemoDispBus 平台 backend:`ui_net_sink_get_lcdmem`→`sim_port_get_screen_mono`、`ui_net_sink_inject_key`→`lv_group_send_data`;配合**共享** `Drivers/BSP/UI/ui_net_sink.c`(协议服务端,双端同一份,TCP 7003) |
|
||
| `sim/cfg_file_store_sim.c` | 通用配置文件存储后端:本地文件 `sim_config.ini`(`fopen`/`rename` 原子写),替代 MCU 端 `App/cfg/cfg_file_store_mcu.c`(FatFS);旧档 `sim_netcfg.bin` **已废弃**——不读、不删、不迁移(2026-10-10) |
|
||
| `lv_conf.h` | 模拟器专用 LVGL 配置。与 MCU 端 `Lib/lv_conf.h` 的关键差异见 §3.3 |
|
||
| `dbg_log.h` | MCU 版 `dbg_log.h` 依赖 `sys_clock.h`(RTC 时间戳),PC 无法编译。此 stub 把 `DBG_INFO/ERROR/DEBUG` 映射到 `printf`;因 `simulator/` 在 include 路径最前,**优先生效**覆盖 MCU 版 |
|
||
| `build_sim.bat` / `build_sim.ps1` | 一键构建入口:vswhere 探测本机 VS 大版本(16=2019 / 17=2022 / 18=2026,取已装最高)→ 映射 CMake 生成器;`build/` 缓存生成器与本机不符(换机/升级 VS)时自动删除重建(带重试,抗文件锁)。**构建入口一律走它,文档/命令中勿写死生成器版本**;`.bat` 仅负责绕过 PowerShell 执行策略并转交 `.ps1` |
|
||
| `CMakeLists.txt` | 收集 LVGL 源码(`../Lib/lvgl`)+ 共享 UI 源码(`../Drivers/BSP/UI`)+ 通用配置文件引擎(`../App/cfg_file.c`、`../Drivers/BSP/NETCFG` 的 net_cfg;`net_cfg_legacy` 已于 2026-10-10 删除)+ `sim/` 平台层,链接预编译 SDL2,产出 `lvgl_sim.exe` |
|
||
| `i1_to_png.py` | 把 `--screenshot` 产出的 I1(1bpp) 原始位图转成 PNG,便于离线比对设计稿。详见 §3.5 |
|
||
| `tests/` | 无头回归脚本(菜单 8 状态像素回归、内容页文本断言 + 黄金参考回归);`ref/` 为**本地生成物、不入库**(首次 `--update` 生成)。详见 §3.6 |
|
||
|
||
> **共享代码不复制**:`ui_menu.c` / `ui_font_cn_12.c` / `ui_net_sink.c` 直接引用 `Drivers/BSP/UI/` 下的原件,
|
||
> MCU 与 PC 两端永远同步。**不要**在本目录内复制 UI 源码。
|
||
|
||
---
|
||
|
||
## 二、架构框架
|
||
|
||
### 2.1 运行架构
|
||
|
||
```
|
||
main.c
|
||
│
|
||
├─ SDL_Init(TIMER | EVENTS) # 仅初始化计时/事件,视频由 LVGL SDL 驱动内部接管
|
||
├─ lv_init() # LVGL v9.3.0 内核
|
||
├─ sim_port_init(160, 160)
|
||
│ ├─ lv_group_create + set_default # 默认输入组
|
||
│ ├─ lv_sdl_window_create(160,160) # SDL 窗口 = 显示设备(I1 单色)
|
||
│ ├─ lv_theme_mono_init(disp, true, # mono 主题(白字黑底),1bpp 下不初始化 = 黑屏
|
||
│ │ LV_FONT_DEFAULT)
|
||
│ └─ lv_sdl_keyboard_create # 键盘 → 默认输入组(方向键/Enter/Esc)
|
||
├─ ui_menu_init() # ★ 与 MCU 共用(Drivers/BSP/UI/ui_menu.c)
|
||
│ # 基元控件页面导航,节点表驱动
|
||
└─ 主循环
|
||
├─ SDL_PollEvent # SDL_QUIT → 退出
|
||
├─ lv_timer_handler() # 驱动 LVGL 定时器/重绘
|
||
├─ lv_refr_now(NULL) # 强制整屏刷新(PARTIAL 模式下保证首帧输出)
|
||
└─ Sleep(lv_timer_handler 返回值) # 自适应休眠
|
||
```
|
||
|
||
### 2.2 渲染链路(1bpp 单色)
|
||
|
||
```
|
||
LVGL 场景(基元控件页面/label,LV_COLOR_FORMAT_I1)
|
||
→ SW 渲染器(仅启用 I1,LV_DRAW_SW_SUPPORT_I1=1)
|
||
→ SDL 窗口 blit(LV_SDL_RENDER_MODE_PARTIAL,1bpp 强制要求)
|
||
→ 屏幕(160×160 物理像素,Windows 高 DPI 可能放大显示,属正常)
|
||
```
|
||
|
||
对照 MCU 端链路(`ui_port.c`):
|
||
|
||
```
|
||
LVGL 场景(同一份 ui_menu.c)
|
||
→ SW 渲染器(I1)
|
||
→ flush 回调 memcpy 到内存帧缓冲 g_ui_fbuf(3200 B)
|
||
→ RemoDispBus 协议(TCP 7003)按需推给远端客户端显示
|
||
```
|
||
|
||
两端**场景层完全相同**,只有"渲染结果去向"不同:模拟器进 SDL 窗口,MCU 进内存帧缓冲再走网络。
|
||
|
||
### 2.3 MCU 端 vs 模拟器 对照
|
||
|
||
| 项 | MCU(Keil MDK) | 模拟器(CMake + MSVC) |
|
||
|----|----------------|------------------------|
|
||
| LVGL 编译 | `.uvprojx` 工程添加 `Lib/lvgl` 源文件 | `Lib/lvgl/CMakeLists.txt`(最小 CMake,**仅模拟器用**,不影响 Keil 编译) |
|
||
| lv_conf.h | `Lib/lv_conf.h` | `simulator/lv_conf.h`(独立,互不影响) |
|
||
| 显示端口 | `ui_port.c`(离屏 framebuffer + flush 回调) | `sim_port.c`(SDL 窗口) |
|
||
| 输入 | 远端 keypad(`ui_port_queue_key`,网络回传键码) | SDL 键盘(`lv_sdl_keyboard_create`) |
|
||
| 主题 | 无主题(`LV_USE_THEME_* = 0`),控件用内置基础样式 | **mono 主题必须开启**(否则 1bpp 黑底黑字) |
|
||
| 日志 | `Drivers/BSP/dbg_log.h`(USART1 + RTC 时间戳) | `simulator/dbg_log.h`(printf stub) |
|
||
| 业务任务 | netTask / ftpTask / timeSyncTask 等 | 无(仅编译纯软件部分的配置文件引擎 net_cfg/cfg_file,可离线验证配置加载/保存) |
|
||
| 时钟 | `lv_tick_inc` 由 ui_task 手动喂(10ms 周期) | SDL 驱动内部 `lv_tick_set_cb(SDL_GetTicks)`,**不可再手动喂** |
|
||
|
||
### 2.4 关键设计决策
|
||
|
||
1. **SDL2**:LVGL 官方 PC 端口同款显示方案;使用仓库内预编译 SDK(`SDL2/SDL2-2.32.10/`),无需系统安装
|
||
2. **CMake + MSVC**:`build_sim.ps1` 按本机 VS 版本自动选生成器(16=2019 / 17=2022 / 18=2026;不同机器版本不同,**勿写死**),`-A x64`;MSVC 下加 `/utf-8` 消除中文注释的 C4819 警告
|
||
3. **共享 UI 源码**:`ui_menu.c` / `ui_font_cn_12.c` 直接编译 `Drivers/BSP/UI/` 原件,改一处两端同时生效
|
||
4. **纯键盘导航**:不创建鼠标/触摸设备,与 MCU 端 keypad 操作语义一致(↑↓←→ / Enter / Esc)
|
||
5. **UI 为主**:不含 CH395F 网络 / NAND 存储 / ADC 等硬件业务;通用配置文件引擎(`cfg_file`)与 `net_cfg` 核心为纯软件,配置加载/保存可在模拟器离线验证(`sim_config.ini`;旧档 `sim_netcfg.bin` 已废弃、不参与)
|
||
|
||
---
|
||
|
||
## 三、构建与运行
|
||
|
||
### 3.1 环境要求
|
||
|
||
| 依赖 | 版本 | 说明 |
|
||
|------|------|------|
|
||
| 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 | 已内置于 `SDL2/`,无需安装 |
|
||
| 操作系统 | Windows | 已验证 win32 |
|
||
|
||
### 3.2 构建命令
|
||
|
||
```powershell
|
||
# 一键构建(仓库根目录执行;自动探测本机 VS2019/2022/2026 生成器,
|
||
# build/ 缓存生成器与本机不符时自动删除重建)
|
||
& "simulator\build_sim.bat"
|
||
|
||
# 运行
|
||
.\simulator\build\Debug\lvgl_sim.exe
|
||
# 或:cmake --build simulator\build --config Debug --target run
|
||
|
||
# 全量重建
|
||
& "simulator\build_sim.bat" -Clean
|
||
```
|
||
|
||
> 改 `CMakeLists.txt` 后再跑一次 `build_sim.bat` 即可(缓存不匹配会自动重建);遇到奇怪问题加 `-Clean` 全量重建。
|
||
> 手动 CMake 等效:`cd simulator; cmake -B build -G "Visual Studio <NN> <YYYY>" -A x64; cmake --build build --config Debug`,生成器号须与本机 VS 大版本一致(16=2019 / 17=2022 / 18=2026)。
|
||
|
||
### 3.3 `lv_conf.h` 与 MCU 端的关键差异
|
||
|
||
| 配置 | MCU 端(`Lib/lv_conf.h`) | 模拟器(本目录) | 原因 |
|
||
|------|--------------------------|------------------|------|
|
||
| `LV_USE_SDL` | 0 | **1** | SDL 显示/键盘驱动 |
|
||
| `LV_SDL_RENDER_MODE` | — | **`PARTIAL`** | 1bpp + SDL 非 PARTIAL 会 `#error` |
|
||
| `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` | 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`) | 模拟器略宽松,便于调试 |
|
||
|
||
### 3.4 键盘操作
|
||
|
||
| 按键 | 功能 |
|
||
|------|------|
|
||
| ↑ / ↓ | 菜单项上下移动 |
|
||
| → | 进入子页 |
|
||
| Enter | 确认 / 进入子页 |
|
||
| ← / Escape | 返回上级 |
|
||
|
||
### 3.5 截图比对设计稿(离线出图)
|
||
|
||
窗口模式只适合人工目检。要把渲染结果存成 PNG 与设计参考(`docs/UI设计/mockups/` 下的原始参考图)做目视比对,用离屏截图 + 转 PNG:
|
||
|
||
```powershell
|
||
cd simulator
|
||
# 1) 离屏渲染首帧,输出 I1(1bpp) 原始位图(已剥离 8 字节调色板,纯像素数据)
|
||
.\build\Debug\lvgl_sim.exe --screenshot shot.bin
|
||
# 2) 转 PNG(默认 160x160;--scale 放大便于查看;python 统一用仓库 .venv,见 AGENTS.md「Python 环境」)
|
||
..\.venv\Scripts\python.exe i1_to_png.py shot.bin # -> shot.png
|
||
..\.venv\Scripts\python.exe i1_to_png.py shot.bin -o 预览.png --scale 6
|
||
```
|
||
|
||
**I1 位图格式**(`i1_to_png.py` 据此解析):1 位/像素,每字节 MSB 在左(bit7 = 该行最左像素),`bit=1` 前景 / `bit=0` 背景,每行 `宽/8` 字节、行内无填充。
|
||
|
||
`i1_to_png.py` 常用参数:
|
||
|
||
| 参数 | 说明 |
|
||
|------|------|
|
||
| `-o, --output` | 输出 PNG 路径(默认与输入同名 `.png`) |
|
||
| `-W, -H` | 宽高(默认 160/160,非默认分辨率时必填) |
|
||
| `--scale N` | 最近邻放大 N 倍,便于查看小图 |
|
||
| `--invert` | 前/背景互换 |
|
||
| `--fg / --bg` | 自定义前/背景色 `R,G,B`(默认黑白) |
|
||
| `--no-auto-palette` | 关闭调色板自动识别(按纯像素解析) |
|
||
|
||
> 依赖 Pillow(已含于仓库 `.venv`,见 `requirements.txt`)。`shot.bin`/`shot*.png` 均已列入 `.gitignore`,可随时删除重生成。
|
||
|
||
### 3.6 无头回归测试(tests/)
|
||
|
||
UI 改动的自动化门禁脚本(完整流程见 `docs/UI设计/UI_自动化测试流程.md`)。除 `check_remo_sink.py`(需 Pillow,`.venv` 已含)外均为纯标准库、任意 python3 均可;为环境统一,一律用仓库 `.venv` 运行:
|
||
|
||
```powershell
|
||
# 仓库根目录执行(前置:本目录已构建出 build/Debug/lvgl_sim.exe)
|
||
.venv\Scripts\python.exe simulator/tests/check_menu_rows.py # 菜单 8 状态像素回归 + 池基线门禁
|
||
.venv\Scripts\python.exe simulator/tests/check_content_pages.py # 文本断言 + 内容页黄金参考回归 + 往返池门禁
|
||
.venv\Scripts\python.exe simulator/tests/check_remo_sink.py # RemoDispBus 远端推流协议回归(起窗口模拟器 + TCP 取帧 + 参考比对)
|
||
```
|
||
|
||
| 脚本 | 原理 | 失败输出 |
|
||
|------|------|----------|
|
||
| `check_menu_rows.py` | 键序注入(u/d/e/s)跑 8 个菜单状态,断言选中行白带、非选行黑底白字、框体边框、往返状态逐像素一致;S0 池水位须等于脚本内 `MENU_POOL_BASELINE` | 未过断言清单 + 保留临时截图 |
|
||
| `check_content_pages.py` | ① 文本断言(解析字体字形,在截图内精确匹配期望串,不依赖 `ref/`)② 11 内容页无头截图与 `tests/ref/shot_*.bin` **逐字节**比对;另校验开页-关页往返池水位 = `POOL_AFTER_ROUNDTRIP` | 文本未命中项 / 差异像素数 + 双方 ink runs 对照 |
|
||
| `check_remo_sink.py` | 启动**窗口**模拟器(非无头,`CREATE_NEW_PROCESS_GROUP` 防管道阻塞),经 TCP 7003 发 LCDMEM 取 1bpp 帧;断言首帧=LCDMEM(无 INIT 污染)/位图 3200 字节/非平凡;复用 `i1_to_png` 出目检 PNG;与无头参考(`lvgl_sim --screenshot`)逐位比对 <2% 判 MATCH。守护"INIT 污染 / 快照卡死"两个历史缺陷 | 各判据失败原因 + `sim_err.txt` 尾部 + 保留 PNG(默认不删,`--clean` 才删) |
|
||
|
||
- **黄金参考与目检 PNG**(`ref/` 不入库):首次/换机后先生成一次——
|
||
`.venv\Scripts\python.exe simulator/tests/check_content_pages.py --update` 出 `ref/*.bin`,
|
||
`.venv\Scripts\python.exe simulator/tests/regen_ref_png.py` 出目检 `ref/*.png`(默认 `--scale 6 --invert`)。
|
||
有意改视觉时先肉眼确认新效果正确再 `--update`(会先跑文本断言防误刷)。
|
||
- **池水位常量漂移**:菜单/页面结构变更导致门禁失败时,确认新值合理后再更新脚本内常量并登记流程文档变更记录;无法解释的漂移 = 内存回归 bug。
|
||
- 退出码均为 0=PASS / 1=断言失败 / 2=环境错误(exe 缺失等)。
|
||
|
||
### 3.7 配置文件自检(--cfgtest)
|
||
|
||
配置引擎"加载 → 轮询 → 重载 → 保存"的进程内闭环验证(无需窗口 / LVGL,退出码 0=PASS / 非 0=FAIL),在当前工作目录产生 `sim_config.ini`:
|
||
|
||
```powershell
|
||
.\build\Debug\lvgl_sim.exe --cfgtest
|
||
```
|
||
|
||
验证链:`net_cfg_load`(旧档迁移已于 2026-10-10 删除,只走 INI)→ 第 1 次 poll 建基线 → 伪造"外部编辑"(原子写,等价 FTP 上传/文本编辑器)→ 2 次 poll 沉降(对应真机 4s 沉降)→ 断言 RAM 副本已更新为新值 → 对已存在文件做外科式保存、回读校验注释/值无损。
|
||
|
||
空目录运行 = 验证出厂模板生成;超限文件(>4096 B)= 判损坏并重建出厂模板;超限文档保存 = 拒绝且不动原文件。窗口模式主循环以与 MCU netTask 相同的 2s 周期调用 `cfg_file_poll()`:外部文件变更沉降后自动重载 `[network]` 段(模拟器无网络栈,变更回调为空操作,仅日志可见)。
|
||
|
||
---
|
||
|
||
## 四、已知坑(构建/运行)
|
||
|
||
1. **`cannot open include file "SDL2/SDL.h"`**
|
||
LVGL SDL 驱动写死 `#include "SDL2/SDL.h"`,而原版 SDK 头文件平铺在 `include/` 下。
|
||
必须把头文件复制一份到 `include/SDL2/` 子目录(本仓库已做好):
|
||
```powershell
|
||
$inc = "simulator\SDL2\SDL2-2.32.10\include"
|
||
New-Item -ItemType Directory -Path "$inc\SDL2" -Force
|
||
Copy-Item "$inc\*.h" "$inc\SDL2\"
|
||
```
|
||
2. **`cannot open include file "lv_conf.h"`**
|
||
`LV_CONF_DIR` / `SDL2_INCLUDE_DIR` 两个 CMake 变量必须在 `add_subdirectory(lvgl)` **之前**定义(见 `CMakeLists.txt` 中的顺序注释),勿随意调整。
|
||
3. **运行后窗口全黑**,按序排查:
|
||
- `LV_USE_THEME_MONO = 1` 且 `sim_port_init` 中调用了 `lv_theme_mono_init`
|
||
- 主循环调用了 `lv_refr_now(NULL)`(PARTIAL 模式保证首帧)
|
||
- 没有重复设置 tick(SDL 驱动内部已 `lv_tick_set_cb`,**不要**再 `SDL_AddTimer` + `lv_tick_inc`)
|
||
4. **中文显示空白**:字体 `ui_font_cn_12` 是子集字体(当前 122 汉字/符号 + ASCII,共 217 字形),缺字渲染为空白。
|
||
补字流程见 `docs/UI设计/UI_中文字体生成与配置.md`(`tools/font_gen/` 两步脚本)。
|
||
5. **改了 UI 代码但模拟器没变化**:确认改的是 `Drivers/BSP/UI/ui_menu.c`(共享原件)而非本目录副本(本目录无副本),然后重新 `cmake --build`。
|
||
6. **编码**:UI 源码为 UTF-8(带 BOM),MSVC 已加 `/utf-8`。**不要**用 PowerShell `Set-Content` 等重写含中文的文件(编码铁律,见根目录 `AGENTS.md`)。
|
||
7. **键盘输入无响应(方向键/Enter 无效)**:`main.c` 主循环**不得**调用 `SDL_PollEvent`。
|
||
LVGL SDL 驱动内部有 5ms 定时器(`lv_sdl_window.c` 的 `sdl_event_handler`)统一轮询分发 SDL 事件;
|
||
主循环若先轮询会消费掉事件队列,键盘输入永远到不了 LVGL(现象:画面正常但按键全部无效)。
|
||
关窗退出由驱动 `LV_SDL_DIRECT_EXIT=1` 自动 `exit(0)`,主循环无需处理 SDL_QUIT。
|
||
|
||
---
|
||
|
||
## 五、边界说明
|
||
|
||
- 模拟器**跑 UI + 配置文件引擎**:不含 CH395F 网络、NAND 存储、ADC、RS-485 等硬件业务;`cfg_file` 引擎与 `net_cfg`(纯软件部分)在模拟器编译运行,可离线验证配置加载/保存
|
||
- 模拟器**含** `ui_net_sink`(Winsock 版 RemoDispBus 推流服务端,TCP 7003)——远端看图链路
|
||
可在模拟器上验证(`simulator/tests/check_remo_sink.py`);CH395F 真实网络栈仍只在 MCU 上验证
|
||
- `build/` 为生成物,可随时删除重建(已在 `.gitignore` 中忽略)
|
||
- `Lib/lvgl/CMakeLists.txt` 仅被本模拟器引用,**不影响** Keil 的 MCU 编译
|
||
|
||
> **模拟器跑过 ≠ 真机跑过**:本目录与真机的**全部已知差异**(网络 pending 机制缺失、代次号恒 0、
|
||
> 日志/配置的 mtime 粒度与堆后端差异、IWDG/NAND/RTC 等硬件不可建模)统一登记在
|
||
> [`../docs/测试说明/模拟器差距清单.md`](../docs/测试说明/模拟器差距清单.md)——
|
||
> 改动触及 `sim/` 任何后端时,**必须同步在那里登记一行**;改动前先看 `docs/测试说明/模拟器差距清单.md` §3 判断是否必须回真机。
|