2、LVGL源码结构解析:lvgl文件夹下的核心组件

拿到LVGL源码包,解压后第一件事是什么?我个人的习惯是——先别急着编译,把文件夹结构看明白。你想想看,一个几千文件的工程,如果连目录都搞不清,后面出问题你都不知道去哪找原因。

LVGL的源码结构其实很清晰。核心就三个目录加一个配置文件。咱们一个一个说。

2.1 src目录——真正的核心代码

src 是LVGL的心脏。所有绘图、事件、控件、布局的逻辑都在这里。我刚开始移植的时候,总想着把整个 src 都加进工程,结果编译报错一大堆。后来才明白——这里面的文件是按模块组织的,你只需要选你需要的。

src 下面又分了几个子目录:

子目录 作用 我的建议
core 对象系统、显示驱动、输入设备等核心机制 必须全部加入工程
draw 底层绘图引擎,包括软件渲染和硬件加速接口 必须加入,但可以裁剪渲染后端
hal 硬件抽象层,定义了你需要实现的接口 必须加入,这是移植的关键
widgets 所有标准控件,比如按钮、标签、滑块 按需加入,用不到的可以去掉
layouts Flex和Grid布局引擎 推荐加入,布局会方便很多
misc 工具函数、数学运算、内存管理 必须加入,很多模块依赖它
others 文件系统、监控、快照等扩展功能 按需加入
我曾经踩过的坑: 第一次移植时,我把 widgets 目录下所有 .c 文件一股脑全加进去了。结果编译出来的固件大了 200KB,而我的 Flash 只剩 150KB。后来只保留了按钮和标签,瞬间就塞进去了。所以——不要贪多,用多少加多少。

2.2 examples目录——最好的学习资料

examples 目录里放的是官方示例代码。每个控件、每个功能都有对应的 demo。我个人觉得,这是学习LVGL最快的方式。

举个例子,你想知道按钮怎么用?直接打开 examples/widgets/btn 目录,里面就有完整的代码。复制粘贴,改改参数,跑起来看看效果。比看文档快多了。

但要注意一点:examples 里的代码是为 PC 模拟器写的。它默认用 SDL 模拟显示和输入。你移植到嵌入式平台时,不能直接拿过来用。需要把显示和输入的部分换成你自己的驱动。

我的习惯: 先在 PC 上用模拟器跑通 examples,确认功能正常。然后再把逻辑代码搬到嵌入式工程里。这样能快速定位问题是出在 LVGL 本身,还是出在移植环节。

2.3 demos目录——开箱即用的演示

demos 目录里是几个完整的演示应用。比如 widgets 演示了所有控件的效果,music 是一个音乐播放器界面,benchmark 用来测试性能。

这些 demo 的好处是——你移植完成后,可以直接跑一个 demo 来验证整个系统是否正常工作。如果 demo 能跑起来,说明你的移植基本没问题了。

但说实话,demos 目录在正式产品里基本用不上。它主要是用来做验证和展示的。我一般只在移植初期跑一下 benchmark,看看帧率是否达标。

2.4 lv_conf.h——整个LVGL的开关

这个文件太重要了。我甚至觉得,移植LVGL的 80% 工作都是在配置这个文件。

lv_conf.h 是LVGL的全局配置文件。它控制着:

  • 最大显示分辨率
  • 颜色深度(16位还是32位)
  • 内存池大小
  • 开启哪些控件和功能
  • 日志等级
  • 等等等等

这个文件默认是 lv_conf_template.h,你需要把它复制一份,重命名为 lv_conf.h,然后放到你的工程里。

打开文件,你会看到一堆 #if 0 和 #if 1 的宏开关。说白了,就是把你要用的功能打开,不需要的关掉。

/* 颜色深度设置 */
#define LV_COLOR_DEPTH     16

/* 内存池大小,单位是字节 */
#define LV_MEM_SIZE        (32 * 1024)

/* 开启日志 */
#define LV_USE_LOG         1
#define LV_LOG_LEVEL       LV_LOG_LEVEL_INFO

/* 开启按钮控件 */
#define LV_USE_BTN         1

/* 关闭不需要的控件,节省内存 */
#define LV_USE_CHART       0
#define LV_USE_CALENDAR    0
关键点: lv_conf.h 必须在所有 LVGL 源文件之前被包含。我见过有人把它放在某个 .c 文件里,结果编译报错说找不到定义。正确的做法是:在编译器的全局包含路径里加上这个文件,或者直接在工程设置里把它设为强制包含头文件。

嗯,这里要注意一个细节:LV_MEM_SIZE 的设置。这个值决定了LVGL内部动态分配内存的上限。设太小了,界面复杂时会分配失败;设太大了,MCU的内存又不够。我一般先设 32KB,跑起来看看,如果报内存不足就往上加。

2.5 整体结构图

说了这么多,咱们用一张图把关系理清楚:

LVGL 源码结构总览 lvgl/ src/ examples/ demos/ core draw hal widgets layouts misc others widgets 示例 layouts 示例 其他功能示例 widgets demo music demo benchmark lv_conf.h(全局配置开关) lv_conf.h 控制所有模块的开关和参数,是移植的第一步

2.6 移植时的文件取舍策略

说白了,你不需要把整个 lvgl 目录都塞进工程。我一般按这个策略来:

  1. src 目录: core、draw、hal、misc 这四个子目录全部加入。widgets 只加你用到的控件。layouts 建议加入,但如果你只用绝对定位,可以不加。others 看需求。
  2. examples 目录: 不加入嵌入式工程。只在 PC 模拟器上参考。
  3. demos 目录: 移植初期加入 benchmark 用于验证。正式产品中全部移除。
  4. lv_conf.h: 必须加入,且放在编译器全局包含路径中。
一个小技巧: 我习惯在工程里建一个 lvgl_port 文件夹,里面放 lv_conf.h 和我的显示/输入驱动文件。这样 LVGL 官方源码和我的移植代码分得清清楚楚,升级 LVGL 版本时直接替换 lvgl 目录就行,不会覆盖我的配置。

好了,源码结构就说到这。记住一句话:src 是引擎,examples 是说明书,demos 是试车跑道,lv_conf.h 是方向盘。搞清楚了这些,后面移植的时候你就知道该动哪里了。