首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >嵌入式技术之LVGL基础之模拟开发和移植

嵌入式技术之LVGL基础之模拟开发和移植

原创
作者头像
用户12678265
发布2026-08-10 14:29:47
发布2026-08-10 14:29:47
1380
举报

嵌入式技术之LVGL基础之模拟开发和移植

前言

在嵌入式GUI开发领域,LVGL(Light and Versatile Graphics Library)凭借其开源、轻量、控件丰富且无需复杂版权授权的特性,已成为物联网设备与工业HMI项目的首选图形框架。然而,许多开发者在初次接触时往往直接跳入硬件适配,导致调试效率极低。高效的实践路径应是 “模拟先行,分层适配,驱动解耦”

本文将围绕LVGL模拟开发环境的搭建,以及针对STM32、ESP32、Linux Framebuffer三大主流平台的移植方法展开,帮助开发者建立起一套可复用的图形框架移植工作流。

一、LVGL模拟开发环境搭建

1.1 为什么需要模拟环境?

在物理硬件上烧录并调试GUI代码,每一次修改动辄耗费数分钟的编译与烧录时间,这对UI交互逻辑的快速迭代而言是不可接受的。模拟环境将整体开发周期缩短约40%,且能够在无硬件环境下并行推进前端与驱动工作。

LVGL官方提供了多种模拟平台,其中基于SDL2的方案最为成熟。本文将基于 VSCode + CMake + MinGW + SDL2 这套组合,搭建LVGL 9.3的PC模拟环境。

1.2 环境搭建步骤

步骤一:安装MinGW-w64编译器

MinGW-w64是将C代码变成Windows可执行文件的编译器。推荐从SourceForge下载x86_64-posix-seh版本的离线包。

解压到指定目录(如D:\DevTools\mingw64),然后将bin目录添加到系统环境变量Path中。验证安装:

代码语言:javascript
复制
gcc --version
步骤二:获取LVGL PC模拟器工程模板

访问GitHub仓库lv_port_pc_vscode,切换到release/v9.3分支:

代码语言:javascript
复制
git clone -b release/v9.3 https://github.com/lvgl/lv_port_pc_vscode.git
步骤三:安装SDL2

SDL2是一个跨平台多媒体库,在模拟环境中扮演“屏幕”和“输入设备”的角色。将SDL2开发包解压到工程目录下的SDL2文件夹。

步骤四:配置CMake

用VSCode打开工程文件夹,安装推荐的CMake Tools和C/C++扩展。CMake会自动检测工具链并生成构建文件。

关键配置在CMakeLists.txt中指定SDL2路径:

代码语言:javascript
复制
set(SDL2_DIR "${CMAKE_SOURCE_DIR}/SDL2")
find_package(SDL2 REQUIRED)
target_link_libraries(lvgl_simulator PRIVATE SDL2::SDL2)
步骤五:配置lv_conf.h

lv_conf_template.h重命名为lv_conf.h,并启用SDL显示驱动:

代码语言:javascript
复制
#define LV_USE_SDL 1
步骤六:编译运行

点击VSCode左下角的“编译”按钮,或通过CMake构建后运行。成功后会弹出一个SDL窗口,显示LVGL的示例界面。

1.3 模拟环境的核心原理

在PC环境中,LVGL的disp_drv(显示驱动)不再指向真实的LCD控制器,而是映射到SDL窗口;indev_drv(输入驱动)则绑定到鼠标事件。开发者完成UI逻辑验证后,再将代码无缝迁移到硬件平台。

二、LVGL移植的核心原理

2.1 LVGL对硬件的最小依赖

要将LVGL运行于具体主控芯片上,必须明确其依赖的硬件资源最小集合。LVGL核心仅要求三部分能力:

  1. 定时时钟滴答:用于动画帧刷新
  2. 显存写入接口:将渲染好的颜色数组推送到显示屏
  3. 外部输入事件:触摸或按键(可选)

2.2 三个核心移植接口

移植工作的实质是编写三个薄层函数:

函数

功能

disp_flush()

将LVGL渲染缓冲区发送至LCD

touchpad_read()

读取触摸坐标

keyboard_read()

读取按键状态

所有与具体MCU外设相关的底层代码——如SPI、I2C、FMC或并行8080总线时序——均被封装在这三个接口背后。

三、STM32平台移植实战

3.1 源码准备

以LVGL v8.2为例,首先从官方仓库获取源码,精简后仅保留必要文件:

代码语言:javascript
复制
lvgl/
├── examples/porting/    # 移植模板文件
├── src/                 # 核心源码
│   ├── core/
│   ├── font/
│   ├── hal/
│   ├── misc/
│   └── widgets/
├── lv_conf_template.h   # 配置文件模板
└── lvgl.h

3.2 添加源码到工程

在STM32CubeIDE中,将LVGL文件夹复制到[project_folder]/Drivers/lvgl。右键点击该文件夹,选择Add/remove include path添加头文件路径。

在Keil MDK中,创建分组并添加所有src/目录下的.c文件(共约149个)。

3.3 配置lv_conf.h

lv_conf_template.h重命名为lv_conf.h,修改条件编译指令:

代码语言:javascript
复制
#if 1 /* Set it to "1" to enable content */

设置颜色深度和屏幕尺寸:

代码语言:javascript
复制
#define LV_COLOR_DEPTH 16
#define LV_HOR_RES_MAX 240
#define LV_VER_RES_MAX 320

3.4 设置时基(心跳)

LVGL需要1ms周期的时钟滴答来驱动动画和任务调度。使用STM32的SysTick或基本定时器:

代码语言:javascript
复制
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    lv_tick_inc(1);  /* 每1ms调用一次 */
}

3.5 配置显示驱动

修改lv_port_disp_template.c,启用条件编译:

代码语言:javascript
复制
#if 1  /* 改为1启用 */

实现disp_init()函数初始化LCD:

代码语言:javascript
复制
static void disp_init(void)
{
    lcd_init();  /* 自己的LCD初始化函数 */
}

实现disp_flush()回调,将缓冲区数据发送到屏幕:

代码语言:javascript
复制
static void disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *color_p)
{
    /* 将color_p中的数据发送到LCD指定区域 */
    lcd_draw_area(area->x1, area->y1, area->x2, area->y2, color_p);
    lv_display_flush_ready(disp);
}

3.6 缓冲区配置

三种缓冲模式可供选择:

  • 单缓冲:内存占用最低,至少为屏幕高度的10%
  • 双缓冲:渲染与刷新并行,适合DMA传输
  • 全尺寸双缓冲:性能最佳,内存占用最大

代码语言:javascript
复制
static uint8_t buf_1[BUFF_SIZE];
static uint8_t buf_2[BUFF_SIZE];

lv_display_set_buffers(disp, buf_1, buf_2, BUFF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL);

3.7 主函数初始化

代码语言:javascript
复制
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    /* 初始化LCD、触摸等外设 */
    lv_init();
    lv_display_t *disp = lv_display_create(240, 320);
    lv_display_set_flush_cb(disp, disp_flush);
    lv_display_set_buffers(disp, buf_1, buf_2, BUFF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL);
    
    /* 创建UI */
    ui_init();
    
    while (1) {
        lv_timer_handler();
        HAL_Delay(5);
    }
}

四、ESP32平台移植实战

4.1 使用ESP-IDF组件方式

LVGL在ESP32上可通过ESP-IDF的组件系统便捷集成。

方式一:Git Submodule

代码语言:javascript
复制
git submodule add https://github.com/lvgl/lvgl.git components/lvgl

方式二:IDF Component Manager(推荐)

代码语言:javascript
复制
idf.py add-dependency lvgl/lvgl>=8.*

下一轮构建时,LVGL组件会自动从组件仓库获取。

4.2 配置LVGL

通过idf.py menuconfig进入配置菜单,依次进入Component config -> LVGL configuration进行配置。

4.3 显示与触摸驱动

LVGL官方提供了lvgl_esp32_drivers仓库,支持多种显示和触摸控制器:

代码语言:javascript
复制
git submodule add https://github.com/lvgl/lvgl_esp32_drivers.git components/lvgl_esp32_drivers

4.4 内存优化建议

对于ESP32-S3等带PSRAM的芯片,建议将LVGL帧缓冲分配在PSRAM中,以释放内部SRAM给Wi-Fi和蓝牙等关键任务使用。

五、Linux Framebuffer平台移植实战

5.1 什么是Framebuffer?

Framebuffer是Linux内核为显示设备提供的一个抽象层,应用程序只需像操作普通内存一样向这块内存写入颜色数据,内核和驱动就会负责同步到物理显示设备。这种方式没有复杂的客户端-服务器架构,延迟极低。

5.2 移植步骤

第一步:获取源码

代码语言:javascript
复制
git clone https://github.com/lvgl/lvgl.git
git clone https://github.com/lvgl/lv_port_linux_frame_buffer.git

第二步:复制移植文件

lv_port_linux_frame_buffer中的main.cMakefile复制到LVGL工程目录。

第三步:使能Framebuffer

在配置文件中将USE_FBDEV设置为1:

代码语言:javascript
复制
#define USE_FBDEV 1

第四步:确认Framebuffer设备

移植前需确保Framebuffer本身工作正常:

代码语言:javascript
复制
cat /dev/urandom > /dev/fb0  # 测试屏幕是否显示随机噪点

第五步:编译运行

代码语言:javascript
复制
make
./lvgl_demo

5.3 高级选项:DRM/KMS

对于生产环境的嵌入式目标,推荐使用DRM/KMS后端替代传统的Framebuffer。DRM提供更现代的直渲染能力,支持多图层和硬件加速。

六、移植优化与调试技巧

6.1 内存优化

  • 启用部分刷新:LVGL默认采用“脏矩形”更新机制,只绘制发生变化的部分区域
  • 合理设置缓冲区大小:缓冲区至少为屏幕高度的10%
  • 利用DMA:对于支持DMA的MCU,使用DMA进行显存搬运可大幅提升性能

6.2 性能调优

  • 启用双缓冲:实现渲染与刷新的并行
  • 配置合适的颜色深度:RGB565(16位)在多数场景下平衡了性能与画质
  • 调整LVGL的刷新率:通过LV_DISP_DEF_REFR_PERIOD控制

6.3 常见问题排查

问题

可能原因

解决方案

屏幕无显示

缓冲区未正确刷新

检查disp_flush回调是否调用了lv_display_flush_ready

动画卡顿

时基未正确设置

确认lv_tick_inc被周期性调用

内存不足

缓冲区过大

减小缓冲区或启用部分刷新模式

触摸无响应

输入驱动未注册

检查indev_drv的注册和回调实现

七、总结

本文系统介绍了LVGL从PC模拟开发到硬件移植的完整路径:

  1. 模拟开发:通过VSCode + CMake + SDL2搭建PC模拟环境,实现UI的快速迭代验证
  2. 移植原理:理解LVGL对硬件的三个最小依赖——时钟滴答、显存写入、输入事件
  3. STM32移植:基于HAL库实现显示驱动和时基配置
  4. ESP32移植:利用ESP-IDF组件系统快速集成
  5. Linux移植:通过Framebuffer或DRM/KMS在嵌入式Linux上运行

掌握这套“模拟先行,分层适配”的方法论后,无论面对何种主控芯片,移植工作的复杂度都将被收敛到可控范围内。建议开发者在实际项目中先跑通模拟环境验证UI逻辑,再根据目标硬件平台选择合适的移植方案,这样可以显著提升开发效率,降低调试成本。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 嵌入式技术之LVGL基础之模拟开发和移植
    • 前言
    • 一、LVGL模拟开发环境搭建
      • 1.1 为什么需要模拟环境?
      • 1.2 环境搭建步骤
      • 1.3 模拟环境的核心原理
    • 二、LVGL移植的核心原理
      • 2.1 LVGL对硬件的最小依赖
      • 2.2 三个核心移植接口
    • 三、STM32平台移植实战
      • 3.1 源码准备
      • 3.2 添加源码到工程
      • 3.3 配置lv_conf.h
      • 3.4 设置时基(心跳)
      • 3.5 配置显示驱动
      • 3.6 缓冲区配置
      • 3.7 主函数初始化
    • 四、ESP32平台移植实战
      • 4.1 使用ESP-IDF组件方式
      • 4.2 配置LVGL
      • 4.3 显示与触摸驱动
      • 4.4 内存优化建议
    • 五、Linux Framebuffer平台移植实战
      • 5.1 什么是Framebuffer?
      • 5.2 移植步骤
      • 5.3 高级选项:DRM/KMS
    • 六、移植优化与调试技巧
      • 6.1 内存优化
      • 6.2 性能调优
      • 6.3 常见问题排查
    • 七、总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档