Skip to content

LVGL UI应用开发说明

目录


概述

LVGL(Light and Versatile Graphics Library)是一款开源的嵌入式图形库,专为资源受限的 MCU 设计,占用内存小、运行效率高、控件丰富,广泛用于模组 LCD 屏的界面开发。目前使用的LVGL9.4.0版本。

版本信息

项目 说明
LVGL 版本 9.4.0
组件路径 components/lvgl-9.4.0
颜色深度 16 bit(RGB565,LV_COLOR_DEPTH 16
操作系统集成 LV_USE_OS = LV_OS_CUSTOM(对接 YOpen OS 抽象层)
运行内存 LV_MEM_SIZE 64 KB
配置文件 components/lvgl-9.4.0/lv_conf.h

版本号定义在 components/lvgl-9.4.0/lv_version.h

#define LVGL_VERSION_MAJOR 9
#define LVGL_VERSION_MINOR 4
#define LVGL_VERSION_PATCH 0

支持功能

功能 说明
控件 label / image / button / list 等 LVGL 内置控件
字体 Montserrat、FontAwesome5、思源黑体(Source Han Sans SC,含中文)
图片解码 JPEG(yopen_jpeg)、BMP、GIF、PNG(可选)
显示驱动 ST7789(240×320)、JD9853(240×320)
文件系统 LittleFS(LV_USE_FS

快速入门

demo_lvgl 为例,展示一个最小 LVGL 应用从放置图片到显示的最短路径。

使用步骤

  1. 准备图片:将图片放入 components/demo/lvgl/fs/root/(如 ycom.jpg),预制文件系统会将其打包进 LittleFS 镜像。
  2. 编译项目:执行构建命令。
  3. 运行显示:上电后自动初始化 LVGL 并在屏幕上显示文字与图片。

编译命令

.\build.bat YM310_W09C.A60 lvgl

<bsp_module> 为模组型号,如 YM310_X09S.U62YM310_W09C.A60YM310_W09S.H60 等;lvgl 对应 project/lvgl.cmake 工程。


实现原理

整体架构

LVGL 在 YOpen SDK 中的调用层次自上而下为:

业务/示例 → LVGL 核心(lvgl-9.4.0)→ 显示驱动抽象(components/lcd)→ SPI LCD 硬件。

  • 业务/示例层demo_lvgl.cai_lvgl.c 等,负责创建控件、处理消息。
  • LVGL 核心层components/lvgl-9.4.0,负责控件渲染、事件、定时器、字体/图片解码。
  • 显示驱动抽象层components/lcdlcdDrv.hlcdOpen / lcdSetWindow / lcdFill / lcdBackLight)。
  • 硬件层:ST7789、JD9853 等 SPI 屏幕。

资源生成与加载流程

编译阶段:
  图片文件(.jpg/.png/.gif) ──→ LVGLImage.py ──→ C 数组(.c)
  字体文件(.ttf/.otf/.woff) ──→ lv_font_conv ──→ C 文件(.c)
  任意文件(图片/音频等)     ──→ 预制文件系统   ──→ LittleFS 镜像

运行阶段:
  lv_init()                 → 初始化 LVGL
  lv_port_disp_init()       → 创建显示设备并注册 flush 回调
  lv_image_set_src()        → 设置图片源(C 数组或文件路径)
  lv_obj_set_style_text_font() → 设置字体
  lv_timer_handler()        → 主循环周期刷新

技术要点

  • 编译时转换:图片和字体在编译阶段被转换为 C 数据,随固件烧录,运行时无需解析源文件。
  • 两种图片加载:可加载为 C 数组(LV_IMAGE_DECLARE),也可通过文件系统路径加载(如 /ycom.jpg)。
  • 按需解码:JPEG / GIF 等格式由 LVGL 在显示时自动调用对应解码器解码。
  • 消息驱动 UI:采用「消息队列 + 主循环」模型,业务线程投递消息,LVGL 任务统一处理刷新。

资源生成方式

图片资源

图片通过 LVGL 官方工具 LVGLImage.py 转换为 C 数组。在工程 CMakeLists.txt 中配置:

# 查找 resource/image 目录下的图片文件
file(GLOB IMAGE_FILES "${CURRENT_CMAKE_DIR}/resource/image/*.jpg")

foreach(IMAGE_FILE ${IMAGE_FILES})
    get_filename_component(IMAGE_NAME ${IMAGE_FILE} NAME_WE)
    set(OUTPUT_C_FILE "${PROJECT_BINARY_DIR}/${IMAGE_NAME}.c")
    set(LVGL_IMAGE_TOOL ${YOPEN_TOP_DIR}/components/lvgl-9.4.0/scripts/LVGLImage.py)

    add_custom_command(
        OUTPUT ${OUTPUT_C_FILE}
        COMMAND python ${LVGL_IMAGE_TOOL} --cf RAW --ofmt C -o ${PROJECT_BINARY_DIR} --name ${IMAGE_NAME} ${IMAGE_FILE}
        MAIN_DEPENDENCY ${IMAGE_FILE}
        COMMENT "Converting image: ${IMAGE_FILE}"
    )

    target_sources(${target} PRIVATE ${OUTPUT_C_FILE})
    target_include_directories(${target} PRIVATE ${PROJECT_BINARY_DIR})
endforeach()

参数说明

参数 说明
--cf RAW 颜色格式,保留原始图片数据(JPEG 用 RAW,带透明用 RAW_ALPHA)
--ofmt C 输出格式为 C 文件
--name {IMAGE_NAME} 生成的变量名(与文件名对应)

详细 JPEG 转换与显示流程见 LVGL JPEG 解码说明。GIF 图片同样使用 LVGLImage.py 转换,配合 src/libs/gif 解码器使用(参考 app/xiaozhiapp/cyberCMakeLists.txt)。

字体资源

字体通过 LVGL 工具 lv_font_conv 转换为 C 文件。SDK 提供了封装脚本 built_in_font_gen.py(位于 components/lvgl-9.4.0/scripts/built_in_font/)。

工具安装lv_font_conv 依赖 Node.js):

  1. 安装 Node.js
  2. 下载 lv_font_conv 代码。
  3. 全局安装:
npm install -g lv_font_conv
  1. 将 Node.js 全局 bin 目录添加到系统环境变量 PATH

生成命令(以思源黑体中文 16 号为例):

python ./built_in_font_gen.py --size 16 -o lv_font_source_han_sans_sc_16_cjk.c \
    --bpp 1 --font SourceHanSansSC-Normal.otf \
    -r 0x20-0xbf,0x3000-0x303F,0xFF00-0xFFEF --symbols 啊阿埃挨哎唉哀.....

参数说明

参数 说明
--size 字体像素尺寸
--bpp 每个像素的位深度(1/2/4,中文字体通常用 1)
-r 包含的字符 Unicode 范围(如 0x20-0xbf ASCII、0x3000-0x303F CJK 标点、0xFF00-0xFFEF 全角)
--symbols 额外指定的字符(逐个列出需要的中文字符)
--font 源字体文件(TTF / OTF / WOFF,如 SourceHanSansSC-Normal.otf
-o 输出的 C 文件名

SDK 已内置常用字体(components/lvgl-9.4.0/src/font/),可直接使用:

字体变量 说明
lv_font_montserrat_24 英文/数字(lv_conf.h 中已启用 24 号)
lv_font_awesome5_16 图标字体(电池/信号/表情等符号)
lv_font_source_han_sans_sc_14_cjk 思源黑体中文 14 号
lv_font_source_han_sans_sc_16_cjk 思源黑体中文 16 号
lv_font_source_han_sans_sc_24_cjk 思源黑体中文 24 号

文件系统资源

任意文件(图片、音频等)可放入预制文件系统目录,编译时打包成 LittleFS 镜像,运行时通过文件路径访问:

# 预制文件系统根目录,该路径下所有文件打包成 LittleFS 镜像
set(YOPEN_PROJECT_FS_DIR ${YOPEN_TOP_DIR}/components/demo/lvgl/fs/root)

例如 demo_lvglfs/root/ 下放置了 ycom.jpgtest.bin,运行时通过 /ycom.jpg/test.bin 访问。


加载方式

初始化流程

LVGL 应用入口通常在一个独立任务中完成初始化并进入主循环:

void demo_lcd_task(void *argv)
{
    // 1. 初始化 LVGL
    lv_init();

    // 2. 初始化显示设备
    lv_port_disp_init();

    // 3. 创建界面
    show_text_demo();
    show_image_demo();

    // 4. 主循环:周期调用 lv_timer_handler 驱动刷新
    while (1) {
        uint32_t time_till_next = lv_timer_handler();
        if (time_till_next == LV_NO_TIMER_READY) {
            time_till_next = 20;
        }
        yopen_rtos_task_sleep_ms(time_till_next);
    }
}

application_init(demo_lcd_task, "COMP.LCD", 4, 4, NULL);

显示驱动

LVGL 不直接操作硬件,而是通过「flush 回调」把渲染结果交给 LCD 驱动。核心流程:

  1. lv_display_create() 创建显示设备;
  2. lv_display_set_flush_cb() 注册刷新回调;
  3. lv_display_set_buffers() 设置渲染缓冲区;
  4. flush 回调中调用 lcdSetWindow / lcdFill 把像素数据刷到屏幕,最后调用 lv_display_flush_ready()
static void disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
    if (disp_flush_enabled) {
        disp_update_window(area->x1, area->y1, area->x2, area->y2, (uint16_t *)px_map);
    }
    lv_display_flush_ready(disp);
}

屏幕参数集中在 components/demo/lvgl/demo_lcd_conf.h,换屏只需修改该文件:

LCD 驱动 ID 分辨率
ST7789 ST7789_ID(0x7789) 240 × 320
JD9853 JD9853_ID(0x9853) 240 × 320

图片加载

支持两种方式:

方式一:C 数组(编译时转换)

// 声明编译时生成的图片变量(变量名与图片文件名对应)
LV_IMAGE_DECLARE(your_image);

lv_obj_t *img = lv_image_create(lv_screen_active());
lv_image_set_src(img, &your_image);

方式二:文件系统路径(运行时从 LittleFS 加载)

// 先注册 JPEG 解码器
extern void lv_yopen_jpeg_init(void);
lv_yopen_jpeg_init();

lv_obj_t *img = lv_image_create(lv_screen_active());
lv_image_set_src(img, "/ycom.jpg");   // JPG 格式
// lv_image_set_src(img, "/test.bin");  // LVGL 工具转成的 BIN 格式

字体加载

通过 lv_obj_set_style_text_font 为控件设置字体:

lv_obj_t *label = lv_label_create(lv_screen_active());

// 中文使用思源黑体
lv_obj_set_style_text_font(label, &lv_font_source_han_sans_sc_24_cjk, LV_PART_MAIN);

// 图标使用 FontAwesome5
lv_obj_set_style_text_font(label, &lv_font_awesome5_16, LV_PART_MAIN);

示例工程

demo_lvgl

最简 LVGL 示例,演示文字和图片显示,适合作为新建 LVGL 应用的起点。

项目 说明
工程文件 project/lvgl.cmake
产品名 LVGL_DEMO
入口任务 demo_lcd_taskapplication_init(..., "COMP.LCD", 4, 4, NULL)
功能 显示文字标签 + 定时刷新计数 + 显示 JPG 图片

核心源码:

// components/demo/lvgl/demo_lvgl.c
lv_init();
lv_port_disp_init();
show_text_demo();
show_image_demo();
// 文字显示
s_label1 = lv_label_create(lv_scr_act());
lv_label_set_text(s_label1, "Hello LVGL 9.4.0");
lv_obj_set_pos(s_label1, 0, 10);

// 图片显示
lv_obj_t *img = lv_image_create(lv_screen_active());
lv_image_set_src(img, "/ycom.jpg");

编译命令:

.\build.bat YM310_W09C.A60 lvgl

ai_xiaozhi_wakeup_lvgl

小智 AI(唤醒版)的 LVGL 界面,采用消息驱动模型,展示电量、信号、表情、状态文字与对话气泡。

项目 说明
工程文件 project/ai_xiaozhi_wakeup_lvgl.cmake
产品名 YM310_W09C_OPEN.AI.XIAOZHI.LVGL
入口任务 ai_lvgl_taskapplication_init(..., "ai.lvgl", 6, APP_PRIORITY_LOW, NULL)
屏幕 ST7789(240 × 320)
功能 电量、信号、表情、状态文字、对话气泡、打字机效果

界面各元素通过消息队列驱动,业务线程调用接口投递消息,LVGL 任务统一处理:

// components/demo/ai_xiaozhi/ai_lvgl.h 中提供的对外接口
void ai_lvgl_signal_set(ai_lvgl_signal_level_t level, bool pdpActive);   // 信号
void ai_lvgl_vbat_set(int vol, bool isCharging);                         // 电量
void ai_lvgl_emoji_set(bool startStop);                                  // 表情
void ai_lvgl_status_text_set(const char *text, int delay);               // 状态文字
void ai_lvgl_dialog_text_set(const char *text, int delay, bool send);    // 对话文字

主循环在 lv_timer_handler() 的基础上,同时等待消息队列:

while (1) {
    uint32_t time_till_next = lv_timer_handler();
    if (time_till_next == LV_NO_TIMER_READY) {
        if (!s_lvgl_enter_sleep) time_till_next = 20;
    }
    // 等待消息并分发处理
    ret = yopen_rtos_queue_wait(s_lvgl_queue, (uint8_t *)&msg, sizeof(msg), time_till_next);
    if (ret == YOPEN_NO_ERROR) {
        switch (msg.type) {
            case AI_LVGL_MSG_TYPE_VBAT:        lvgl_vbat_set(...);        break;
            case AI_LVGL_MSG_TYPE_EMOJI:       lvgl_emoji_set(...);       break;
            case AI_LVGL_MSG_TYPE_STATUS_TEXT: lvgl_status_text_set(...); break;
            case AI_LVGL_MSG_TYPE_DIALOG_TEXT: lvgl_dialog_text_set(...); break;
            case AI_LVGL_MSG_TYPE_SIGNAL:      lvgl_signal_set(...);      break;
        }
    }
}

对话气泡在 components/demo/ai_xiaozhi/ai_lvgl_dialog.c 中实现,使用 lv_font_source_han_sans_sc_16_cjk 中文字体。

编译命令:

.\build.bat YM310_W09C.A60 ai_xiaozhi_wakeup_lvgl

附录

文件结构

components/
├── lvgl-9.4.0/                          # LVGL 核心库(9.4.0)
│   ├── lv_conf.h                         # LVGL 配置(颜色深度、内存、解码器开关等)
│   ├── lv_version.h                      # 版本号定义
│   ├── scripts/
│   │   ├── LVGLImage.py                  # 图片转换工具
│   │   └── built_in_font/                # 字体生成脚本
│   │       └── built_in_font_gen.py
│   └── src/
│       ├── font/                         # 内置字体(含思源黑体、FontAwesome5)
│       └── libs/
│           ├── yopen_jpeg/               # JPEG 解码器
│           ├── bmp/                      # BMP 解码器
│           ├── gif/                      # GIF 解码器
│           └── qrcode/                   # 二维码
├── lcd/                                  # LCD 驱动抽象层
│   └── inc/
│       ├── lcdDrv.h                      # LCD 驱动接口
│       ├── lcdDev_7789.h                 # ST7789 驱动(240×320)
│       └── lcdDev_jd9853.h               # JD9853 驱动(240×320)
└── demo/
    ├── lvgl/                             # demo_lvgl 示例
    │   ├── demo_lvgl.c                   # LVGL 应用入口
    │   ├── demo_lvgl_disp.c              # 显示驱动
    │   ├── demo_lvgl_disp.h
    │   ├── demo_lcd_conf.h               # 屏幕参数配置
    │   └── fs/root/                      # 预制文件系统(ycom.jpg、test.bin)
    └── ai_xiaozhi/                       # ai_xiaozhi_wakeup_lvgl 示例
        ├── ai_lvgl.c                     # LVGL 界面入口(消息驱动)
        ├── ai_lvgl.h                     # 对外接口与消息定义
        ├── ai_lvgl_dialog.c              # 对话气泡 UI
        └── fs/root/                      # 预制文件系统(提示音 mp3 等)

project/
├── lvgl.cmake                           # demo_lvgl 工程配置
└── ai_xiaozhi_wakeup_lvgl.cmake         # ai_xiaozhi_wakeup_lvgl 工程配置

依赖模块

模块 路径 说明
lvgl components/lvgl-9.4.0 LVGL 图形库核心
lcd components/lcd LCD 显示驱动抽象
yopen_jpeg components/lvgl-9.4.0/src/libs/yopen_jpeg JPEG 图片解码器
font components/font 独立点阵字库(宋体,非 LVGL 字体,可选)

相关文档