3. 源码获取与编译:从GitHub拉取LVGL源码,使用CMake与Makefile进行编译
说实话,很多初学者在LVGL上栽的第一个跟头,不是API不会用,而是源码根本编译不过。我记得刚入行那会儿,光配置环境就折腾了两天,最后发现是CMake版本不对。这一章,我就带你把这步走稳。
本章核心目标:从GitHub拉取LVGL源码,掌握CMake和Makefile两种编译方式,最终在你的开发板上跑起第一个LVGL程序。
3.1 从GitHub拉取源码
LVGL的官方仓库在GitHub上,地址是 https://github.com/lvgl/lvgl。我个人习惯用 git clone 而不是直接下载ZIP包,因为后续更新方便。
git clone https://github.com/lvgl/lvgl.git
cd lvgl
git checkout v8.3.0 # 建议锁定稳定版本
版本选择建议:目前v8.3.x是生产环境最稳定的版本。v9.0虽然新,但部分驱动和第三方库还没跟上。我在一个量产项目上吃过v9.0早期版本的亏——某个触摸驱动死活不兼容,最后回退到v8.3才解决。
拉下来之后,目录结构是这样的:
lvgl/
├── src/ # 核心源码
├── examples/ # 示例工程
├── demos/ # 演示程序
├── docs/ # 文档
├── lv_conf_template.h # 配置文件模板
├── lvgl.h # 主头文件
└── CMakeLists.txt # CMake构建文件
嗯,这里要注意:lv_conf_template.h 需要复制一份并重命名为 lv_conf.h,否则编译会报错。我见过有人直接改模板文件,结果下次更新仓库时全丢了。
3.2 两种编译方式:CMake vs Makefile
LVGL官方同时支持CMake和Makefile。你可能会问:到底用哪个?我的建议是——看你的目标平台。
| 对比项 | CMake | Makefile |
|---|---|---|
| 跨平台能力 | 强(Windows/Linux/Mac) | 弱(依赖GNU工具链) |
| 配置复杂度 | 中等 | 低 |
| IDE集成 | 好(可生成VS/Xcode工程) | 差 |
| 嵌入式MCU场景 | 常用(配合ARM GCC) | 更常用(裸机/RTOS) |
| 学习曲线 | 稍陡 | 平缓 |
说白了,如果你在Linux上做原型验证,CMake是首选;如果你在STM32这类MCU上做产品,Makefile更直接。我在一个项目中两种混用过——PC端用CMake调试UI逻辑,板子上用Makefile烧录。
3.3 使用CMake编译(PC模拟器)
我们先在PC上跑起来,这样调试方便。LVGL官方提供了SDL模拟器,可以直接在电脑上预览UI效果。
3.3.1 安装依赖
# Ubuntu/Debian
sudo apt install build-essential cmake libsdl2-dev
# macOS
brew install cmake sdl2
3.3.2 拉取模拟器工程
git clone https://github.com/lvgl/lv_port_pc_eclipse.git
cd lv_port_pc_eclipse
这个工程已经帮你配好了CMakeLists.txt,我们只需要做两件事:
- 把
lv_conf_template.h复制为lv_conf.h - 修改
lv_conf.h中的LV_COLOR_DEPTH为32
3.3.3 编译与运行
mkdir build && cd build
cmake ..
make -j4
./demo
如果一切顺利,你会看到一个LVGL的演示界面弹出来。我第一次看到那个旋转的仪表盘时,说实话挺激动的——一个嵌入式GUI库能在PC上跑,意味着调试效率翻倍。
常见坑点:
cmake ..报错找不到SDL2 → 检查libsdl2-dev是否安装make编译极慢 → 用-j4或-j8开启多核- 运行时黑屏 → 检查
lv_conf.h中LV_COLOR_DEPTH是否匹配显示器
3.4 使用Makefile编译(嵌入式MCU)
现在我们把目光转向真正的战场——嵌入式MCU。以STM32F429为例,它自带LCD控制器和触摸屏,是LVGL的经典搭档。
3.4.1 工程结构
一个典型的LVGL+STM32工程长这样:
project/
├── Core/ # STM32 HAL库代码
├── Drivers/ # 外设驱动
├── lvgl/ # LVGL源码(直接拷贝或submodule)
├── lv_conf.h # LVGL配置
├── lv_port_disp.c # 显示驱动接口
├── lv_port_indev.c # 输入设备接口
├── Makefile # 主Makefile
└── startup_stm32f429xx.s # 启动文件
3.4.2 编写Makefile
这里我给出一个最小可用的Makefile模板。注意,我故意去掉了不必要的优化选项,让你能看清核心逻辑。
# 编译器
CC = arm-none-eabi-gcc
# 目标芯片
MCU = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
# LVGL源码路径
LVGL_DIR = ./lvgl
LVGL_SRC = $(wildcard $(LVGL_DIR)/src/*.c) \
$(wildcard $(LVGL_DIR)/src/**/*.c)
# 应用源码
APP_SRC = main.c \
lv_port_disp.c \
lv_port_indev.c
# 所有源文件
SRC = $(APP_SRC) $(LVGL_SRC)
# 编译选项
CFLAGS = $(MCU) -O2 -Wall -I. -I$(LVGL_DIR)
# 链接选项
LDFLAGS = $(MCU) -TSTM32F429ZITx_FLASH.ld
# 目标
all: firmware.elf
firmware.elf: $(SRC:.c=.o)
$(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o firmware.elf
我的经验:这个Makefile虽然能用,但实际项目里我会加上 -Werror 把警告当错误处理。曾经有一个项目,因为一个未使用的变量警告被忽略,结果在特定条件下触发了栈溢出——排查了整整三天。
3.4.3 编译与烧录
make clean
make
arm-none-eabi-objcopy -O binary firmware.elf firmware.bin
# 然后用ST-Link或J-Link烧录
如果编译通过但屏幕没反应,别慌。先检查 lv_port_disp.c 中的 disp_flush 函数——我遇到过最多次的问题就是这里:要么DMA没配置对,要么像素格式不匹配。
3.5 知识体系总览
下面这张图总结了本章的核心流程,你可以把它当作一个快速索引:
3.6 避坑指南:我踩过的三个坑
最后,分享几个我在实际项目中遇到的典型问题,希望能帮你省点时间。
坑一:lv_conf.h 路径不对
我曾经把 lv_conf.h 放在了 lvgl/ 目录下,结果编译时LVGL内部找不到配置,全部用了默认值。正确做法是放在工程根目录,并在编译选项中用 -I. 包含当前路径。
坑二:内存分配不足
LVGL默认使用动态内存分配。在STM32上,如果你不修改 LV_MEM_SIZE,默认只有16KB。一个稍微复杂的界面就会导致分配失败。我建议至少设到64KB:
#define LV_MEM_SIZE (64U * 1024U)
坑三:显示驱动刷新率不匹配
LVGL的刷新频率默认是30fps。如果你的屏幕刷新一帧需要50ms,那实际帧率只有20fps。这时候要么优化驱动,要么调低 LV_DISP_DEF_REFR_PERIOD。我在一个项目里把刷新周期从30ms改到50ms,UI反而更流畅了——因为减少了撕裂。