2、开发环境搭建:硬件平台选择、软件工具链安装、源码获取与工程结构解析

好,咱们正式开始动手了。这一章我带你搞定开发环境。说白了,就是把你电脑变成一台能编译、能烧录、能调试 LVGL 的工作站。我这些年移植 LVGL 踩过的坑,有一半都出在环境没搭好上。所以这一章,咱们稳扎稳打。

2.1 硬件平台怎么选?STM32 还是 ESP32?

很多新手一上来就问:「老师,我用哪个板子好?」我的回答是:看你手头有什么,就用什么。但如果你让我推荐,我会说——

对比项 STM32(以F4/H7为例) ESP32(以S3为例)
主频 168~480 MHz 240 MHz
RAM 192KB~1MB(片内) 512KB(片内)+ 外挂PSRAM
Flash 1~2MB(片内) 16MB(外挂)
显示接口 LTDC/FMC(需外挂SDRAM) SPI/QSPI + PSRAM
开发难度 中等(寄存器/HAL库) 较低(Arduino/ESP-IDF)
成本 板子约80~200元 板子约30~60元

我个人习惯:做产品原型用 STM32,因为它的显示控制器 LTDC 是硬核,刷屏不占 CPU。做快速验证或小项目,我直接上 ESP32,便宜、WiFi 蓝牙都有,社区资源也丰富。

我的建议:如果你是第一次接触 LVGL,用 ESP32 + 一块 3.5 寸 SPI 屏。成本不到 80 块,资料多,遇到问题好搜。

2.2 软件工具链安装

工具链这东西,装一次管三年。但装错了,能折腾你三天。我分三个平台来讲。

2.2.1 IDE 选择

  • STM32 用户:我推荐 STM32CubeIDE。免费、自带 CubeMX 配置、GCC 编译器、OpenOCD 调试器,一套搞定。你不需要再装别的。
  • ESP32 用户:用 VS Code + ESP-IDF 插件。或者直接用 Arduino IDE(适合纯新手)。我个人更倾向 ESP-IDF,因为 LVGL 的官方例程就是基于它写的。

2.2.2 编译器与调试器

嗯,这里要注意:STM32CubeIDE 自带的 ARM GCC 版本可能比较老。我遇到过编译 LVGL 的某些新特性时报错,比如 _Generic 关键字不支持。解决办法是手动更新编译器。

手动更新 GCC 步骤(STM32 用户):

  1. 下载最新版 ARM GCC:arm-gnu-toolchain(去 ARM 官网)
  2. 解压到 C:\ARM_GCC\/opt/arm-gcc/
  3. 在 CubeIDE 中:Window → Preferences → C/C++ → Build → Global Tools Paths,把编译器路径指过去

调试器方面,ST-Link 是标配。ESP32 用板载的 USB 转串口(CH340/CP2102)就能烧录和看日志。如果你想单步调试 ESP32,需要买一个 JTAG 调试器,比如 FT2232H。说实话,我很少用 JTAG 调 ESP32,printf 大法它不香吗?

2.2.3 源码获取

LVGL 的源码托管在 GitHub 上。我建议你直接 clone 官方仓库,而不是下载 zip。为什么?因为后续你想切分支、看历史提交、或者拉取子模块,git 操作方便得多。

# 克隆 LVGL 主仓库
git clone https://github.com/lvgl/lvgl.git

# 进入目录
cd lvgl

# 查看所有版本标签
git tag -l

# 切换到稳定版本(比如 v8.3.11)
git checkout v8.3.11
避坑指南:我曾经直接下载了 master 分支的 zip,结果编译时发现缺少 lvgl/exampleslvgl/demos 文件夹。原来这些是子模块,zip 下载不会自动拉取。所以一定要用 git clone --recursive 或者手动 git submodule update --init

2.3 工程结构解析

源码拿到了,咱们看看里面都有啥。我带你走一遍核心目录。

lvgl/
├── lvgl/                  # 核心源码
│   ├── src/               # 所有源文件
│   │   ├── core/          # 核心对象、事件、刷新机制
│   │   ├── draw/          # 绘图引擎(软件渲染、GPU加速)
│   │   ├── font/          # 字体引擎
│   │   ├── hal/           # 硬件抽象层(显示、触摸、文件系统)
│   │   ├── misc/          # 工具函数、内存管理、日志
│   │   ├── layouts/       # 布局引擎(Flex、Grid)
│   │   ├── libs/          # 第三方库(PNG、GIF、Freetype等)
│   │   ├── others/        # 其他功能(文件浏览器、日历等)
│   │   └── widgets/       # 所有控件(按钮、标签、滑块等)
│   ├── examples/          # 示例代码
│   ├── demos/             # 演示应用(音乐播放器、手表等)
│   ├── docs/              # 文档(HTML、PDF)
│   └── lv_conf_template.h # 配置文件模板
└── lv_porting/            # 移植模板(显示、触摸、文件系统)

你想想看,这个结构其实很清晰。核心在 src/ 里,你基本不用动。你需要改的是 lv_conf.h(从模板复制过来)和 lv_porting/ 里的文件。

我的习惯:我会把 lv_conf_template.h 复制到项目根目录,重命名为 lv_conf.h。然后在 lv_porting 里,我只保留 lv_port_disp.clv_port_indev.c,其他删掉。这样工程清爽,不会引入无关代码。

2.3.1 配置文件 lv_conf.h 的关键宏

这个文件是 LVGL 的「总开关」。我挑几个你一定会碰到的:

宏定义 作用 我的建议值
LV_COLOR_DEPTH 颜色深度:16 或 32 16(节省一半显存)
LV_MEM_SIZE LVGL 内部堆大小 32KB(简单UI)~ 128KB(复杂UI)
LV_HOR_RES_MAX 屏幕水平分辨率 你的屏幕宽度
LV_VER_RES_MAX 屏幕垂直分辨率 你的屏幕高度
LV_USE_DEMO_WIDGETS 启用控件演示 1(调试阶段打开)
我曾经犯过的错:第一次移植时,我把 LV_MEM_SIZE 设成了 8KB,结果创建几个按钮就崩了。后来查文档才发现,LVGL 默认的控件对象每个就要占用几十到几百字节。所以,起步至少 32KB,别省这点内存。

2.3.2 移植模板 lv_porting 怎么用?

打开 lv_porting 文件夹,你会看到三个核心文件:

  • lv_port_disp.c/.h —— 显示驱动接口
  • lv_port_indev.c/.h —— 输入设备接口(触摸、按键、编码器)
  • lv_port_fs.c/.h —— 文件系统接口(如果你需要读取 SD 卡图片)

每个文件里都有 /* TODO */ 注释,告诉你哪里需要填代码。说白了,你只需要实现三个函数:

  • disp_flush() —— 把显存里的像素数据刷到屏幕
  • touchpad_read() —— 读取触摸坐标
  • encoder_read() —— 读取编码器状态(如果有)

剩下的初始化、缓冲区管理,模板里都写好了框架。你照着填空就行。

小技巧:disp_flush() 函数里,记得在刷完一帧后调用 lv_disp_flush_ready()。我见过有人忘了这步,结果屏幕一直黑着。LVGL 会等这个回调通知,才继续下一帧的绘制。

2.4 验证环境是否搭好

工具链装完、源码拉下来、工程结构看懂了。怎么知道环境没问题?我的做法是:先编译一个空工程,只包含 LVGL 核心库,不跑任何 UI。编译通过,说明工具链和源码路径没问题。

# 在项目目录下执行(以 GCC 为例)
make clean
make -j4

# 如果看到类似输出,说明编译成功
# arm-none-eabi-size build/lvgl_demo.elf
# text    data     bss     dec     hex
# 123456   1234    5678  130368  1fd40

然后,我会烧录一个最简单的 demo:只显示一个标签 "Hello LVGL"。如果屏幕亮了,字出来了,恭喜你,环境搭建完成!

最后说一句:环境搭建这一步,急不得。我见过太多人卡在编译器版本不对、路径有中文、或者缺少某个 DLL 上。如果你遇到问题,先检查这三样:编译器路径、头文件包含路径、链接脚本里的内存分配。八成问题出在这三个地方。