杰理 SDK 文档中心
首页
首页
  • fw-Bootloader:JL 系列定制 Bootloader

    • Bootloader 架构与芯片适配
    • uboot 升级协议与流程
    • 上位机升级工具
    • 编译环境与快速开始
  • ac792n-ota-loader:AC792N 系列 OTA Loader

    • 工程结构与公共运行时框架
    • SD 卡与 USB 基础升级通道
    • 安全升级通道(SD/USB)
    • 用户自定义升级通道(UART/USB HID)
    • LVGL 图形化升级界面与模拟器
  • ac791n-ota-loader:AC791N 系列 OTA Loader

    • uboot 应用框架与 WiFi 示例
    • 升级通道变体(AP/STA/USB HID)
    • 网络与系统库依赖

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 GuiderNXP 提供的 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

设计意图分析:

  1. custom_init(lv_ui *ui) — 接收 GUI Guider 生成代码创建的界面句柄 lv_ui,把业务回调/消息订阅挂到对应控件上。所有与升级相关的 UI 初始化都收敛在这一入口,方便 OTA 主流程在启动时一次性调用。
  2. lvgl_msg_send_update_prompt(char *string) — 发送提示类消息(如“检测到升级包”“升级完成,即将重启”),UI 侧在提示控件上展示字符串。
  3. lvgl_msg_send_update_process(int process) — 发送数值进度(0~100 百分比),UI 侧驱动进度条(lv_bar)或环形进度动画。
  4. 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.jsonhome 页面的具体控件布局、属性、事件绑定
jlui/design/default/ui/home.pnghome 页面的设计预览图
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.batWindows 批处理入口:一键构建/更新 LVGL 库
bin/liblvgl.extra.a预编译的 LVGL 扩展静态库(含平台差异适配)
bin/libavcodec.dll.a / libavformat.dll.a / libavutil.dll.aFFmpeg 解码库导入库(模拟器内用于视频/动图解码)
bin/libcjson.acJSON 库(解析 ui.json 等设计/配置文件)

模拟器设计意图:

  1. 开发提效:GUI 界面迭代(布局、字体、动画)在 PC 上秒级验证,避免反复烧录真机;
  2. 依赖预编译:通过 liblvgl.extra.a 与 FFmpeg 导入库,模拟器在 Windows 上即可链接运行,无需在 PC 上重新编译 LVGL 全部源码;
  3. 配置一致:模拟器与真机共用 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_CALENDARbool0(本工程)是否启用日历组件;关闭时 custom.h 兜底定义 lv_calendar_date_t
LV_COLOR_DEPTHint16/32颜色深度,与 800x480 屏驱动匹配
LV_HOR_RES_MAX / LV_VER_RES_MAXint800 / 480最大分辨率,对应本工程屏幕规格
字体配置JSONFangZhengKaiTiJianTi_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_tcustom.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 版本时须同步更新扩展库与模拟器导入库。

扩展点

  1. 新增 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 的三个消息接口。
  2. 界面改版:在 GUI Guider 中修改 home.json 对应页面并重新导出,custom 接口不变,业务代码零改动;多语言文案维护于 i18n.json。
  3. 消息扩展:在 jlui/mvvm/msg/update.json 中追加消息字段,并在 custom 层新增 lvgl_msg_send_update_xxx 包装函数,即可扩展 UI 能力(如剩余时间、版本号显示)。
  4. 模拟器定制:通过 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 与控件参考(外部链接)
Prev
用户自定义升级通道(UART/USB HID)