2、移植前的准备工作:硬件平台选择、开发环境搭建、获取LVGL源码、了解LVGL目录结构

说实话,很多朋友一上来就急着写代码,结果卡在编译环境上折腾好几天。我早期移植LVGL时也犯过这个错——拿到源码就开干,最后发现硬件选型不对,内存不够,又得从头来。所以,移植前的准备工作,真的不能跳过。

这一节,我会带你走一遍完整的准备流程。从选硬件到搭环境,再到拿到源码、看懂目录结构。每一步我都会结合自己的踩坑经验来讲。

2.1 硬件平台选择:别让内存成为瓶颈

LVGL对硬件的要求,说白了就两点:足够的RAM 和 能刷屏的接口。我见过有人用STM32F103C8T6(只有20KB RAM)硬跑LVGL,结果界面卡成PPT。嗯,这其实不是LVGL的问题,是选型没想清楚。

核心结论:LVGL的帧缓冲(framebuffer)至少需要屏幕分辨率 × 每像素字节数的内存。比如320×240的16位色屏,单缓冲就需要320×240×2 = 150KB。双缓冲翻倍,300KB。

我建议你按这个思路来选硬件:

硬件等级 典型芯片 RAM Flash 适用场景
入门级 ESP32、STM32F4系列 192KB - 512KB 1MB - 2MB 320×240以下,控件少于20个
进阶级 STM32H7、i.MX RT系列 1MB - 8MB 8MB - 32MB 800×480,复杂动画,多页面
高性能级 全志V3s、瑞芯微RK系列 64MB - 512MB 128MB - 1GB 带Linux,1080P,高级特效

我的建议:如果你刚开始学移植,首选ESP32或STM32F429。这两个芯片社区资源多,遇到问题好搜。我当年用STM32F429做第一个项目,踩的坑基本都能在论坛找到答案。

2.2 开发环境搭建:选对工具链,省一半时间

开发环境这块,我踩过最大的坑是——用Keil MDK编译LVGL,结果工程文件一多,编译一次要3分钟。后来换了VS Code + GCC,30秒搞定。你想想看,每天编译几十次,省下来的时间够喝多少杯咖啡?

我个人习惯把环境分成两类:

  • MCU类(裸机/RTOS):VS Code + GCC ARM工具链 + CMake。或者用STM32CubeIDE也行,它集成了GCC和调试器。
  • MPU类(Linux):Ubuntu + 交叉编译工具链 + Makefile。我一般用arm-linux-gnueabihf-gcc。

具体步骤,我列个清单:

  1. 安装编译器:MCU用arm-none-eabi-gcc,Linux用arm-linux-gnueabihf-gcc。记得加到系统PATH里。
  2. 安装构建工具:CMake(推荐3.16以上)、Make或Ninja。
  3. 配置调试器:OpenOCD + GDB,或者直接用JLink + Ozone。
  4. 验证环境:写一个空的main函数,编译通过就算成功。

注意:我曾经在Windows上用MinGW编译LVGL,结果链接时报了一堆undefined reference。后来发现是MinGW的C库和LVGL的POSIX线程接口不兼容。建议MCU开发直接用Linux或WSL2,省心很多。

2.3 获取LVGL源码:官方渠道最靠谱

获取源码这事,看起来简单,但版本选不对也会出问题。我建议直接从GitHub的lvgl/lvgl仓库拉取。别去第三方网站下载,那些版本可能被改过,或者缺文件。

我个人习惯用git clone,而不是下载ZIP包。为什么?因为后续更新方便,git pull一下就同步了。而且你可以切到特定tag,保证版本稳定。

# 克隆最新版
git clone https://github.com/lvgl/lvgl.git

# 切到稳定版本(比如v8.3.0)
cd lvgl
git checkout v8.3.0

如果你需要同时使用LVGL的额外组件(比如文件系统、输入设备驱动),可以一起拉取:

git clone https://github.com/lvgl/lv_drivers.git
git clone https://github.com/lvgl/lv_lib_freetype.git

小技巧:我习惯在项目目录下建一个third_party文件夹,把所有第三方库放进去。这样项目结构清晰,移植时也方便复制。

2.4 了解LVGL目录结构:别在文件海洋里迷路

拿到源码后,第一件事不是编译,而是打开目录看一眼。LVGL的目录结构设计得很清晰,但如果你不熟悉,很容易迷失在几百个文件里。

下面这张图是我自己整理的LVGL核心目录结构,你看完就能知道每个文件夹是干嘛的:

LVGL核心目录结构图 lvgl/ (根目录) src/ examples/ docs/ core/ (核心对象) draw/ (渲染引擎) widgets/ (控件库) layouts/ (布局) hal/ (硬件抽象层) misc/ (杂项工具) porting/ (移植示例) widgets/ (控件示例) styles/ (样式示例) 💡 移植时重点关注: src/hal/ 下的显示驱动和输入设备接口,以及 examples/porting/ 下的移植模板。

我来逐个解释一下这些目录的作用:

  • src/:核心源码。你移植时主要改的是src/hal/里的文件,其他目录一般不动。
  • examples/porting/:移植模板。里面有lv_port_disp.c、lv_port_indev.c、lv_port_fs.c三个文件。我建议你直接复制这三个文件到你的项目里,然后填空。
  • examples/:各种示例代码。刚开始学的时候,可以跑一跑examples/widgets里的demo,看看效果。
  • docs/:文档。虽然在线文档更全,但离线版在没网时很有用。

重点提醒:移植的核心工作都在examples/porting/里。你只需要实现三个接口:显示驱动(disp)、输入设备驱动(indev)、文件系统(fs,可选)。其他代码,LVGL已经帮你写好了。

2.5 快速验证:跑一个最小工程

环境搭好、源码拿到、目录看懂之后,我建议你立刻跑一个最小工程。哪怕屏幕上只显示一个"Hello World",也能证明你的移植链路是通的。

我一般这样操作:

  1. 在项目里新建main.c,包含lvgl.h。
  2. 调用lv_init()初始化LVGL。
  3. 调用lv_port_disp_init()初始化显示。
  4. 创建一个简单的label控件:lv_label_create(lv_scr_act())。
  5. 在while循环里调用lv_timer_handler()。
#include "lvgl.h"
#include "lv_port_disp.h"

int main(void)
{
    lv_init();
    lv_port_disp_init();

    lv_obj_t *label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "Hello LVGL!");
    lv_obj_center(label);

    while(1) {
        lv_timer_handler();
        // 这里加你的延时函数,比如 HAL_Delay(5)
    }
}

我的经验:第一次编译如果报错,别慌。90%的错误都是路径没配好,或者缺少某个头文件。检查一下你的编译器能不能找到lvgl.h,以及lv_conf.h有没有放在正确的位置。