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,我们只需要做两件事:

  1. 把 lv_conf_template.h 复制为 lv_conf.h
  2. 修改 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 知识体系总览

下面这张图总结了本章的核心流程,你可以把它当作一个快速索引:

LVGL源码获取与编译流程 1. GitHub拉取源码 2. 复制lv_conf.h并配置 3a. CMake编译(PC模拟器) 3b. Makefile编译(嵌入式MCU) SDL模拟器运行 烧录到开发板 建议:PC上先用CMake调试UI,再移植到MCU用Makefile编译

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反而更流畅了——因为减少了撕裂。