链接:https://docs.lvgl.io/master/
docs:LVGL
LVGL(Light and Versatile Graphics Library)是用于在资源受限的嵌入式
系统上创建图形用户界面(GUI)的开源解决方案。
它提供丰富的控件
和灵活的**事件
系统**来实现动态交互。
该库能高效处理绘制/渲染到各类显示屏的操作,并支持来自多种**输入设备**的输入
,所有功能都可通过中心化的配置进行定制。
概览
章节
- 配置(lv_conf.h)
- 显示屏(lv_display)
- 控件(lv_obj)
- 样式(lv_style)
- 布局(lv_flex, lv_grid)
- 输入设备(lv_indev)
- 事件系统(lv_event)
- 绘制/渲染(lv_draw)
第一章:配置(lv_conf.h)
欢迎开启LVGL之旅~
在嵌入式系统图形用户界面(GUI),LVGL就像一套多功能工具箱,能够从智能手表到工业面板的各种小屏幕上绘制精美的界面。
但与实体工具箱类似,并非所有项目都需要用到全部工具。假设我们正在开发一款配备基础小屏幕
的简易设备,仅需几个按钮和文本显示。
此时我们希望设备尽可能高效运行,同时占用最少的内存和处理器资源。
这正是lv_conf.h
的用武之地!可将lv_conf.h
视为整个LVGL库的"定制清单"或"设置菜单"。
通过这个特殊文件,我们可以精确指定
需要启用的功能(如特定按钮类型或高级绘图效果)以及需要禁用的功能。
通过这种配置,最终程序将针对特定设备进行极致优化,节省宝贵内存并提升运行效率。
什么是lv_conf.h
?
lv_conf.h
是一个标准C头文件(扩展名为.h
),包含大量LVGL配置参数。这些参数在C语言中通常称为"宏
"或"定义",其作用类似于功能开关或数值设定器。
例如某个配置项可能如下:
#define LV_USE_BUTTON 1
这行代码告知LVGL:"我们需要在项目中使用按钮!"其中1
表示启用,0
表示禁用。
配置文件的获取与设置
初次获取LVGL时并不会直接得到lv_conf.h
文件,而是会看到lvgl/lv_conf_template.h
文件。
这相当于包含所有可能选项的蓝图或默认清单。
要为项目配置LVGL,请按以下步骤操作:
复制模板文件:复制
lvgl/lv_conf_template.h
并重命名为lv_conf.h
- 重要提示:将
lv_conf.h
置于lvgl
文件夹同级目录,形成如下项目结构:项目目录/ ├── lvgl/ │ ├── src/ │ └── ... └── lv_conf.h └── main.c └── ...
- 重要提示:将
激活配置内容:打开新建的
lv_conf.h
文件,在文件顶部可见:#if 0 /* 将此值设为"1"以激活配置内容 */
将
0
改为1
以激活文件内所有设置项:#if 1 /* 将此值设为"1"以激活配置内容 */
至此,lv_conf.h
已准备就绪可供定制~
新手必备配置项
我们以构建简易内存优化型GUI(含按钮和文本)为目标,重点讲解几个关键配置项。
1. 屏幕色深(LV_COLOR_DEPTH
)
该参数决定每个像素颜色表示的位数。位数越多色彩越丰富
,但内存占用也越高。
16
:常见于嵌入式显示设备(如RGB565格式),内存占用低于32位32
:适用于高质量显示(如ARGB8888),内存需求较高
对于简易设备,选择16
位可有效节省内存:
// 位于lv_conf.h
/*====================
颜色设置
*====================*/
/** 色深选项:1 (I1), 8 (L8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888) */
#define LV_COLOR_DEPTH 16 // 从默认的32或16修改为16以节省内存
2. LVGL内存分配(LV_MEM_SIZE
)
LVGL需要预分配内存来存储内部变量、绘图缓冲区和控件数据。
LV_MEM_SIZE
定义了为LVGL保留的内存容量。
模板文件中默认值通常为(64 * 1024U)
即64KB。对于小型设备,若功能简单可缩减至32KB或更低:
// 位于lv_conf.h
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
/** 为lv_malloc()预留的可用内存大小(>= 2kB) */
#define LV_MEM_SIZE (32 * 1024U) // 从64KB调整为32KB以节省内存
#endif
3. 控件启用/禁用(LV_USE_WIDGET_NAME
)
LVGL提供多种预制组件(称为"控件"),包括按钮、滑块、图表、日历等。
对于简易设备,仅需启用按钮和基础文本(标签)控件即可显著减小程序体积。
在lv_conf.h
中找到如下段落进行配置:
// 位于lv_conf.h
/*==================
* 控件
*================*/
#define LV_USE_ANIMIMG 0 // 禁用
#define LV_USE_ARC 0 // 禁用
#define LV_USE_BAR 0 // 禁用
#define LV_USE_BUTTON 1 // 启用!
#define LV_USE_BUTTONMATRIX 0 // 禁用
#define LV_USE_CALENDAR 0 // 禁用(模板中为1)
// ... 更多控件配置 ...
#define LV_USE_LABEL 1 // 启用!
将未使用的控件设为0
后,编译器会完全忽略相关代码,显著减小最终程序体积。
4. 日志与调试(LV_USE_LOG
, LV_USE_PERF_MONITOR
, LV_USE_SYSMON
)
这些参数控制LVGL是否输出调试日志或性能监控信息。
开发阶段开启有助于调试,但正式发布时应关闭以优化代码体积和性能:
// 位于lv_conf.h
/*-------------
* 日志系统
*-----------*/
/** 启用日志模块 */
#define LV_USE_LOG 0 // 从1改为0以优化发布版本
// ... 文件后续部分 ...
/** 1: 启用系统监控组件 */
#define LV_USE_SYSMON 0 // 从1改为0以优化发布版本
#if LV_USE_SYSMON
// ... 相关设置 ...
/** 1: 显示CPU使用率和FPS计数 */
#define LV_USE_PERF_MONITOR 0 // 从1改为0
#endif
⭕lv_conf.h
–条件编译
当编译LVGL项目(即将C代码转换为设备可执行程序)时,编译器会读取lv_conf.h
配置。
每个#define
指令相当于给编译器的特殊指令。当某功能被启用(如#define LV_USE_BUTTON 1
),编译器会将LVGL库中所有按钮相关代码包含进最终程序。
若功能被禁用(如#define LV_USE_CALENDAR 0
),编译器则完全跳过日历相关代码,相当于这些代码从未存在!
此过程称为**条件编译**,是精确适配硬件能力和项目需求的强大工具。
其工作原理可简化为:
在LVGL源代码中,可见包围代码块的#if
和#endif
指令。例如按钮功能的实现代码:
// 在LVGL源文件中(如lv_button.c)
#include "lv_conf.h" // 包含定制化配置
#if LV_USE_BUTTON // 仅当LV_USE_BUTTON为1时编译本段
// ...
// 按钮功能相关代码
// ...
void lv_button_create(lv_obj_t *parent)
{
// 创建按钮的代码
}
// ...
#endif // LV_USE_BUTTON结束
同理,被禁用功能的代码段:
// 在LVGL源文件中(如lv_calendar.c)
#include "lv_conf.h"
#if LV_USE_CALENDAR // 当LV_USE_CALENDAR为0时跳过本段
// ...
// 日历控件相关代码
// ...
void lv_calendar_create(lv_obj_t *parent)
{
// 创建日历的代码
}
// ...
#endif
这种精妙的机制使LVGL具备极强的适应性,既能运行于资源有限的微型控制器,也能驾驭功能齐全的智能设备。
总结
我们已完成LVGL定制的第一步!理解lv_conf.h
至关重要,因为它直接影响项目的体积、性能和可用功能。
通过精心选择组件
和调整内存参数
,可以确保LVGL完美适配目标嵌入式系统——无论是简易显示器还是功能丰富的智能设备。
现在我们已经掌握如何配置LVGL本身,接下来让我们进入实际显示环节
!
第二章:显示屏(lv_display)
欢迎回来!
在第一章:配置(lv_conf.h)中,我们学习了如何为特定设备裁剪优化LVGL。可将lv_conf.h
视为为工具箱选择合适工具
的过程。但若没有可供绘制的画布,再好的工具箱又有何用?
**显示模块(lv_display
)**正是为此而生!
设想我们已将物理屏幕连接到微控制器,LVGL需要全面了解该屏幕的信息:尺寸(分辨率)、色彩模式,以及最关键的部分——如何将LVGL内存中的精美画面传输至物理屏幕实现可视化。
lv_display
对象如同物理屏幕的数字化身,负责处理LVGL绘图引擎与显示硬件之间的所有通信。若无此模块,LVGL将无从知晓绘制位置及可视化方法
什么是lv_display
?
lv_display
(代码中以lv_display_t
表示)是LVGL与物理屏幕交互的核心模块,主要管理:
- 分辨率:屏幕像素尺寸(如320x240像素)
- 色深:每个色彩使用的位数(如16位RGB565)
- 绘图缓冲区:微控制器内存中的专用区域,作为LVGL绘制图形的临时画布
- 刷新回调函数:开发者编写的关键函数,用于将缓冲区像素数据传送至物理显示硬件
LVGL支持多屏显示,但本章聚焦单屏基础配置。
显示屏初始设置
我们以320x240分辨率、16位色的常见屏幕为例,详解配置流程:
1. 创建显示对象
首先需向LVGL声明显示设备的存在:
#include "lvgl.h" // 必须包含LVGL主头文件
// 定义显示屏分辨率
#define MY_DISP_HOR_RES 320
#define MY_DISP_VER_RES 240
void setup_display() {
// 1. 创建显示对象
lv_display_t * my_display = lv_display_create(MY_DISP_HOR_RES, MY_DISP_VER_RES);
// ... 后续配置将在此添加
}
lv_display_create()
函数向LVGL声明了显示设备的分辨率,返回的my_display
指针用于后续配置
2.配置绘图缓冲区
LVGL采用"预渲染-传输"机制,需在微控制器RAM中开辟绘图缓冲区:
- 单缓冲区:LVGL完成绘制后等待传输,可能导致低刷新率下的画面撕裂
- 双缓冲区:支持交替渲染与传输,建议RAM充足时采用
以下示例配置占屏幕1/10大小的单缓冲区:
#define LV_COLOR_DEPTH 16 // 需与lv_conf.h设置一致
#define MY_DISP_HOR_RES 320
#define MY_DISP_VER_RES 240
// 根据色深计算每像素字节数
#define BYTES_PER_PIXEL (LV_COLOR_DEPTH / 8)
// 静态内存中声明缓冲区(持久化)
static lv_color_t draw_buffer[MY_DISP_HOR_RES * MY_DISP_VER_RES / 10];
void setup_display() {
lv_display_t * my_display = lv_display_create(MY_DISP_HOR_RES, MY_DISP_VER_RES);
// 2. 绑定绘图缓冲区
lv_display_set_buffers(my_display, draw_buffer, NULL, sizeof(draw_buffer), LV_DISPLAY_RENDER_MODE_PARTIAL);
}
LV_DISPLAY_RENDER_MODE_PARTIAL
启用局部渲染模式,仅更新变化区域以优化性能
3. 实现刷新回调函数
此为核心环节,需开发者根据显示硬件编写像素传输逻辑:
// 实际屏幕写入函数(需根据硬件实现)
void my_put_pixel_on_actual_screen(int32_t x, int32_t y, lv_color_t color) {
// 示例:SPI屏可能需要通过协议发送坐标数据
// 具体实现取决于显示控制器与通信接口
}
// 3. 自定义刷新回调函数
void my_display_flush_callback(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map) {
lv_color_t * buf = (lv_color_t *)px_map; // 像素数据转换
// 遍历待刷新区域
for (int32_t y = area->y1; y <= area->y2; y++) {
for (int32_t x = area->x1; x <= area->x2; x++) {
my_put_pixel_on_actual_screen(x, y, *buf);
buf++; // 移动至下一像素
}
}
// 关键!通知LVGL刷新完成
lv_display_flush_ready(disp);
}
area
参数指定需更新的屏幕矩形区域- 完成像素传输后必须调用
lv_display_flush_ready()
释放缓冲区
4. 注册回调函数
将自定义回调绑定至显示对象:
void setup_display()
{
lv_display_t * my_display = lv_display_create(MY_DISP_HOR_RES, MY_DISP_VER_RES);
lv_display_set_buffers(my_display, draw_buffer, NULL, sizeof(draw_buffer), LV_DISPLAY_RENDER_MODE_PARTIAL);
// 4. 注册刷新回调
lv_display_set_flush_cb(my_display, my_display_flush_callback);
}
至此,LVGL已完成显示系统的初始化,可进行图形渲染。
默认显示对象
首个通过lv_display_create()
创建的显示对象会自动成为默认显示设备。
多数LVGL控件创建函数将自动关联此默认显示。
多屏系统可通过lv_display_set_default()
切换当前默认显示。
显示模块工作原理
显示模块的运作流程可通过以下时序图理解:
内部实现解析:
lv_display_create()
会在内存中分配lv_display_t
结构体,包含分辨率、缓冲区指针及回调函数等关键信息。刷新定时器(
refr_timer
)会周期性地触发画面更新。
总结
本章完成了LVGL显示系统的配置,重点包括:
显示对象
的创建与属性设置绘图缓冲区
的内存管理硬件相关的刷新
回调实现- LVGL
渲染管线
的运作原理
至此,我们已搭建起GUI系统的显示基础,下一章将深入探讨如何构建交互界面元素。