首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >LVGL基础之模拟开发与移植:从PC原型到嵌入式硬件的完整实践

LVGL基础之模拟开发与移植:从PC原型到嵌入式硬件的完整实践

原创
作者头像
用户12678265
发布2026-08-08 15:02:28
发布2026-08-08 15:02:28
1840
举报

LVGL基础之模拟开发与移植:从PC原型到嵌入式硬件的完整实践

引言

LVGL(Light and Versatile Graphics Library)是一款开源的嵌入式图形库,广泛应用于微控制器和资源受限的嵌入式设备上。在嵌入式GUI开发中,一个常见的痛点是:每次修改UI都需要烧录固件到硬件上验证,调试效率极低。LVGL的PC模拟器完美解决了这个问题——你可以在PC上编写和调试UI代码,确认无误后再无缝移植到嵌入式硬件上。

本文将从PC模拟开发嵌入式移植两个维度,系统性地讲解LVGL的开发流程,包含完整的代码示例和配置细节。

一、PC模拟开发:在无硬件环境下快速迭代

1.1 为什么需要PC模拟器?

在PC上运行LVGL模拟器具有以下核心优势:

  • 硬件无关性:无需开发板即可编写和运行LVGL应用
  • 跨平台支持:Windows、Linux、macOS均可运行
  • 代码可移植:PC上编写的代码可直接复制到嵌入式项目中使用
  • 调试体验优越:PC调试器更快、支持日志文件、printf调试、性能分析等

LVGL官方推荐在开始嵌入式移植之前,先在PC模拟器上完成UI开发和验证

1.2 环境准备:SDL2与构建工具

PC模拟器依赖SDL2(Simple DirectMedia Layer)作为图形后端,负责窗口创建、鼠标键盘事件处理和渲染输出。

各平台安装命令:

代码语言:javascript
复制
# Ubuntu/Debian
sudo apt-get update && sudo apt-get install -y build-essential libsdl2-dev cmake

# Arch Linux
sudo pacman -Syu && sudo pacman -S sdl2 base-devel cmake

# macOS (Homebrew)
brew install sdl2 cmake make

# Fedora
sudo dnf install @development-tools cmake SDL2-devel

1.3 获取PC模拟器项目

LVGL官方为多种IDE提供了开箱即用的模拟器项目:

IDE

推荐场景

仓库地址

VSCode

Linux/macOS(SDL)

lv_port_pc_vscode

Visual Studio

Windows

lv_port_pc_visual_studio

Eclipse

Linux/macOS(SDL)

lv_sim_eclipse_sdl

Generic Linux

fbdev/DRM/SDL切换

lv_port_linux

以VSCode为例,克隆项目:

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

--recursive参数会同时拉取LVGL核心源码作为子模块。

1.4 项目结构与配置

克隆完成后,项目结构如下:

代码语言:javascript
复制
lv_port_pc_vscode/
├── lvgl/              # LVGL核心源码(子模块)
├── lv_conf.h          # LVGL配置文件
├── main.c             # 主程序入口
├── simulator.code-workspace  # VSCode工作区文件
└── CMakeLists.txt     # CMake构建配置

关键配置文件:lv_conf.h

lvgl/lv_conf_template.h复制到项目根目录并重命名为lv_conf.h,将开头的#if 0改为#if 1以启用配置。核心配置项包括:

代码语言:javascript
复制
/* 颜色深度:根据目标显示屏设置 */
#define LV_COLOR_DEPTH 16  /* 可选: 1, 8, 16, 24, 32 */

/* 内存分配方式:0使用LVGL内置分配器,1使用标准C库 */
#define LV_MEM_CUSTOM 0

/* 动态内存大小(仅当LV_MEM_CUSTOM=0时生效) */
#define LV_MEM_SIZE (48U * 1024U)  /* 至少2KB */

/* 启用SDL后端 */
#define LV_USE_SDL 1

1.5 运行模拟器

在VSCode中:

  1. 双击simulator.code-workspace打开工作区
  2. 安装推荐的插件
  3. 点击左侧Run and Debug面板,选择Debug LVGL demo with gdb
  4. 按F5启动调试

模拟器窗口将显示LVGL的demo界面,你可以在main.c中编写自己的UI代码。

1.6 在模拟器中启用FreeRTOS

为了更真实地模拟嵌入式环境,该项目还集成了FreeRTOS。在lv_conf.h中配置:

代码语言:javascript
复制
#define LV_USE_OS LV_OS_FREERTOS

注意:FreeRTOS需要足够的堆内存,建议配置为512MB

代码语言:javascript
复制
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 512 * 1024 * 1024 ) )

1.7 多后端切换

LVGL支持多种显示后端,你可以在lv_conf.h中轻松切换:

代码语言:javascript
复制
/* 使用Linux Framebuffer替代SDL */
#define LV_USE_LINUX_FBDEV 1

然后在代码中创建对应的display:

代码语言:javascript
复制
lv_display_t *display = lv_linux_fbdev_create();
lv_linux_fbdev_set_file(display, "/dev/fb0");

二、嵌入式移植:从模拟到硬件

2.1 移植的整体流程

将LVGL移植到嵌入式平台通常需要完成以下步骤:

  1. 获取LVGL源码并集成到项目工程
  2. 配置lv_conf.h 适配目标硬件
  3. 实现显示驱动flush_cb回调)
  4. 实现输入设备驱动(触摸/按键)
  5. 提供时钟节拍lv_tick_inc
  6. 初始化LVGL并创建UI

2.2 获取与集成源码

指定版本克隆(避免使用最新版导致的不兼容):

代码语言:javascript
复制
git clone -b v8.3.10 --depth=1 https://github.com/lvgl/lvgl.git

lvgl文件夹复制到你的工程目录下,并将lv_conf_template.h复制为lv_conf.h,放置在与lvgl文件夹同级的目录中。

2.3 核心移植:显示驱动

显示驱动是移植中最关键的部分。LVGL通过flush_cb回调函数将渲染好的像素数据发送到显示屏。

步骤一:创建Display对象

代码语言:javascript
复制
#include "lvgl.h"

/* 定义显示屏分辨率 */
#define DISPLAY_WIDTH  320
#define DISPLAY_HEIGHT 240

/* 帧缓冲区(根据颜色深度计算大小) */
#define BYTES_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565))
#define BUFFER_SIZE (DISPLAY_WIDTH * DISPLAY_HEIGHT * BYTES_PER_PIXEL)

static uint8_t buf_1[BUFFER_SIZE];
static uint8_t buf_2[BUFFER_SIZE];  /* 双缓冲,可选 */

void lvgl_init(void)
{
    /* 1. 初始化LVGL核心 */
    lv_init();
    
    /* 2. 创建Display */
    lv_display_t *disp = lv_display_create(DISPLAY_WIDTH, DISPLAY_HEIGHT);
    
    /* 3. 注册flush回调 */
    lv_display_set_flush_cb(disp, my_flush_cb);
    
    /* 4. 设置帧缓冲区(双缓冲) */
    lv_display_set_buffers(disp, buf_1, buf_2, BUFFER_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL);
}

步骤二:实现flush_cb回调

代码语言:javascript
复制
void my_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
    /* area->x1, area->y1: 左上角坐标 */
    /* area->x2, area->y2: 右下角坐标 */
    /* px_map: 渲染好的像素数据指针 */
    
    int32_t width = area->x2 - area->x1 + 1;
    int32_t height = area->y2 - area->y1 + 1;
    
    /* 将px_map中的数据通过LCD驱动发送到显示屏 */
    lcd_draw_area(area->x1, area->y1, width, height, px_map);
    
    /* 重要:通知LVGL刷新完成 */
    lv_display_flush_ready(disp);
}

2.4 输入设备驱动(触摸屏)

步骤一:创建输入设备

代码语言:javascript
复制
void lvgl_touch_init(void)
{
    lv_indev_t *indev = lv_indev_create();
    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);  /* 触摸屏类型 */
    lv_indev_set_read_cb(indev, touch_read_cb);
}

步骤二:实现read回调

代码语言:javascript
复制
void touch_read_cb(lv_indev_t *indev, lv_indev_data_t *data)
{
    int16_t x, y;
    uint8_t pressed;
    
    /* 从触摸驱动读取坐标和按压状态 */
    touch_driver_read(&x, &y, &pressed);
    
    if (pressed) {
        data->state = LV_INDEV_STATE_PRESSED;
        data->point.x = x;
        data->point.y = y;
    } else {
        data->state = LV_INDEV_STATE_RELEASED;
    }
}

2.5 时钟节拍(Tick)

LVGL需要知道时间流逝才能处理动画和超时。你需要定期调用lv_tick_inc()

代码语言:javascript
复制
/* 在定时器中断或RTOS任务中调用 */
void timer_interrupt_handler(void)
{
    lv_tick_inc(1);  /* 假设定时器周期为1ms */
}

2.6 主循环

main()函数中完成初始化后,进入主循环:

代码语言:javascript
复制
int main(void)
{
    /* 初始化硬件外设(LCD、触摸、时钟等) */
    hardware_init();
    
    /* 初始化LVGL */
    lvgl_init();
    
    /* 创建UI */
    create_ui();
    
    while (1) {
        lv_timer_handler();  /* 处理LVGL任务 */
        delay_ms(5);         /* 建议5ms间隔 */
    }
}

2.7 针对具体平台的特殊说明

STM32平台:将LVGL文件夹复制到[project_folder]/Drivers/lvgl,在STM32CubeIDE中通过Project → Properties → C/C++ Build → Settings → Include paths添加LVGL目录。建议使用DMA2D硬件加速提升性能。

ESP32平台:最简单的方式是通过esp_lvgl_port组件集成:

代码语言:javascript
复制
idf.py add-dependency "espressif/esp_lvgl_port^2.3.0"

Linux Framebuffer平台:使用官方lv_port_linux项目,通过Kconfig菜单选择fbdev、DRM或SDL后端。

三、调试技巧与常见问题

3.1 内存不足

如果LVGL运行异常,首先检查lv_conf.h中的LV_MEM_SIZE是否足够。对于复杂UI,建议至少配置为64KB以上。

3.2 显示异常(花屏/错位)

  • 检查LV_COLOR_DEPTH是否与显示屏匹配(RGB565对应16位)
  • 确认flush_cb中发送数据的格式(颜色字节顺序)是否正确
  • 验证帧缓冲区地址是否对齐

3.3 模拟器窗口不显示

在启用FreeRTOS的模拟器项目中,如果SDL窗口不出现,通常是因为堆内存不足。将configTOTAL_HEAP_SIZE调整为512MB可解决。

3.4 性能优化建议

  • 使用双缓冲减少屏幕撕裂
  • 启用DMA/EDMA进行数据传输
  • lv_conf.h中禁用不需要的模块(如未使用的widget、字体、图片格式等)
  • 合理设置LV_DISPLAY_RENDER_MODE_PARTIAL渲染面积

四、总结

LVGL的模拟开发嵌入式移植构成了完整的开发闭环:在PC模拟器上快速验证UI逻辑和交互效果,然后将经过验证的代码无缝迁移到嵌入式硬件上。这种工作流大幅提升了嵌入式GUI的开发效率。

本文涵盖了从环境搭建、模拟运行到实际移植的完整技术路径,包含lv_conf.h配置、flush_cb显示驱动实现、输入设备注册等核心代码。掌握这些基础,你就能够在任何嵌入式平台上高效地使用LVGL构建图形界面。

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

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

目录
  • LVGL基础之模拟开发与移植:从PC原型到嵌入式硬件的完整实践
    • 引言
    • 一、PC模拟开发:在无硬件环境下快速迭代
      • 1.1 为什么需要PC模拟器?
      • 1.2 环境准备:SDL2与构建工具
      • 1.3 获取PC模拟器项目
      • 1.4 项目结构与配置
      • 1.5 运行模拟器
      • 1.6 在模拟器中启用FreeRTOS
      • 1.7 多后端切换
    • 二、嵌入式移植:从模拟到硬件
      • 2.1 移植的整体流程
      • 2.2 获取与集成源码
      • 2.3 核心移植:显示驱动
      • 2.4 输入设备驱动(触摸屏)
      • 2.5 时钟节拍(Tick)
      • 2.6 主循环
      • 2.7 针对具体平台的特殊说明
    • 三、调试技巧与常见问题
      • 3.1 内存不足
      • 3.2 显示异常(花屏/错位)
      • 3.3 模拟器窗口不显示
      • 3.4 性能优化建议
    • 四、总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档