3、LVGL环境搭建:源码获取、编译配置、移植到不同平台(STM32/ESP32/Linux)、模拟器使用
好,咱们进入第三章。环境搭建这事儿,说简单也简单,说复杂也复杂。我见过不少新手,代码写得挺溜,结果卡在环境配置上,一卡就是好几天。说白了,环境搭不好,后面的学习全是纸上谈兵。
这一章,我会带着你从源码获取开始,一步步把LVGL跑起来。咱们会聊到STM32、ESP32、Linux这三个主流平台,还会讲讲模拟器——我个人觉得,模拟器是学习LVGL最快的方式,没有之一。
本章核心目标:让你能在自己的开发板上点亮LVGL的第一个Demo,并且知道怎么在不同平台之间切换。
3.1 源码获取:从GitHub到本地
LVGL的源码托管在GitHub上,项目名叫lvgl。我个人习惯直接克隆主仓库,而不是下载压缩包——这样方便后续切换版本和拉取更新。
git clone https://github.com/lvgl/lvgl.git
cd lvgl
git checkout v8.3.0 # 建议使用稳定版本
嗯,这里要注意一点。LVGL的版本迭代挺快的,v8和v9的API有不小差异。我建议初学者先锁定v8.3.x,这个版本最成熟,资料也最多。等你把v8玩熟了,再去看v9的新特性,会轻松很多。
小技巧:除了主仓库,你还需要一个lv_drivers仓库,里面封装了显示和触摸的驱动接口。克隆命令:git clone https://github.com/lvgl/lv_drivers.git
源码目录结构大致是这样的:
| 目录/文件 | 说明 |
|---|---|
src/ |
核心源码,包括控件、布局、绘图等 |
examples/ |
官方示例,学习必备 |
lv_conf_template.h |
配置文件模板,你需要复制并重命名为lv_conf.h |
lvgl.h |
主头文件,包含所有API声明 |
3.2 编译配置:lv_conf.h 是核心
LVGL的配置全在一个文件里——lv_conf.h。这个文件控制着所有功能的开关、内存大小、颜色深度等等。说白了,它就是LVGL的「总开关面板」。
我曾经在一个项目里,因为忘了改LV_COLOR_DEPTH,导致显示出来的颜色全是花的。排查了半天,最后发现是配置里写的是16位,但屏幕硬件是18位。嗯,这种坑踩过一次就记住了。
下面是一个典型的配置片段:
/* 颜色深度:16位 RGB565 */
#define LV_COLOR_DEPTH 16
/* 内存池大小:32KB */
#define LV_MEM_SIZE (32U * 1024U)
/* 开启 Tick 接口 */
#define LV_TICK_CUSTOM 1
/* 开启日志输出,调试用 */
#define LV_USE_LOG 1
#define LV_LOG_LEVEL LV_LOG_LEVEL_INFO
/* 开启常用控件 */
#define LV_USE_BTN 1
#define LV_USE_LABEL 1
#define LV_USE_SLIDER 1
警告:LV_MEM_SIZE不要设得太小。如果内存不够,LVGL会静默失败——界面不显示,但也不报错。我建议至少32KB起步,复杂界面需要128KB以上。
3.3 移植到不同平台
移植LVGL的核心工作就两件事:提供显示缓冲和提供时钟节拍。其他都是锦上添花。
3.3.1 STM32 平台
STM32上跑LVGL,我一般用CubeMX生成基础工程,然后手动集成LVGL源码。显示驱动需要实现一个flush_cb回调函数:
/* 显示刷新回调 */
static void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/* 将 color_p 中的数据通过 SPI/FSMC 发送到 LCD */
LCD_DrawBitmap(area->x1, area->y1, area->x2 - area->x1 + 1, area->y2 - area->y1 + 1, (uint16_t *)color_p);
/* 通知 LVGL 刷新完成 */
lv_disp_flush_ready(disp_drv);
}
时钟节拍一般用硬件定时器,每1ms调用一次lv_tick_inc(1)。嗯,这里有个细节——定时器优先级要设得合适,别被其他中断抢占了导致界面卡顿。
3.3.2 ESP32 平台
ESP32上移植LVGL更简单,因为官方提供了lvgl_esp32_drivers组件。你只需要在menuconfig里配置好屏幕型号和引脚,剩下的驱动代码自动生成。
我个人觉得ESP32是学习LVGL性价比最高的硬件平台——便宜、WiFi/蓝牙都有、社区活跃。唯一要注意的是,ESP32的FreeRTOS任务栈大小要留够,我给LVGL的任务栈一般设4096字节。
/* ESP32 上的 LVGL 任务 */
void lvgl_task(void *arg)
{
lv_init();
lvgl_driver_init(); // 官方驱动初始化
/* 创建 UI */
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello ESP32!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
while(1) {
lv_task_handler();
vTaskDelay(pdMS_TO_TICKS(5));
}
}
3.3.3 Linux 平台
Linux下跑LVGL,通常用FrameBuffer或DRM/KMS驱动。我建议用DRM,它比老旧的FrameBuffer更稳定,支持双缓冲和垂直同步。
Linux移植不需要自己写底层驱动,直接用lv_drivers里的fbdev.c或drm.c就行。编译时链接-ldrm -lgbm库。
/* Linux DRM 初始化示例 */
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.flush_cb = drm_flush; // lv_drivers 提供
disp_drv.hor_res = 1024;
disp_drv.ver_res = 600;
lv_disp_drv_register(&disp_drv);
3.4 模拟器使用:学习效率翻倍
好了,重点来了。模拟器是我最推荐的学习方式。你想想看,在PC上写代码、编译、运行,整个过程不到10秒。而在嵌入式平台上,每次烧录都要等几十秒甚至几分钟。
LVGL官方提供了基于SDL2的模拟器。你只需要安装SDL2库,然后编译lv_sim_eclipse_sdl项目就行。
# Ubuntu 上安装 SDL2
sudo apt-get install libsdl2-dev
# 克隆模拟器项目
git clone https://github.com/lvgl/lv_sim_eclipse_sdl.git
cd lv_sim_eclipse_sdl
make -j4
./build/bin/main
运行起来后,你会看到一个窗口,里面就是LVGL的Demo界面。鼠标可以模拟触摸,键盘可以模拟按键。调试起来非常方便。
我的习惯:先在模拟器上把UI布局和交互逻辑调通,确认没问题了,再移植到硬件上。这样硬件调试的时间能减少80%。
模拟器还有一个隐藏好处——你可以用IDE的断点调试功能。这在嵌入式平台上很难做到,但在PC上就是家常便饭。遇到界面卡死或者显示异常,直接打断点看变量,问题一目了然。
3.5 避坑指南
最后,分享几个我踩过的坑:
- lv_conf.h 没生效——检查文件路径,必须放在
lvgl目录的上一级,或者在编译器里指定LV_CONF_PATH宏。 - 屏幕闪烁——多半是刷新频率太低,或者没有使用双缓冲。在
lv_conf.h里把LV_DISP_DEF_REFR_PERIOD改成30ms试试。 - 触摸没反应——先确认触摸驱动是否返回了正确的坐标值。我习惯在
touchpad_read_cb里加个串口打印,看看数据对不对。 - 编译报错说找不到头文件——检查编译器包含路径,确保
lvgl和lv_drivers的根目录都在路径里。
嗯,环境搭建这部分就到这里。记住一句话:模拟器是学习利器,硬件移植是最终目标。先把模拟器玩熟,后面的事情就水到渠成了。