运行/自检记录与待机显示设计说明
版本:v1.1(已实施)
日期:2026-09-14
状态:S1–S9 全部完成——双端构建 0/0、--logtest PASS、ui-verify 五门禁全绿;真机 @test 待上电回归(§8.2)
关联文档:项目架构总览.md · STM32F4-Base存储架构说明.md · 系统墙钟时间维护说明.md · UI设计/内容页架构.md(如存在)· docs/UI设计/mockups/(待机显示页面.jpg / 运行记录页.jpg / 自检记录页.jpg / 密码页面实际机器的示例图片.jpg)
1. 背景与需求
| # |
需求 |
说明 |
| R1 |
密码错误提示 |
用户密钥页输错密码时提示"密码输入错误",并允许重新输入(现状:仅清空重输、无提示) |
| R2 |
待机显示页 |
长时间无操作时默认显示的界面(参考 mockups/待机显示页面.jpg:6 路电流值 + 底部时钟);只有按确认键才进入菜单 |
| R3 |
运行记录页 |
记录装置上电时间、重启时间(参考 mockups/运行记录页.jpg:标题 + 逐条"时间戳 + 事件" + 页脚分页) |
| R4 |
自检记录页 |
记录开机自检结果,有问题才记录(参考 mockups/自检记录页.jpg) |
| R5 |
FTP 可读存储 |
记录以纯文本文件落盘,FTP(根目录 = FatFS 卷根)可直接下载读取 |
| R6 |
先调研后设计 |
网络调研存储方式选型 → 写入本设计文档 → 按文档实施 |
2. 现状分析
2.1 UI 框架(Drivers/BSP/UI/,MCU/模拟器共用)
- 内容页 = 描述符驱动:
content_page_def_t(标题/几何/行数据源/4 钩子),框架 ui_content_page.c 统一建屏、切屏、焦点、按键分发与绘制;页面模块零 LVGL 对象。
- 分页已内建:
selectable=1 + rows_per_page(↑↓移选、窗口跟随)+ 页脚"第 X 页,共 Y 页"自动计算;动态行经 refresh(row, name, value) 逐行取数(栈缓冲 32B/列),行数经 count_cb()。
- 菜单树已含目标叶子:
ui_menu_tree.txt "记录查询"下已有 自检记录 = node 39、运行记录 = node 40(当前为空白终点框,无内容页映射);无需改树。
- 密码页
ui_page_password.c:3 位数字框 + ↑↓←→/ENTER;输错仅清空重输(R1 缺口)。
- 按键流:物理/远端键 → LVGL keypad → 默认输入组 → 焦点对象事件回调(菜单容器
menu_key_cb / 内容页容器 cp_key_cb)——空闲检测可在此统一挂钩。
- 字体:子集位图字体
ui_font_cn_12,字符集 ui_cn_chars.txt(当前 122 汉字/符号 + ASCII)。
2.2 存储与时间
| 项 |
现状 |
| 卷 |
GD5F2GQ5UE SPI NAND 256MB(2048 块 × 64 页 × 2KB),dhara FTL(磨损均衡/坏块管理),FatFS FAT32,FF_USE_LFN=0 → 文件名必须 8.3 |
| FTP |
lftpd 根目录 = 卷根 /;现有文件 /config.ini(8.3);旧档 /cfg/netcfg.bin 已废弃——现固件不读不写也不删(仅可能残留在旧设备上) |
| 并发 |
FF_FS_REENTRANT=1 + 系统锁(ffsystem.c osMutex):单次 f_* 调用受保护,多调用序列(open→seek→write→close)需单写者纪律 |
| 墙钟 |
sys_clock_get_str(buf,20) → "YYYY-MM-DD HH:MM:SS";sys_clock_get() → Unix 秒(失败 = UINT32_MAX);SD2506 RTC + timeSyncTask TCP 校时 |
| 挂载 |
defaultTask:f_mount(forced=1),空卷自动 f_mkfs FAT32,成功后置 SYS_EVT_STORAGE_READY |
2.3 现有自检检查点(v1 埋点位置)
| 检查点 |
位置 |
失败表现 |
| RTC 读取/墙钟建立 |
app_main_init():sd2506_init() + sys_clock_init()(FreeRTOS 启动前) |
时间戳无效,后续日志无意义时间 |
| NAND 初始化 |
app_main_init():gd5f2gq5ue_init() 返回非 GD5F_OK |
存储不可用 |
| ADC 初始化 |
app_main_init():tpafe5160_init() 超时 |
采样不可用 |
| FatFS 挂载/建卷 |
defaultTask:f_mount/f_mkfs 失败(置位 STORAGE_READY 前) |
FTP/配置/记录全部不可写 |
| 网络链路 |
net_task net_stack_bring_up():PHY 等待 ≤3s 后仍 DISCONNECT |
组网可用但无链路 |
2.4 RAM/LVGL 池预算(依据 docs/内存管理/RAM使用分析与优化方案.md)
- FreeRTOS 堆真实余量 ≈ 11~12KB;LVGL 池 16KB 静态,菜单常驻占用 ≈5KB。
- 本设计新增 RAM:全部 < 1KB(§7),不新增任务、不动任务栈。
3. 网络调研摘要(R6)
| 主题 |
结论 |
来源 |
| 日志行格式与时间戳 |
syslog(RFC 5424):一行一条记录;TIMESTAMP 采用 ISO 8601/RFC 3339(YYYY-MM-DDTHH:MM:SS[.frac][Z],机器可读、字典序可排序);MSG 用 UTF-8。本设计取空格变体 YYYY-MM-DD HH:MM:SS(与既有 sys_clock_get_str 输出一致),另附 epoch 秒字段供程序化解析 |
RFC 5424 §6.2.3/§6.4(datatracker.ietf.org/doc/html/rfc5424) |
| 写入模型 |
Linux printk:消息先进 RAM 环形缓冲,由专用线程异步刷持久存储;热路径禁止同步 I/O。本设计对应"RAM 待发队列 + defaultTask 单写者落盘" |
kernel.org/doc/html/latest/core-api/printk-basics.html |
| FatFs 追加语义 |
FA_OPEN_APPEND(= FA_WRITE|FA_SEEKEND):打开即定位 EOF,文件不存在则创建;配合 f_lseek(fp, FILE_SIZE) 可取文件大小。本设计每条记录一次 f_open→f_write(64B)→f_close |
工程内 Lib/FatFs/ff.h:399、ff.c:3871(与官方文档一致) |
| NAND 写入代价 |
dhara FTL 已做磨损均衡;FAT32 下每次小追加约消耗 1 个簇(4KB)。本场景写频率极低(每上电 1~N 条、故障才写),簇开销与磨损可接受;以文件容量上限约束增长 |
docs/架构设计/STM32F4-Base存储架构说明.md §6.3 + GD5F 驱动参数 |
选型结论:纯文本、定长 64B 记录、一行一条(ISO 时间戳 + epoch + UTF-8 描述);追加写 + 容量上限截断;RAM 队列 + 单写者任务落盘。
不选变长行(需扫描计数/解析,掉电半行难界定);不选二进制格式(违背 R5 FTP 直读)。
4. 总体设计
4.1 模块与文件清单
| 文件 |
端 |
说明 |
App/log/sys_log.h |
共享 |
公共 API + 记录格式常量 + 行拆分内联函数(无 OS 依赖) |
App/log/sys_log_mcu.c |
MCU |
FatFS 后端:待发队列(启动前静态数组 + FreeRTOS 消息队列)、defaultTask 落盘、容量截断、上电/重启分类 |
sim/sim_syslog.c |
模拟器 |
宿主文件后端(CWD 下 sim_run.log / sim_selftest.log),直接追加,time() 时间戳 |
Drivers/BSP/UI/ui_standby.h/.c |
共享 |
空闲检测 + 待机屏(屏幕覆盖层:保存/还原前屏与焦点) |
Drivers/BSP/UI/ui_page_runlog.h/.c |
共享 |
运行记录内容页描述符(动态行,数据源 = sys_log API) |
Drivers/BSP/UI/ui_page_selftest.h/.c |
共享 |
自检记录内容页描述符(同上) |
App/app/app_main.c |
MCU |
埋点:RTC/NAND/ADC 失败 → syslog_post(SYSLOG_SELFTEST, …) |
Src/freertos.c(defaultTask USER CODE) |
MCU |
挂载失败埋点 + 上电/重启分类记录 + 队列排空落盘循环 |
App/task/net_task.c |
MCU |
埋点:链路超时 → syslog_post(SYSLOG_SELFTEST, "通讯异常") |
Drivers/BSP/UI/ui_page_password.c |
共享 |
R1:错误提示 + 重输 |
Drivers/BSP/UI/ui_content_page.h/.c、ui_menu.c |
共享 |
新增 2 个内容页 ID/描述符表项/node 39·40 映射;键事件挂钩空闲通知 |
ui_cn_chars.txt + tools/font_gen/ |
— |
补 10 字重生成字体(§6) |
MDK-ARM/*.uvprojx、simulator/CMakeLists.txt |
— |
新文件双端注册(new-source-file skill) |
simulator/main.c |
模拟器 |
--logtest / --standby / --idle <s> 无头自检与测试钩子 |
4.2 日志记录格式(定长 64B,一行一条)
示例行:2026-09-14 16:48:34 1789465714 装置上电记录(后随 NUL 填充至 64B)。
定长收益:条数 = filesize / 64(O(1),免扫描);第 k 条偏移 = k*64(随机读,UI 翻页零解析成本);掉电半行被 floor(size/64) 自然排除。
epoch 10 位约束:2026 年 Unix 秒已达 10 位(2001-09-23 起),字段按 %010u 定宽;uint32 上限 4294967295(2106 年)内不溢出。
描述 ≤28B 约束:受框架动态行栈缓冲 CP_ROW_BUF_LEN=32(含 NUL)限制,不改框架。
4.3 文件与容量
| 文件 |
内容 |
上限 |
满时策略 |
/log/run.log |
上电/重启记录 |
1024 条(64KB) |
删除重建(整卷清空,串口告警);DTU 上电频率低,容量充裕 |
/log/selftest.log |
自检故障记录 |
512 条(32KB) |
同上 |
- 目录
/log/ 首次落盘前 f_mkdir(已存在则忽略);文件名均 8.3 合规。
- FTP 视角:卷根下多一个
LOG 目录,两个 .log 文本文件可直接下载(R5)。
- 记录只增不改不删(除满时重建与未来"清除记录"功能),保证 FTP 端所见即历史。
4.4 写路径(单写者,调研结论落地)
- 读路径与写路径并发安全:UI 页在 uiTask 直接
f_open(FA_READ) 按偏移读记录(只读既有内容,写者只在 EOF 追加);FatFS 系统锁保护单次调用。
- 存储未就绪/挂载失败:待发条目滞留 RAM(下次上电丢失),串口打印告警——存储故障时本就无法持久化,属预期行为。
4.5 读路径(UI / 模拟器)
- MCU:
f_open(FA_READ) → f_lseek(idx*64) → f_read(64B) → f_close,内联拆分函数切 ts/desc。
- 模拟器:宿主文件同语义(UI 页双端零差异)。
- UI 页每帧仅读当前窗口 ≤8 条记录(每次重绘 8×64B SPI 读,用户翻页触发,频率极低)。
5. 功能设计
5.1 R1 密码错误提示(ui_page_password.c)
- ENTER 比对失败 →
s_err=1:3 框清零、光标回首框,数字框下方居中显示"密码输入错误"(y≈104)。
- 错误态下按 ↑↓←→:先清除提示再正常执行该键(即"让用户重新输入");再按 ENTER = 重新比对。
- 比对成功路径不变(回调授权/关页)。布局常量与绘制沿用现有 culling 模式,零 LVGL 对象。
5.2 R2 待机显示页(ui_standby.c/h)
进入条件:UI 初始化完成后,60s(默认,可配)内无任何按键 → 切待机屏。
- 空闲检测 =
lv_timer(1s 周期,MCU/模拟器均经 lv_timer_handler 驱动,无 OS 依赖);
- 活动通知挂钩:
menu_key_cb、cp_key_cb 每收到一个键调 ui_standby_notify_activity()(重置计时)。
待机屏内容(参考 mockup,全 ASCII 无需新字体字):
键语义(用户定案):待机屏上只有 ENTER(确认键)进入菜单;其余按键忽略(同时重置空闲计时)。
- 实现为屏幕覆盖层:进入时保存当前活动屏 + 组内焦点对象,加载待机屏并聚焦其容器;ENTER 时还原前屏与焦点。
- 内容页打开期间超时 → 同样切待机(页面对象保留在底层,ENTER 返回后原样恢复,编辑态不丢)。
5.3 R3 运行记录(上电/重启分类)
defaultTask 挂载成功、排空待发队列之后写一条:
- 阈值
SYSLOG_REBOOT_WINDOW_S = 600:断电 <10min 会被误判为重启——无掉电检测硬件时该歧义不可消除,取工程折中并在此声明。
- 串口标记(供 @test):
runlog boot entry: 装置上电记录 / 装置重启记录(sys_log_mcu.c DBG_INFO,与落盘描述串同源)。
5.4 R4 自检记录(v1 项表)
| 检查点 |
触发时机 |
描述串(落盘 + UI 显示同源) |
| RTC/墙钟建立失败 |
app_main_init |
时钟芯片异常 |
| NAND 初始化失败 |
app_main_init |
NAND异常 |
| ADC 初始化超时 |
app_main_init |
ADC异常 |
| FatFS 挂载/建卷失败 |
defaultTask(仅串口,存储不可写) |
(无法落盘,串口 [SELFTEST] fs: FAIL) |
| 网络链路 3s 未建立 |
net_task bring_up 后 |
通讯异常 |
- 只记故障;全部通过不写记录(与 mockup"有问题进行记录"一致)。
- v1 不做"已复归/动作/标志字"(mockup 中为保护继电器专有语义)——列入开放问题 O2。
- UI 页空态:
count_cb 返回 0 时以单条伪行 无记录 呈现(页脚"共 1 页",避免"共 0 页")。
6. UI 布局与字体
6.1 新增字符集(11 字)→ 重生成 ui_font_cn_12.c
| 用途 |
新字 |
| 密码错误提示"密码输入错误" |
输、入、错、误 |
| 运行记录"装置上电记录 / 装置重启记录" |
上、电、重 |
| 自检故障名"时钟芯片异常"等(异/常为公共后缀) |
芯、片、异、常 |
流程按 font-regen skill:改 ui_cn_chars.txt → extract_cn → make_font → diff。注意:extract_cn 只扫 UI 源码,故障描述串在非 UI 源码(sys_log_mcu.c/app_main.c/net_task.c)中,须手动补入字符集;其余文案(运行记录/自检记录/无记录/第 X 页共 Y 页)均已在字符集内;待机页全 ASCII。
6.2 页面几何(160×160,沿用框架常量体系)
| 页 |
参数 |
| 运行记录 / 自检记录 |
selectable=1, rows_per_page=8, top_y=24, pitch=14, band_h=13(同 AC/DC 约定;偏离 mockup:实机照片无选中带,但框架 selectable 页选中行恒黑字、band_h=0 时黑字黑底不可见,故保留标准白带),left_x=7;每条记录 = 2 行:偶数行 = 时间戳(YYYY-MM-DD HH:MM:SS,19 ASCII ≈114px 可容)、奇数行 = 描述(≤9 汉字);↑↓按行移动(8 行/页 = 4 条/页),页脚自动分页 |
| 待机屏 |
6 数据行 y=28 起 pitch=18(label x≈24 / = x≈72 / 值 x≈96);分隔线 y≈132;时钟 y≈140 居中 |
| 密码页错误提示 |
"密码输入错误" 居中,y≈104(数字框行 y=78..92 之下) |
7. 内存预算
| 项 |
大小 |
位置 |
| sys_log MCU:启动前待发数组 8×12B + 队列 8×12B + 状态 |
≈300B(静态)+ ≈150B(FreeRTOS 堆,消息队列) |
.bss / heap_4 |
| ui_standby:待机屏 + 容器 LVGL 对象(常驻) |
≈300B |
LVGL 池(16KB,当前占用 ≈5KB) |
| 记录页模块 |
0(描述符驱动,零 LVGL 对象;仅栈缓冲) |
— |
| 字体 +10 字形 |
≈250B Flash |
.rodata |
结论:不新增任务、不动任务栈、FreeRTOS 堆增量 <200B,LVGL 池余量充足。
8. 测试计划
8.1 模拟器(无头自动化)
| 用例 |
手段 |
日志引擎闭环 --logtest |
post N 条 → count/get 回读断言 + 宿主文件字节级校验(64B 定长、ISO 时间戳、NUL 填充),0=PASS(仿 --cfgtest) |
待机屏渲染 --standby |
无头注键后直接调测试钩子强制进入待机 → 截图比对(布局/时钟行);窗口模式 --idle <s> 可人工验证真实超时路径 |
| 记录页像素回归 |
--keys 导航至 node 39/40 → 截图 + 文本断言(时间戳行/描述行/页脚分页),纳入 ui-verify 门禁 |
| 密码错误提示 |
--keys 输错密码序列 → 截图断言"密码输入错误"可见;再按方向键 → 提示消失 |
8.2 真机(@test 串口标记)
Required:App init OK、Network Init、net init: OK、runlog boot entry:(装置上电记录 / 装置重启记录)、UI standby: ready (idle=60s);
Forbidden:FAIL/ERROR/HardFault。
人工目检:待机屏(静置 60s)、记录页翻页、FTP 下载 /log/*.log 文本可读性。
9. 风险与开放问题
| # |
项 |
策略 |
| O1 |
断电 <10min 的"上电/重启"歧义 |
§5.3 阈值折中,文档声明;如需精确可后续加掉电标志(PVD) |
| O2 |
故障"已复归"/持续监测(mockup 语义) |
v2:周期自检 + 活动故障状态机 + 恢复记录 |
| O3 |
"清除记录"菜单项(node 27 #P,现为空框) |
v2:UI 动作 = 删除/重建 log 文件(与满时策略同路径) |
| O4 |
待机页电流值接实时数据 |
待交流量标定管线(AC 页占位数据替换)落地后接入 tpafe5160_snapshot()(原子快照;旧 tpafe5160_get_buf() 已于 2026-10-10 删除,见 AGENTS.md) |
| R-1 |
FAT32 小追加簇开销 |
§3 调研结论:写频率极低可接受;容量上限约束增长 |
| R-2 |
UI 读与落盘并发 |
只读既有内容 + EOF 追加 + FatFS 系统锁,无互斥需求(§4.4) |
10. 实施步骤(按序执行,每步双端构建 0/0)
✅ S1–S9 已于 2026-09-14 全部完成:Keil @build 0 错 0 警 + 模拟器 0 警告;S8 = --logtest PASS(期间修复 ts 19 字符 / epoch 10 位两处格式 bug)+ ui-verify 五门禁全绿(池基线 5440/5520B、pwc 参考更新)+ 记录页/待机屏/密码错误提示截图目检通过;S9 = 文档同步。
| # |
步骤 |
产物 |
| S1 |
字体补字 + 重生成 |
ui_cn_chars.txt、ui_font_cn_12.c |
| S2 |
日志引擎:sys_log.h(共享 API/格式)+ sys_log_mcu.c + sim/sim_syslog.c |
双端可 post/count/get |
| S3 |
自检埋点 + defaultTask 落盘循环 + 上电/重启分类 |
app_main/freertos/net_task 接线 |
| S4 |
密码错误提示 |
ui_page_password.c |
| S5 |
待机屏 ui_standby(空闲检测 + 覆盖层)+ 键挂钩 |
ui_menu/ui_content_page 各一行通知 |
| S6 |
记录页 ×2 + 内容页框架接线(枚举/描述符表/node 映射) |
ui_page_runlog/selftest、ui_content_page.h/.c、ui_menu.c |
| S7 |
双端注册新文件(uvprojx + CMakeLists)+ 构建 0/0 |
new-source-file skill |
| S8 |
模拟器无头回归(--logtest/--standby/截图断言)+ 真机 @test |
测试通过记录 |
| S9 |
文档同步(项目架构总览、AGENTS.md、本文档状态) |
docs |