X-TRACK 中的 LVGL 8.3 图形库:从嵌入式 GUI 特性到模拟器与移植实战

原创2026-10-03 16:34:39600 阅读
文章标签:智能硬件嵌入式硬件开发

X-TRACK 中的 LVGL 8.3 图形库:从嵌入式 GUI 特性到模拟器与移植实战

导读

LVGL(Light and Versatile Graphics Library)是一个用 C 编写、面向嵌入式系统的开源图形库,它以"轻量、通用、可裁剪"著称,为微控制器设备提供按钮、图表、列表、滑动条等完整控件体系与动画、抗锯齿等高级渲染能力。本仓库 Software/X-Track/Simulator/LVGL.Simulator/lvgl/README.md 是随 X-TRACK 项目一同携带的 LVGL 官方文档,正文将以其为核心骨架,完整继承其特性清单、硬件需求、平台支持与示例代码,并结合 X-TRACK 仓库中真实的 lv_conf 配置、Windows 桌面模拟器入口、Linux framebuffer 移植代码以及 Dialplate 页面实现,带你从"读文档"走向"看真实工程如何落地"。


一、仓库中 LVGL 的版本与文档定位

X-TRACK 将 LVGL 以源码方式内置在模拟器工程与各移植目标中。本关联文档所在目录为 Software/X-Track/Simulator/LVGL.Simulator/lvgl,同目录下还配有中文版本 README_zh.md,两份文档互为镜像。

从版本头文件可以确认仓库携带的精确版本号:

#define LVGL_VERSION_MAJOR 8
#define LVGL_VERSION_MINOR 3
#define LVGL_VERSION_PATCH 0

见 lvgl.h。也就是说,本文所讨论的 LVGL 功能、宏与 API 均以 v8.3.0 为准。项目根目录 README.md 的更新日志也印证了这条升级路径:v2.4 版本"更新 lvgl v8.3.0 dev",v2.7 版本继续"同步 commit 941d95b0…"保持与主线对齐。


二、核心特性:一套为嵌入式场景设计的 GUI 能力

原文档对 LVGL 的特性做了系统归纳,以下逐条继承并结合仓库配置佐证:

  • 丰富的模块化图形组件:按钮(button)、图表(chart)、列表(list)、滑动条(slider)、图片(image)等一应俱全。在 X-Track 的 lv_conf.h 中,LV_USE_BTN、LV_USE_CHART、LV_USE_SLIDER、LV_USE_IMG、LV_USE_LABEL、LV_USE_ROLLER、LV_USE_SWITCH、LV_USE_TEXTAREA、LV_USE_TABLE、LV_USE_ARC、LV_USE_MENU、LV_USE_METER、LV_USE_SPINNER 等均置为 1,几乎全量启用,供表盘与系统信息页使用。
  • 高级图形引擎:动画(animation)、抗锯齿(anti-aliasing)、透明度(opacity)、平滑滚动、图层混合。X-Track 表盘页面正是用 lv_anim_timeline 编排入场动画(详见下文第四节)。
  • 多种输入设备:触摸屏、鼠标、键盘、编码器、按键等。X-Track 的 Windows 模拟器调用 lv_win32_add_all_input_devices_to_group(NULL) 一次性挂载全部输入;Linux 移植则用 evdev 接入指针设备(见 main.cpp)。
  • 多显示设备支持:单库可驱动多个 display。
  • 硬件无关性:可运行于任意满足要求的 MCU 与显示器。仓库实际落地在国产 AT32F403A / AT32F435(见 MDK-ARM_F403A 与 MDK-ARM_F435)、Linux framebuffer(Linux)以及 Windows 桌面模拟器(LVGL.Simulator)四类目标上。
  • 低资源可裁剪:最低 64 kB Flash、16 kB RAM 即可运行。X-Track 在 lv_conf.h 中将内置内存池 LV_MEM_SIZE 配置为 72U * 1024U 字节(72 KB),并针对 MCU 端与 PC 端分别选用内置分配器或系统 malloc/free。
  • 多语言与 UTF-8:支持中文、日文、韩文、阿拉伯文等。仓库配置 LV_TXT_ENC LV_TXT_ENC_UTF8(lv_conf.h),X-Track 的界面即包含中文场景。
  • 类 CSS 样式与布局:支持 Flexbox 与 Grid 布局。仓库在 lv_conf.h 中同时开启 LV_USE_FLEX 1 与 LV_USE_GRID 1;Dialplate 页面的底部信息区就用 LV_FLEX_FLOW_ROW_WRAP 实现了信息卡片自动换行排列(见下文)。
  • OS / 外部内存 / GPU 可选:内建支持 STM32 DMA2D、SWM341 DMA2D、NXP PXP 与 VGLite;X-Track 的 lv_conf 中这些加速项均为 0(未启用),属于"可选不必须"的典型用法。
  • 单帧缓冲也能平滑渲染:X-Track 的 Linux 移植即单缓冲方案(详见第四节)。
  • 纯 C 实现、兼容 C++:X-Track 的应用层是 C++ 工程,直接 #include "lvgl/lvgl.h" 即可无缝互操作。
  • MicroPython 绑定:可通过 Python 调用 LVGL API。
  • PC 模拟器:无硬件即可开发调试,X-Track 的 LVGL.Simulator 工程 正是这一特性的工程化产物。
  • 丰富例程与文档:lvgl 目录内自带 examples(415+ 个示例文件)与 docs 文档树,可离线查阅。

三、硬件需求与支持平台

3.1 硬件需求表(继承原文)

原文档给出了一份权威的最低/推荐配置表,这是评估"我的芯片能否跑 LVGL"的第一依据:

项目 最低要求 推荐配置
架构 16/32/64 位微控制器或微处理器 同左
时钟 > 16 MHz > 48 MHz
Flash/ROM > 64 kB > 180 kB
静态 RAM > 16 kB > 48 kB
Draw buffer(绘制缓冲) > 1 × 水平分辨率像素 > 1/10 屏幕大小
编译器 C99 或更新 同左

注意:实际内存占用会随架构、编译器与编译选项变化,表中数值仅供参考。X-Track 在 240×240、RGB565(每像素 2 字节)的显示方案下,将 LVGL 内存池设为 72 KB,并提供 StackInfo 与 HAL::Memory_DumpInfo() 等工具用于实测栈与堆占用,正是"按需调整、实测为准"的工程实践。

3.2 支持平台(继承原文)

LVGL 平台无关,只要满足上表即可运行,原文档列举的平台包括:

  • NXP:Kinetis、LPC、iMX、iMX RT
  • STM32F1 / F3 / F4 / F7 / L4 / L5 / H7 系列
  • Microchip:dsPIC33、PIC24、PIC32MX、PIC32MZ
  • Linux frame buffer(/dev/fb)
  • Raspberry Pi、Espressif ESP32、Infineon Aurix、Nordic NRF52、Quectel 模组、SYNWIT SWM341 等

LVGL 同时还以以下形式分发:Arduino 库、PlatformIO 包、Zephyr 库、ESP32 组件、NXP MCUXpresso 组件、NuttX 库、RT-Thread RTOS 包等。

仓库实例对照:X-Track 虽然没有直接使用上述商业平台,但完美印证了"平台无关"这一主张——同一套 LVGL v8.3 同时跑在国产 AT32F403A/AT32F435 裸机(MDK 工程)、Linux framebuffer(Software/X-Track/Linux)和 Windows Win32(LVGL.Simulator.cpp)上,应用层代码零改动复用。


四、快速入门:官方 10 步学习路径与仓库落地对照

原文档给出了官方推荐的学习顺序,X-Track 仓库可以逐条"对号入座":

  1. 看在线演示体验 LVGL —— 对 X-Track 而言,最直接的体验方式是直接构建运行仓库的 LVGL.Simulator.sln 工程;
  2. 读文档 Introduction —— 仓库内 lvgl/docs 提供了完整文档副本;
  3. 看 Quick overview 熟悉基础 —— 对应 lvgl 源码中 lv_obj、lv_style 等核心对象模型;
  4. 搭建 PC 模拟器 —— X-Track 的 LVGL.Simulator 就是现成答案,基于 lv_drivers 的 win32drv 实现,240×240 窗口模拟真实表盘分辨率(见 LVGL.Simulator.cpp);
  5. 动手实践例程 —— lvgl/examples 内置 100+ 示例;
  6. 参考移植指南并移植到开发板 —— X-Track 的 lv_port、lv_port_fs_sdfat.cpp、lv_port_indev.cpp 以及 MDK-ARM_F403A/Platform/HAL/HAL_Display.cpp 就是一套完整的移植范例;
  7. 细读 Overview 深入理解 —— 源码 lvgl/src 按 core / draw / widgets / layouts / misc 分层组织,是研读内部机制的入口;
  8. 查阅 Widgets 文档 —— lvgl/docs/widgets 在仓库内可离线浏览;
  9. 有问题上论坛提问;
  10. 阅读贡献指南参与社区。

4.1 Windows 模拟器主循环(仓库实证)

X-Track 的模拟器入口展示了 LVGL 应用的标准骨架(LVGL.Simulator.cpp):

int main()
{
    lv_init();
    lv_fs_if_init();

    if (!lv_win32_init(GetModuleHandleW(NULL), SW_SHOW,
                       SCREEN_HOR_RES, SCREEN_VER_RES, ...)) return -1;
    lv_win32_add_all_input_devices_to_group(NULL);

    HAL::HAL_Init();
    App_Init();

    while (!lv_win32_quit_signal)
    {
        lv_timer_handler();   /* 驱动 LVGL 定时器与重绘 */
        HAL::HAL_Update();    /* 刷新传感器/GPS 等外设数据 */
        Sleep(1);
    }
    App_Uninit();
    return 0;
}

lv_timer_handler() 是 LVGL 的事件引擎核心:所有动画、刷新、输入处理都在其中推进。

4.2 Linux framebuffer 移植要点(仓库实证)

main.cpp 给出了一个教科书级的 LVGL 显示/输入移植流程:

/* 1. 初始化 fbdev,取屏幕尺寸 */
fbdev_init();
fbdev_get_sizes(&width, &height);

/* 2. 分配绘制缓冲(单缓冲方案) */
lv_color_t* buf = (lv_color_t*)malloc(disp_buf_size * sizeof(lv_color_t));
static lv_disp_draw_buf_t disp_buf;
lv_disp_draw_buf_init(&disp_buf, buf, NULL, disp_buf_size);

/* 3. 注册显示驱动:flush 回调指向 fbdev_flush */
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.flush_cb = fbdev_flush;
disp_drv.hor_res  = width;
disp_drv.ver_res  = height;
lv_disp_drv_register(&disp_drv);

/* 4. 注册指针输入设备:evdev 读取鼠标/触摸 */
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type     = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb  = evdev_read;
lv_indev_register(&indev_drv);

同时,Linux 端通过 lv_conf.h 中的 LV_TICK_CUSTOM 1 使用自定义时钟源,并在 main.cpp 末尾实现了 custom_tick_get()(基于 gettimeofday),省去了手动调用 lv_tick_inc() 的麻烦。


五、第一个控件:C 与 MicroPython 官方示例(完整继承)

原文档给出的按钮 + 标签示例是理解 LVGL API 的最佳起点,此处完整保留并补充注释:

5.1 C 语言版本

lv_obj_t * btn = lv_btn_create(lv_scr_act());                   /*在活动屏幕创建按钮*/
lv_obj_set_pos(btn, 10, 10);                                    /*设置位置 (x, y)*/
lv_obj_set_size(btn, 100, 50);                                  /*设置尺寸 (宽, 高)*/
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); /*绑定点击事件回调*/

lv_obj_t * label = lv_label_create(btn);                        /*在按钮上创建标签*/
lv_label_set_text(label, "Button");                             /*设置标签文本*/
lv_obj_center(label);                                           /*标签水平垂直居中*/
...

void btn_event_cb(lv_event_t * e)
{
  printf("Clicked\n");
}

要点解读:lv_scr_act() 返回当前活动屏幕对象;lv_obj_* 系列是通用对象 API;事件回调通过 lv_obj_add_event_cb 注册,LV_EVENT_CLICKED 表示点击释放事件——这是 LVGL v8 的事件模型(v7 之前是 lv_obj_set_event_cb 直接赋回调)。

5.2 MicroPython 版本

def btn_event_cb(e):
  print("Clicked")

# 创建按钮与标签
btn = lv.btn(lv.scr_act())
btn.set_pos(10, 10)
btn.set_size(100, 50)
btn.add_event_cb(btn_event_cb, lv.EVENT.CLICKED, None)

label = lv.label(btn)
label.set_text("Button")
label.center()

MicroPython 绑定把 C API 按"类方法"风格暴露:lv.btn、lv.label 对应 C 的 lv_btn_create、lv_label_create,事件常量位于 lv.EVENT 命名空间下。

5.3 仓库实战:Dialplate 表盘页的真实 UI 代码

官方示例演示了"创建—定位—绑定事件"三步,X-Track 的 DialplateView.cpp 则展示了同一套 API 在真实产品中的组合用法——创建容器、移除默认样式、设置圆角与背景色、加载自定义字体、创建并居中对齐标签:

lv_obj_t* cont = lv_obj_create(par);
lv_obj_remove_style_all(cont);                       /*清空默认样式,便于完全自定义*/
lv_obj_set_size(cont, LV_HOR_RES, 142);              /*宽度铺满屏幕*/
lv_obj_set_style_bg_opa(cont, LV_OPA_COVER, 0);      /*不透明背景*/
lv_obj_set_style_bg_color(cont, lv_color_hex(0x333333), 0);
lv_obj_set_style_radius(cont, 27, 0);                /*圆角半径 27*/
lv_obj_set_y(cont, -36);

lv_obj_t* label = lv_label_create(cont);
lv_obj_set_style_text_font(label, ResourcePool::GetFont("bahnschrift_65"), 0); /*自定义大号数字字体*/
lv_obj_set_style_text_color(label, lv_color_white(), 0);
lv_label_set_text(label, "00");                      /*速度初始值*/
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 63);        /*顶部居中*/

同一个页面还用 lv_anim_timeline 编排了 500ms 缓出(ease_out)的入场动画(DialplateView.cpp),对应官方特性清单中的"动画引擎";用 LV_FLEX_FLOW_ROW_WRAP 让 4 个信息卡片自动换行(DialplateView.cpp),对应"Flexbox 布局"。可以看出:官方文档描述的每一项能力,在 X-Track 中都有对应的真实调用点。


六、lv_conf.h 关键配置解析(仓库实证)

LVGL 通过 lv_conf.h 进行编译期裁剪。仓库将配置文件放在 Software/X-Track/Simulator/LVGL.Simulator/lv_conf.h,MCU 端也各自维护一份(见 MDK-ARM_F435/Platform/lv_port 目录下的配置链)。以下摘取与本项目强相关的关键项:

配置宏 仓库取值 含义与影响
LV_COLOR_DEPTH 16 RGB565 色彩深度,2 字节/像素,与 240×240 ST7789 屏匹配;SPI 8 位接口场景下配合 LV_COLOR_16_SWAP 1 交换高低字节
LV_MEM_SIZE 72U * 1024U 内置内存池大小(字节);LV_MEM_CUSTOM 在 Arduino 端为 0、PC 端为 1,PC 直接使用 malloc/free
LV_DISP_DEF_REFR_PERIOD 16 显示刷新周期(ms),对应约 60 FPS 的刷新节奏;更新日志显示从 20ms 优化到 16ms
LV_INDEV_DEF_READ_PERIOD 30 输入设备轮询周期(ms),适合编码器/按键场景
LV_TICK_CUSTOM 1 自定义时基,Linux 用 custom_tick_get()、Win32 用 timeGetTime()、Arduino 用 millis()
LV_DPI_DEF 130 默认 DPI,用于推算控件默认尺寸与内边距
LV_USE_FLEX / LV_USE_GRID 1 / 1 启用 Flexbox 与 Grid 布局引擎
LV_TXT_ENC LV_TXT_ENC_UTF8 UTF-8 编码,支持中文等多语言文本
LV_FONT_MONTSERRAT_10 1 内置 Montserrat 10 号字体,且 LV_FONT_DEFAULT 指向它;其余内置字体全部关闭以省 Flash,界面改用 ResourcePool 加载的自定义字体(如 bahnschrift)
LV_USE_LOG Arduino 端 0、PC 端 1 日志开关,PC 端通过 printf 输出,便于调试
LV_USE_PNG Arduino 端 0、PC 端 1 PNG 解码器按平台使能——更新日志 v2.2 明确指出"默认开启 PNG 解码器,受限于内存 MCU 无法使用"

LV_USE_GPU_*(STM32 DMA2D、SWM341、NXP PXP/VG-Lite、SDL)在仓库中全部为 0,与官方"GPU 可选、不必须"的描述一致;LV_BUILD_EXAMPLES、各 LV_USE_DEMO_* 均为 0,说明 X-Track 将 LVGL 作为库裁剪集成、不携带官方 demo,这也呼应了更新日志 v1.6"去除 lv_examples,减少工程体积"的做法。


七、周边生态:文件系统与扩展组件

LVGL 的可用性还依赖周边配套,仓库中同样有据可查:

  • 文件系统抽象(lv_fs_if):X-Track 的 Windows 模拟器调用 lv_fs_if_init()(LVGL.Simulator.cpp),并通过 lv_fs_pc.c 将 PC 目录映射为 LVGL 驱动器,用于读取离线地图瓦片;MCU 端则用 lv_port_fs_sdfat.cpp 对接 SdFat,实现 SD 卡轨迹记录与地图读取。
  • 输入设备封装(lv_port_indev):lv_port_indev.cpp 为编码器创建全局默认 group(lv_group_get_default()),让 Dialplate、LiveMap、SystemInfos 等页面共享同一焦点管理——对应官方"encoder 支持"特性。
  • PNG 解码:PC 端通过 lv_png_init 启用内置 PNG 解码器(lv_conf.h),配合 lv_img_png 组件在 LiveMap 页面渲染 PNG 格式地图瓦片。

八、阅读路径速查

围绕本文主题,建议按以下顺序深入仓库:

综上:LVGL 文档回答"这个库能做什么、门槛多高、怎么入门",而 X-Track 仓库则回答"在真实的嵌入式骑行码表上它被怎样配置、移植和组合使用"。两者互为印证,是学习嵌入式 GUI 开发的一条高质量完整链路。

登录后查看全文
X-TRACK