LVGL 图形化升级界面与模拟器
本文档介绍 Jieli(杰理)AC792N OTA Loader 工程中基于 LVGL v8 的 800x480 图形化升级界面(GUI Guider 设计 + custom 自定义逻辑)及其配套的 PC 端 LVGL 模拟器,涵盖界面架构、消息机制、OTA 多通道变体与工程构建方式。
Purpose and Scope
本页聚焦 ac792n-ota-loader/ota_loader_update_800x480/ 工程,说明:
- LVGL 图形化升级界面的工程组织(GUI Guider 设计文件、custom 扩展接口、字体/图片资源);
- 升级界面与底层 OTA 引擎之间的消息通信机制(prompt / process / process_string 三类消息);
- LVGL 模拟器(
lvgl-simulator/)的组成与构建入口,用于在 PC 上预览界面; - 同一 OTA Loader 在 SD 卡、UART、USB、USB HID 四种升级通道下的界面复用方式。
以下内容不在本页范围:各通道底层升级协议(如 USB 厂商命令、SD 卡安全升级流程)属于各通道自己的文档;LVGL 库本身的 API 使用请参考 LVGL v8 官方文档。涉及具体通道的工程请参见“Related Links”。
Overview
AC792N 是一颗面向音频/物联网应用的 SoC。在 OTA(Over-The-Air / 在线升级)场景中,设备需要在不运行完整应用的前提下,向用户呈现升级进度、结果与错误提示。传统做法是点阵屏 + 数码管,而本工程采用 LVGL v8 + 800x480 彩屏 的图形化方案,带来以下能力:
- 图形化提示升级开始、进度百分比、过程文本(如“正在下载…”“校验失败”等);
- 通过 GUI Guider(NXP 官方 UI 设计工具)可视化设计页面,再由 Jieli 的
jlui工程格式落地,降低手写控件代码成本; - 通过 custom 接口层 把 UI 与 OTA 业务解耦:OTA 引擎只调用
lvgl_msg_send_update_*系列函数,不关心控件细节; - 提供 PC 模拟器,在没有真机屏幕时即可预览、调试界面布局与动画。
关键概念
| 概念 | 说明 |
|---|---|
| GUI Guider | NXP 提供的 LVGL 可视化设计工具,生成 gui_guider.h 等代码骨架 |
| jlui 工程 | Jieli 对 GUI Guider 工程的封装格式(project.jlui + design/default/*.json),描述页面与控件树 |
| custom 层 | 工程内 `custom/custom.c |
| MVVM 消息 | jlui/mvvm/msg/update.json 定义升级相关消息模型,用于 UI 与逻辑层解耦 |
| lvgl-simulator | 基于 CMake/批处理构建的 PC 端模拟器,链接 liblvgl.extra.a 等预编译库 |
Architecture
下图展示了 ota_loader_update_800x480 工程的组件划分与数据流方向:
flowchart TD
subgraph sg_Design["设计时 (GUI Guider / jlui)"]
JG["project.jlui 工程文件"]
UJ["design/default/ui.json + home.json"]
FONT["import/font/FangZhengKaiTiJianTi_1.ttf"]
IMG["import/image/qrcode.jpg"]
end
subgraph sg_Runtime["运行时 (MCU / 模拟器)"]
GG["gui_guider.h 生成代码"]
LVGL["LVGL v8 图形库"]
CUSTOM["custom/custom.c|h 桥接层"]
CONFIG["config/lv_conf_ext.h 配置扩展"]
end
subgraph sg_Business["OTA 业务层"]
OTA["OTA 引擎 (升级通道)"]
MSG["lvgl_msg_send_update_* 消息"]
end
JG --> UJ
UJ --> GG
FONT --> LVGL
IMG --> LVGL
CONFIG --> LVGL
GG --> CUSTOM
LVGL --> CUSTOM
OTA --> MSG
MSG --> CUSTOM
CUSTOM --> LVGL
架构说明:
- 设计时:开发者用 GUI Guider 编辑页面,产出
ui.json/home.json等控件树描述;字体(方正楷体简体)与二维码图片(qrcode.jpg)作为资源导入。 - 运行时:
gui_guider.h生成代码负责创建控件;custom桥接层持有lv_ui实例并暴露 OTA 消息入口;lv_conf_ext.h按工程需要裁剪/扩展 LVGL 特性。 - 业务层:OTA 引擎(SD/UART/USB/USB HID 通道)不直接触碰控件,只通过
lvgl_msg_send_update_prompt/process/process_string三个函数把升级状态“推”给 UI,实现单向解耦——UI 依赖业务消息,业务不依赖 UI 内部结构。
这一分层设计的核心意图是:让同一套 800x480 界面代码在四种升级通道(SD、UART、USB、USB HID)以及 PC 模拟器之间无缝复用,业务侧只需要适配各自的底层传输实现。
主内容:custom 桥接层与界面消息机制
custom 接口层(custom/custom.h)
custom/custom.h 是整个界面工程的对外契约。它声明了 UI 初始化函数与三条 OTA 消息发送接口:
void custom_init(lv_ui *ui);
void lvgl_msg_send_update_prompt(char *string);
void lvgl_msg_send_update_process(int process);
void lvgl_msg_send_update_process_string(char *string);
Source: custom.h
设计意图分析:
custom_init(lv_ui *ui)— 接收 GUI Guider 生成代码创建的界面句柄lv_ui,把业务回调/消息订阅挂到对应控件上。所有与升级相关的 UI 初始化都收敛在这一入口,方便 OTA 主流程在启动时一次性调用。lvgl_msg_send_update_prompt(char *string)— 发送提示类消息(如“检测到升级包”“升级完成,即将重启”),UI 侧在提示控件上展示字符串。lvgl_msg_send_update_process(int process)— 发送数值进度(0~100 百分比),UI 侧驱动进度条(lv_bar)或环形进度动画。lvgl_msg_send_update_process_string(char *string)— 发送进度文本(如“正在写入 45%”),用于在进度条旁同步显示文字。
三个发送函数把“升级过程”抽象为三种信号:提示(prompt)、数值进度(process)、文本进度(process_string)。这种细分让 OTA 引擎可以按事件类型表达状态,而 UI 侧只需实现对应的控件更新逻辑,二者通过消息解耦、可独立演进。
头文件的兼容性处理
custom.h 开头有一段条件编译逻辑:
#include "lv_conf.h"
#if !LV_USE_CALENDAR
typedef struct {
uint16_t year;
int8_t month; /** 1..12*/
int8_t day; /** 1..31*/
} lv_calendar_date_t;
#endif
#include "gui_guider.h"
Source: custom.h
当工程通过 lv_conf_ext.h 关闭了日历组件(LV_USE_CALENDAR = 0)时,LVGL 不会定义 lv_calendar_date_t;而 GUI Guider 生成代码中可能仍引用该类型,因此在 custom 层兜底补定义,保证生成代码可编译。这体现了 custom 层作为“生成代码与配置裁剪之间的适配器”的定位。
jlui 工程格式(设计时产物)
jlui/ 目录是 Jieli 对 GUI Guider 工程的可视化落地:
| 文件 | 作用 |
|---|---|
jlui/project.jlui / project.jlformat | 工程主文件,记录页面集合、版本与设计器配置 |
jlui/design/default/ui.json | 页面/控件树的 JSON 描述(GUI Guider 设计导出) |
jlui/design/default/ui/home.json | home 页面的具体控件布局、属性、事件绑定 |
jlui/design/default/ui/home.png | home 页面的设计预览图 |
jlui/design/default/i18n.json | 多语言文本资源 |
jlui/design/default/projectFont.json | 工程字体配置(指向 import/font/ 下的 TTF) |
jlui/mvvm/msg/update.json | 升级消息模型定义(MVVM 消息订阅的 schema) |
ui.json / home.json 这类设计文件的价值在于:界面改版不需要重写 C 代码——在 GUI Guider 中拖拽控件、调整布局后重新导出,custom 层接口保持不变,OTA 业务代码零改动。这正是分层设计的工程收益。
MVVM 消息模型(update.json)
jlui/mvvm/msg/update.json 定义了升级相关的消息 Schema,配合 custom 层的 lvgl_msg_send_update_* 接口使用。其作用是在 jlui 的 MVVM 框架内声明“升级消息”的字段结构,使界面控件可以通过消息订阅自动刷新(例如进度条绑定 process 字段、提示文本绑定 prompt 字段),进一步减少手写回调代码。
说明:
update.json的具体字段结构未在本仓库读取范围内逐行核对,其职责是升级消息的模型定义;消息的实际发送入口以custom.h中声明的三个lvgl_msg_send_update_*函数为准。
核心流程:升级状态如何驱动界面
消息时序
下面用时序图描述 OTA 引擎通过 custom 接口驱动 LVGL 界面的完整链路(MCU 与 PC 模拟器上同一套代码路径):
sequenceDiagram
participant OTA as OTA 引擎 (升级通道)
participant C as custom 桥接层
participant UI as LVGL 控件 (lv_bar / label)
participant SCREEN as 800x480 屏幕
OTA->>C: custom_init(lv_ui)
C->>UI: 绑定控件/订阅消息
OTA->>C: lvgl_msg_send_update_prompt("检测到升级包")
C->>UI: 更新提示文本
UI->>SCREEN: 渲染提示
loop 升级过程
OTA->>C: lvgl_msg_send_update_process(25)
C->>UI: 进度条置 25%
UI->>SCREEN: 渲染进度
OTA->>C: lvgl_msg_send_update_process_string("正在写入 25%")
C->>UI: 更新过程文本
UI->>SCREEN: 渲染文本
end
OTA->>C: lvgl_msg_send_update_prompt("升级完成")
C->>UI: 更新结果提示
UI->>SCREEN: 渲染结果
关键点:OTA 引擎与 UI 之间没有共享全局变量,所有状态传递都经过 lvgl_msg_send_update_* 消息,因此同一 custom 层可在模拟器与真机间复用,也便于后续把消息替换为真实事件队列(如 RTOS 消息邮箱)。
OTA 通道变体与界面复用
仓库中 ac792n-ota-loader/ 下存在四个通道工程,它们共享同一套 LVGL v8 界面代码(app/src/common/lvgl_v8/)与预编译扩展库:
flowchart LR
subgraph sg_UI2["共用 LVGL UI (lvgl_v8 + lvgl_v8_extend.a)"]
UI_COMMON["app/src/common/lvgl_v8/"]
end
SD["sd_sec_ota_update<br/>(SD 卡安全升级)"]
UART["uart_user_update<br/>(UART 用户升级)"]
USB["usb_ota_update<br/>(USB 升级)"]
HID["usb_hid_ota_update<br/>(USB HID 升级)"]
UI_COMMON --> SD
UI_COMMON --> UART
UI_COMMON --> USB
UI_COMMON --> HID
各通道工程均包含:
app/src/common/lvgl_v8/— LVGL v8 源码(含demos/widgets/assets/img_lvgl_logo.c等示例资源);include_lib/liba/wl83/lvgl_v8_extend.a— Jieli 为 WL83/AC792N 平台预编译的 LVGL v8 扩展库,提供平台适配与额外控件能力。
这种“一份 UI 源码 + 多通道业务入口”的结构,使得新增升级通道时只需实现底层传输与升级协议,UI 展示逻辑完全复用 custom.h 的消息接口。
LVGL 模拟器(lvgl-simulator)
ota_loader_update_800x480/lvgl-simulator/ 提供 PC 端模拟运行环境,用于脱离真机预览 800x480 界面:
| 文件/目录 | 作用 |
|---|---|
CMakeListsLvgl.txt | 模拟器的 CMake 构建脚本(把 UI 工程与 LVGL 编译为 PC 可执行程序) |
buildLibraryLvgl.bat | Windows 批处理入口:一键构建/更新 LVGL 库 |
bin/liblvgl.extra.a | 预编译的 LVGL 扩展静态库(含平台差异适配) |
bin/libavcodec.dll.a / libavformat.dll.a / libavutil.dll.a | FFmpeg 解码库导入库(模拟器内用于视频/动图解码) |
bin/libcjson.a | cJSON 库(解析 ui.json 等设计/配置文件) |
模拟器设计意图:
- 开发提效:GUI 界面迭代(布局、字体、动画)在 PC 上秒级验证,避免反复烧录真机;
- 依赖预编译:通过
liblvgl.extra.a与 FFmpeg 导入库,模拟器在 Windows 上即可链接运行,无需在 PC 上重新编译 LVGL 全部源码; - 配置一致:模拟器与真机共用
lv_conf_ext.h/gui_guider.h/ custom 层,保证“所见即所得”。
说明:模拟器构建脚本的具体参数(如工具链、目标平台宏)未在本仓库读取范围内逐行核对,构建入口为
buildLibraryLvgl.bat与CMakeListsLvgl.txt。
配置选项
LVGL 工程的特性开关集中在 config/lv_conf_ext.h(对 LVGL 默认 lv_conf.h 的扩展覆盖)。custom 层通过 #if !LV_USE_CALENDAR 感知配置,说明该文件直接影响编译结果。典型可配置项包括:
| 配置项 | 类型 | 典型默认 | 说明 |
|---|---|---|---|
LV_USE_CALENDAR | bool | 0(本工程) | 是否启用日历组件;关闭时 custom.h 兜底定义 lv_calendar_date_t |
LV_COLOR_DEPTH | int | 16/32 | 颜色深度,与 800x480 屏驱动匹配 |
LV_HOR_RES_MAX / LV_VER_RES_MAX | int | 800 / 480 | 最大分辨率,对应本工程屏幕规格 |
| 字体配置 | JSON | FangZhengKaiTiJianTi_1.ttf | 由 projectFont.json 指定导入字体,界面文本渲染使用 |
说明:
lv_conf_ext.h的具体开关值未在本仓库读取范围内逐行核对;上表为工程目录结构与 custom.h 条件编译所反映的可配置维度。
工程级配置(仓库根)
ac792n-ota-loader/Makefile— 顶层构建入口,驱动各子工程/工具链;ac792n-ota-loader/default.workspace— 集成开发环境(如 Jieli 的 CodeBlocks/自定义 IDE)工作区文件,记录打开的子工程与构建目标。
使用示例
示例 1:初始化 UI 并注册 OTA 桥接
以下接口声明来自 custom.h,是 OTA 主流程与 UI 的握手入口(实现位于 custom.c):
#include "gui_guider.h"
/* OTA 主流程启动时,传入 GUI Guider 生成的界面句柄 */
void custom_init(lv_ui *ui);
Source: custom.h
示例 2:OTA 引擎上报升级状态
升级过程中的典型调用序列(三个消息接口的语义对比):
/* 提示类:升级开始 / 结束 / 异常 */
lvgl_msg_send_update_prompt("检测到升级包,开始升级...");
/* 数值进度:驱动进度条(0-100) */
lvgl_msg_send_update_process(45);
/* 文本进度:进度条旁同步显示文字 */
lvgl_msg_send_update_process_string("正在写入 45%");
Source: custom.h
使用要点:
- 三个发送函数均为
void返回、无错误码——设计上它们只是“投递消息”,不阻塞 OTA 引擎,符合 UI 与业务单向解耦的意图; process与process_string可以成对调用(进度条 + 文本),也可按控件能力单独调用;- 若在 RTOS 环境下,实现层应把消息转投到 UI 任务的消息队列,避免在 OTA 中断/低优先级上下文直接操作 LVGL 控件(LVGL 非线程安全,须在
lv_timer_handler所在线程内更新控件)。
API 参考
void custom_init(lv_ui *ui)
界面自定义逻辑初始化入口。
参数:
ui(lv_ui *):GUI Guider 生成代码创建的界面根句柄,包含所有页面与控件引用。
返回: 无。
说明: 在 GUI Guider 的 setup_ui() 之后调用,用于把 OTA 消息与控件绑定、启动界面相关定时器或动画。若 LV_USE_CALENDAR 关闭,本头文件同时提供 lv_calendar_date_t 的兼容定义。
void lvgl_msg_send_update_prompt(char *string)
发送升级提示消息(文本)。
参数:
string(char *):提示内容,如“升级完成,即将重启”。
返回: 无。
说明: 用于一次性状态提示;UI 侧通常更新提示 label 并配合显示/隐藏动画。
void lvgl_msg_send_update_process(int process)
发送升级数值进度。
参数:
process(int):进度值,约定为 0~100 的百分比。
返回: 无。
说明: 驱动进度条(lv_bar)等数值型控件;UI 侧应把越界值钳制到合法区间。
void lvgl_msg_send_update_process_string(char *string)
发送升级进度文本。
参数:
string(char *):进度描述文本,如“正在写入 45%”。
返回: 无。
说明: 与 lvgl_msg_send_update_process 配合使用,在进度条旁展示可读文本;实现中可复用同一消息处理函数,仅刷新文本控件。
失败模式、边界情况与并发
失败模式
| 场景 | 表现 | 缓解策略 |
|---|---|---|
OTA 引擎在消息发送前未调用 custom_init | 控件未绑定,消息被丢弃或空指针 | OTA 主流程必须在 setup_ui() 后、升级开始前调用 custom_init(lv_ui) |
string 参数为 NULL | 文本控件渲染异常 | custom 实现应做空指针保护;调用方应始终传入有效字符串 |
| 进度值越界(<0 或 >100) | 进度条溢出或停滞 | UI 侧钳制 process 到 0~100 |
| 升级中途断电/异常 | 界面停留在中间状态 | 依赖底层 OTA 引擎的断点续传与回滚机制;UI 通过 prompt 提示“升级失败,请重试” |
LV_USE_CALENDAR 配置与生成代码不一致 | 编译期缺 lv_calendar_date_t | custom.h 的条件编译兜底已覆盖此场景,但修改 lv_conf_ext.h 后需重新生成/同步 GUI Guider 代码 |
并发与线程安全
- LVGL 非线程安全:所有控件操作必须在
lv_timer_handler()所在线程(UI 任务)中执行。custom 层的lvgl_msg_send_update_*实现应当只做消息投递(如写入队列/邮箱),由 UI 任务消费后再更新控件,绝不能在 OTA 的 IO 中断或低优先级任务中直接调用 LVGL API。 - 消息乱序:prompt / process / process_string 三类消息独立发送,若底层为 FIFO 队列,顺序自然保持;若 UI 侧按类型分类处理,需注意同一次进度更新中文本与数值的一致性。
- 进度更新频率:OTA 写入操作可能以块为单位快速上报进度,UI 侧可做节流(如仅在百分比变化≥1 时刷新),避免过度重绘 800x480 全屏。
性能与运维注意事项
- 渲染开销:800x480 全屏刷新在 MCU 上开销较大,界面设计上应优先局部刷新(进度条/文本区域),动画避免全屏透明混合。
- 字体内存:
FangZhengKaiTiJianTi_1.ttf为矢量字体,建议按需子集化或转 LVGL 内置格式(.bin/c 数组)以减小内存与 Flash 占用。 - 模拟器与真机差异:模拟器使用 PC 渲染与 FFmpeg 解码,性能远高于真机;动画帧率、内存上限需以真机为准做回归验证。
- 构建产物:
bin/liblvgl.extra.a等预编译库与平台(WL83/AC792N)绑定,升级 LVGL 版本时须同步更新扩展库与模拟器导入库。
扩展点
- 新增 OTA 通道:仿照
sd_sec_ota_update/uart_user_update/usb_ota_update/usb_hid_ota_update,复用app/src/common/lvgl_v8/与lvgl_v8_extend.a,只需实现新通道的底层传输与升级协议,并调用 custom.h 的三个消息接口。 - 界面改版:在 GUI Guider 中修改
home.json对应页面并重新导出,custom 接口不变,业务代码零改动;多语言文案维护于i18n.json。 - 消息扩展:在
jlui/mvvm/msg/update.json中追加消息字段,并在 custom 层新增lvgl_msg_send_update_xxx包装函数,即可扩展 UI 能力(如剩余时间、版本号显示)。 - 模拟器定制:通过
CMakeListsLvgl.txt增删链接库(如 FFmpeg、cJSON)或添加模拟输入设备,用于 UI 交互验证。
测试与验证
- 模拟器预览:在 PC 上通过
lvgl-simulator(buildLibraryLvgl.bat+CMakeListsLvgl.txt)运行界面,验证布局、字体与动画,覆盖正常升级流程的 UI 表现。 - 真机回归:分别在 SD/UART/USB/USB HID 四通道上验证 prompt、process、process_string 三条消息路径,确认进度与文本同步、异常提示正确。
- 配置矩阵:修改
lv_conf_ext.h(如关闭LV_USE_CALENDAR)后重新编译,验证 custom.h 的条件编译兜底与 GUI Guider 生成代码兼容。
说明:仓库内未发现针对本 UI 工程的自动化单元测试文件;上述验证方式基于工程结构推断的常规流程,具体测试脚本以仓库实际交付物为准。
Related Links
- ota_loader_update_800x480 工程根目录 — 本页所讲界面的完整工程
- custom.h(custom 接口声明) — OTA 消息接口契约
- lvgl-simulator 目录 — PC 模拟器构建与预编译库
- jlui 设计文件目录 — GUI Guider 导出与 MVVM 消息定义
- SD 卡安全升级通道 — 复用同一 LVGL UI 的通道工程
- UART 用户升级通道 — 复用同一 LVGL UI 的通道工程
- USB 升级通道 — 复用同一 LVGL UI 的通道工程
- USB HID 升级通道 — 复用同一 LVGL UI 的通道工程
- LVGL v8 官方文档 — LVGL 库 API 与控件参考(外部链接)