2. 嵌入式GUI基础与LVGL运行环境

说实话,很多初学者在LVGL上栽跟头,不是因为库本身难,而是环境没搭好。我见过有人花了一周时间在STM32上折腾LVGL,结果发现是时钟配置错了。所以这一章,咱们就把环境搭建这件事彻底讲透。

2.1 两种开发路径,你该怎么选?

LVGL的开发环境,说白了就两条路:MCU实机移植和PC模拟器。我个人的建议是——先模拟器,后MCU。为什么?

  • 模拟器:编译快、调试方便、不用反复烧录。适合UI原型验证和功能开发。
  • MCU:贴近真实硬件,涉及驱动、内存、帧率优化。适合产品化阶段。

我自己做项目时,通常先在模拟器上把界面逻辑调通,再移植到STM32上做性能调优。这样效率最高。

核心原则:UI逻辑在模拟器上开发,硬件适配在MCU上完成。两者互补,不是替代关系。

2.2 基于模拟器的LVGL环境搭建(PC模拟器)

模拟器我用得最多的是SDL2 + LVGL的方案,跨平台、稳定、调试方便。下面以Windows + VS Code为例,带你走一遍。

2.2.1 准备工作

  • 安装 VS Code(或CLion、VS Studio)
  • 安装 CMake(3.16以上)
  • 安装 MinGW-w64 或 MSVC 编译器
  • 下载 SDL2 开发库(Windows版)

2.2.2 克隆LVGL官方模拟器项目

git clone https://github.com/lvgl/lv_port_pc_eclipse.git
cd lv_port_pc_eclipse

这个项目已经帮你配好了LVGL + SDL2的基础框架。你只需要改几个路径就行。

2.2.3 配置CMakeLists.txt

找到 CMakeLists.txt,设置SDL2的路径:

set(SDL2_DIR "C:/SDL2-2.26.3/cmake")
find_package(SDL2 REQUIRED)

2.2.4 编译运行

mkdir build && cd build
cmake ..
make
./lv_sim

如果一切顺利,你会看到一个LVGL的演示界面弹出来。嗯,到这里,你的模拟器环境就搭好了。

小技巧:如果编译报SDL2找不到,检查一下环境变量。我遇到过好几次,都是路径写错了。

2.3 基于MCU的LVGL移植(以STM32为例)

模拟器跑通了,接下来就是真刀真枪的MCU移植。我以STM32F429 + 480x272 RGB屏为例,这是最常见的组合。

2.3.1 移植的核心步骤

  1. 准备裸机工程:用STM32CubeMX生成一个基础工程,开启LTDC、DMA2D、SDRAM(如果外挂)。
  2. 下载LVGL源码:从GitHub拉取 lvgl 和 lv_drivers。
  3. 配置lv_conf.h:这是LVGL的“总开关”。
  4. 实现显示驱动:把画点、刷屏函数挂到LVGL上。
  5. 实现输入驱动:触摸屏或按键。
  6. 配置心跳和内存:提供系统时钟和内存分配。

2.3.2 lv_conf.h 关键配置

#define LV_COLOR_DEPTH     16
#define LV_HOR_RES_MAX     480
#define LV_VER_RES_MAX     272
#define LV_DPI_DEF         100
#define LV_MEM_CUSTOM      0
#define LV_USE_DMA2D       1

这里要注意:LV_USE_DMA2D 一定要开。STM32F4系列有硬件2D加速,不开就浪费了。我早期项目没开这个,帧率只有15fps,开了之后直接飙到45fps。

2.3.3 显示驱动实现(简化版)

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    // 将color_p数据通过LTDC刷到屏幕区域
    HAL_LTDC_Color_Fill(&hltdc, area->x1, area->y1, area->x2, area->y2, (uint32_t)color_p);
    lv_disp_flush_ready(disp_drv);
}

这个函数是LVGL和硬件的桥梁。每次LVGL需要刷新屏幕时,就会调用它。

2.3.4 触摸驱动实现

static void touch_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    // 读取触摸IC(如FT5x06)的坐标
    data->point.x = touch_x;
    data->point.y = touch_y;
    data->state = touch_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
}

我曾经踩过的坑:触摸坐标和屏幕坐标方向不一致。比如触摸X对应屏幕Y。记得做坐标映射,否则点哪里都不对。

2.4 工程结构解析

不管你是用模拟器还是MCU,LVGL的工程结构都差不多。我画了一张图,帮你理清关系。

LVGL工程结构图 应用层(UI界面) LVGL核心库(lvgl/) 显示驱动 输入驱动 系统接口(Tick/内存) 硬件层(MCU/模拟器) 说明:应用层调用LVGL API,LVGL通过驱动层与硬件交互。驱动层是移植的关键。

2.5 工程目录结构(典型)

一个标准的LVGL工程,目录结构大致如下:

project/
├── lvgl/                  # LVGL核心源码
│   ├── src/               # 核心库
│   ├── lv_conf.h          # 配置文件
│   └── lvgl.h             # 主头文件
├── lv_drivers/            # 官方驱动
│   ├── display/           # 显示驱动
│   ├── indev/             # 输入驱动
│   └── lv_drv_conf.h      # 驱动配置
├── main.c                 # 主函数
├── CMakeLists.txt         # 编译配置
└── README.md

这个结构很清晰。你只需要关注 lv_conf.h 和 lv_drv_conf.h 两个配置文件,以及自己写的 main.c。

2.6 常见问题与避坑

问题 原因 解决方案
编译报错:lv_conf.h not found 路径未包含 在CMake或IDE中添加 lvgl/ 路径
屏幕花屏 LTDC时序不对 检查HBP、HFP、VBP、VFP参数
触摸无反应 I2C地址或中断配置错误 用逻辑分析仪抓I2C波形
帧率低 未开启DMA2D 设置 LV_USE_DMA2D = 1

我曾经踩过的坑:有一次移植LVGL到STM32F429,屏幕一直闪。查了两天,最后发现是SDRAM的时序配置不对,导致LVGL的缓冲区数据被破坏。所以,先确保SDRAM读写稳定,再跑LVGL。

2.7 小结

这一章我们做了三件事:

  • 在PC上搭建了LVGL模拟器环境,方便快速开发UI。
  • 在STM32上完成了LVGL的移植,包括显示和触摸驱动。
  • 解析了LVGL的工程结构,让你知道每个文件是干什么的。

环境搭好了,下一步就是真正开始写UI了。别急,先把今天的内容消化掉。如果你在搭建过程中遇到问题,回头看看上面的表格,大部分坑我都帮你踩过了。