3、LVGL框架初探:架构概览、源码结构与配置详解

好,咱们正式开始接触LVGL了。说实话,我第一次打开LVGL源码的时候,也被它的目录结构吓了一跳——文件夹一层套一层,光看名字根本不知道从哪下手。但后来摸透了它的设计思路,发现其实挺清晰的。

这一节,我带你把LVGL的骨架摸一遍。搞懂了架构和配置,后面移植起来就顺风顺水了。

3.1 LVGL整体架构:分层设计

LVGL本质上是一个分层设计的图形框架。你可以把它想象成一个三层蛋糕:

  • 底层(硬件抽象层):负责跟你的硬件打交道——屏幕怎么刷、触摸屏怎么读、定时器怎么跑。
  • 中间层(核心库):这是LVGL的心脏,包含对象管理、事件处理、样式系统、动画引擎等。
  • 上层(组件层):按钮、标签、滑块、图表……你看到的那些现成控件都在这里。

我习惯把这种结构叫做「依赖倒置」——上层依赖中间层,中间层依赖底层,但底层反过来通过回调函数被上层调用。这样做的好处很明显:换硬件平台时,你只需要重写底层那几行代码,上层完全不用动。

LVGL 三层架构 上层:组件层(Widgets) 按钮 · 标签 · 滑块 · 图表 · 列表 · 下拉菜单 …… 中间层:核心库(Core) 对象管理 · 事件系统 · 样式 · 动画 · 布局 · 绘图 底层:硬件抽象层(HAL) 显示驱动 · 输入设备驱动 · 定时器 / 心跳 移植时只需修改底层 HAL,上层代码无需改动

核心思想:LVGL的架构设计让你「一次编写,到处运行」。换屏幕、换MCU,改的只是底层那几行驱动代码。

3.2 源码结构分析:别被文件夹吓到

下载LVGL源码后,你会看到这样一个目录结构(以LVGL v8.x为例):

lvgl/
├── lv_conf_template.h    # 配置文件模板
├── lvgl.h                # 主头文件,包含所有API
├── src/
│   ├── lv_core/          # 核心:对象、事件、样式
│   ├── lv_draw/          # 绘图引擎:软件渲染、GPU加速
│   ├── lv_extra/         # 扩展控件:图表、仪表盘等
│   ├── lv_font/          # 字体引擎:内置字体、自定义字体
│   ├── lv_hal/           # 硬件抽象层:显示、输入、定时器
│   ├── lv_misc/          # 杂项:内存管理、日志、数学工具
│   ├── lv_themes/        # 主题:默认主题、深色主题
│   └── lv_widgets/       # 基础控件:按钮、标签、滑块……
└── examples/             # 示例代码

我个人建议你重点关注三个目录:

  • lv_hal/:移植的核心战场,你主要改这里面的文件。
  • lv_core/:理解LVGL的运行机制,对象树、事件传播都在这里。
  • lv_conf_template.h:配置开关,决定LVGL的功能集和内存占用。

我记得第一次移植时,傻乎乎地把整个源码都看了一遍,结果越看越懵。后来才明白——你不需要理解每一行代码,只需要知道「哪个文件管什么事」就够了。

我的习惯:把 lv_conf_template.h 复制一份改名为 lv_conf.h,放在项目根目录。然后从 lv_hal/ 开始读,其他目录用到什么再看什么。

3.3 配置选项详解:lv_conf.h 是总开关

lv_conf.h 是LVGL的「总控制台」。你想想看,一个图形库要适配从几百KB内存的MCU到几百MB内存的MPU,没有配置开关根本不可能。这个文件里定义了上百个宏,我挑几个最关键的给你讲透。

3.3.1 基础开关:启用/禁用LVGL

#if LV_CONF_H
/* 如果定义了 LV_CONF_H,表示使用外部配置 */
#else
/* 否则使用 lv_conf.h 中的默认配置 */
#endif

/* 最重要的开关:启用LVGL */
#define LV_USE_LVGL_H 1

这个没什么好说的,必须打开。

3.3.2 颜色深度:决定显存占用

/* 颜色深度:16位(RGB565)最常见 */
#define LV_COLOR_DEPTH 16

/* 可选值:8、16、32 */
/* 8位:颜色少,省内存,适合低端MCU */
/* 16位:平衡之选,大多数场景够用 */
/* 32位:颜色丰富,但显存翻倍 */

我在一个STMF103项目上用过8位色,说实话,颜色过渡有明显的色块感。如果你的屏幕支持16位,我建议直接用16位,性价比最高。

3.3.3 内存管理:别让LVGL吃掉你的RAM

/* LVGL动态内存大小(单位:字节) */
#define LV_MEM_SIZE (32 * 1024)    /* 32KB */

/* 内存管理方式 */
#define LV_MEM_CUSTOM 0            /* 0=使用LVGL内置内存管理器 */
                                   /* 1=使用标准库malloc/free */

这里有个坑:LV_MEM_SIZE 不是越大越好。我见过有人给一个只有64KB RAM的芯片分配了48KB给LVGL,结果系统直接跑不起来。一般来说,给LVGL分配总RAM的30%~50%比较合理。

避坑指南:我曾经在一个项目里把 LV_MEM_SIZE 设得太大,导致 lv_init() 直接卡死。后来用调试器一看,内存分配失败,系统进入硬件错误中断。建议先设小一点,跑起来后再慢慢调大。

3.3.4 显示缓冲区:影响刷新流畅度

/* 显示缓冲区大小(单位:像素数) */
#define LV_HOR_RES_MAX         240    /* 屏幕宽度 */
#define LV_VER_RES_MAX         320    /* 屏幕高度 */
#define LV_DPI_DEF             100    /* 每英寸点数,用于字体缩放 */

/* 缓冲区行数:1、2、10…… */
#define LV_BUFFER_HEIGHT       10     /* 建议10~60行 */

缓冲区越大,刷新越流畅,但占用的RAM也越多。我一般先用10行测试,如果屏幕刷新有撕裂感,再逐步增加。

3.3.5 控件开关:按需裁剪

/* 基础控件 */
#define LV_USE_BTN      1    /* 按钮 */
#define LV_USE_LABEL    1    /* 标签 */
#define LV_USE_SLIDER   1    /* 滑块 */

/* 高级控件(按需开启) */
#define LV_USE_CHART    0    /* 图表:占用较大,不用就关掉 */
#define LV_USE_TABLE    0    /* 表格 */
#define LV_USE_CALENDAR 0    /* 日历 */

这个很好理解——用不到的控件就关掉,能省不少Flash和RAM。我习惯先把所有控件都关了,用到哪个再开哪个。

3.3.6 其他重要配置

配置项 说明 推荐值
LV_USE_LOG 日志输出,调试时打开 1(调试)/ 0(发布)
LV_TICK_PERIOD LVGL心跳周期(毫秒) 1~5
LV_USE_ANIMATION 动画引擎 1(建议开启)
LV_USE_FILESYSTEM 文件系统支持(如需要加载图片) 0(无文件系统)/ 1(有)
LV_FONT_DEFAULT 默认字体 &lv_font_montserrat_14

小技巧:发布版本记得把 LV_USE_LOG 关掉,否则串口会被日志刷爆,影响性能。我吃过这个亏,调试时开着日志没问题,一发布忘了关,客户反馈界面卡顿……

3.4 配置实战:一个典型的STM32F4配置

给你看一个我实际用过的配置片段,跑在STM32F429上,屏幕是480x272的RGB接口屏:

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

/* 内存 */
#define LV_MEM_SIZE (64 * 1024)    /* 64KB */
#define LV_MEM_CUSTOM 0

/* 显示参数 */
#define LV_HOR_RES_MAX 480
#define LV_VER_RES_MAX 272
#define LV_DPI_DEF 100
#define LV_BUFFER_HEIGHT 20

/* 开启的控件 */
#define LV_USE_BTN      1
#define LV_USE_LABEL    1
#define LV_USE_SLIDER   1
#define LV_USE_BAR      1
#define LV_USE_IMG      1

/* 关闭的控件 */
#define LV_USE_CHART    0
#define LV_USE_TABLE    0
#define LV_USE_CALENDAR 0
#define LV_USE_MSGBOX   0

/* 其他 */
#define LV_USE_LOG      1    /* 调试阶段开着 */
#define LV_TICK_PERIOD  1
#define LV_USE_ANIMATION 1

这个配置跑下来,Flash占用大约120KB,RAM占用约80KB(含显存缓冲区)。对于F429来说绰绰有余。

3.5 小结:你现在该做什么

嗯,这一节内容不少,但核心就三件事:

  1. 理解架构:LVGL是三层结构,移植只改底层。
  2. 熟悉源码:重点看 lv_hal/、lv_core/ 和 lv_conf.h。
  3. 配置裁剪:根据你的硬件资源,合理设置颜色深度、内存大小和控件开关。