第1部分:启程篇 - 从零构建第一个LVGL应用
文章目录
第1章:欢迎来到LVGL的世界
1.1 什么是LVGL?
LVGL(Light and Versatile Graphics Library)是一个开源的嵌入式图形库,用于为任何微控制器、显示器或触摸屏创建漂亮的GUI。你可以将其理解为一个为资源受限的嵌入式设备打造的“迷你版Qt或Android UI框架”。
其核心目标是在有限的资源(如低内存、低速CPU)下,提供丰富、流畅的用户界面体验。
1.2 为什么选择LVGL?
选择LVGL,意味着你选择了一个兼具强大功能与高度可移植性的解决方案。其优势可以通过以下架构图一目了然:
具体来说,其优势体现在:
- 资源高效:经过精心优化,最低配置下仅需 ~64 kB Flash 和 ~16 kB RAM 即可运行。
- 控件丰富:提供按钮、滑块、图表、列表、动画等40多种控件,满足绝大多数GUI需求。
- 高度可移植:与处理器架构和显示控制器无关,只需提供基础的
display_flush和indev_read函数即可适配新硬件。 - 强大的样式系统:支持类似CSS的样式设置,包括边框、阴影、渐变、变换等,可以轻松创建现代化的界面。
- 活跃的社区:拥有一个非常活跃和乐于助人的社区,问题能够快速得到解答。
1.3 LVGL的“心脏”:事件循环与任务处理器
LVGL并非一个实时操作系统,但它内部实现了一个轻量级的事件驱动架构。其核心运作机制可以用以下公式和流程图描述:
核心的心跳来源于一个周期性中断(如SysTick),它为所有动画和任务提供时间基准:
系统状态 = f ( 时间 t , 用户输入 I , 事件 E ) \text{系统状态} = f(\text{时间}t, \text{用户输入}I, \text{事件}E) 系统状态=f(时间t,用户输入I,事件E)
在每个主循环中,lv_timer_handler() 函数会检查自上次调用后经过的时间 Δt,并更新所有需要更新的部分。
理解这个工作流程,对于后续的开发和调试至关重要。
第2章:实战:打造你的第一个LVGL“Hello World”
理论说再多,不如亲手运行一遍。本章我们将使用VS Code + PlatformIO这一强大的组合,在PC模拟器上快速搭建LVGL开发环境。这是学习和原型开发的最快路径。
2.1 环境准备:安装VS Code与PlatformIO
-
安装Visual Studio Code
- 访问 VS Code官网,下载并安装对应你操作系统的版本。
-
安装PlatformIO插件
- 打开VS Code,点击左侧活动栏的“扩展”图标(或按
Ctrl+Shift+X)。 - 在搜索框中输入
PlatformIO IDE。 - 找到由 PlatformIO 发布的扩展,点击“安装”。
- 打开VS Code,点击左侧活动栏的“扩展”图标(或按
2.2 创建LVGL模拟器项目
- 在VS Code中,通过
Ctrl+Shift+P(或F1)打开命令面板。 - 输入
PlatformIO: New Project并选择。 - 在弹出的窗口中配置项目:
- Name:
my_first_lvgl - Board: 在搜索框中输入
native,选择 Native Development Platform。这是我们使用PC模拟器的关键。 - Framework: 选择
SDL。SDL是一个多媒体库,LVGL模拟器用它来创建窗口和处理输入。 - Location: 选择你希望保存项目的路径。
- Name:
- 点击 Finish。PlatformIO会自动创建项目结构并下载必要的依赖。
2.3 编写代码:让“Hello World”显示出来
PlatformIO创建的项目会有一个 src/main.cpp 文件。我们用以下代码完全替换其内容:
/**
* LVGL "Hello World" 示例程序
* 运行于PC模拟器(SDL)
*/
#include <lvgl.h>
#include <lv_drivers/display/monitor.h>
#include <lv_drivers/indev/mouse.h>
#include <stdlib.h>
/* 为LVGL设置一个心跳周期,例如1ms */
#define LV_TICK_PERIOD_MS 1
/* 声明函数 */
static void lv_tick_task(void *arg);
int main(void) {
/********************
* 1. LVGL初始化
********************/
lv_init();
/********************
* 2. 显示和输入设备初始化
* 对于模拟器,我们使用Monitor驱动
********************/
/* 初始化显示驱动:monitor */
monitor_init();
/* 创建了一个默认的显示缓冲区(screen-sized) */
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[LV_HOR_RES_MAX * 120]; /* 声明一个缓冲区 */
lv_disp_draw_buf_init(&draw_buf, buf1, NULL, LV_HOR_RES_MAX * 120);
/* 创建显示驱动 */
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv); /* 使用默认值初始化 */
disp_drv.draw_buf = &draw_buf; /* 设置绘制缓冲区 */
disp_drv.flush_cb = monitor_flush; /* 设置刷新回调函数,将缓冲区内容刷到屏幕上 */
disp_drv.hor_res = LV_HOR_RES_MAX; /* 设置水平分辨率 */
disp_drv.ver_res = LV_VER_RES_MAX; /* 设置垂直分辨率 */
lv_disp_drv_register(&disp_drv); /* 最终注册驱动 */
/* 初始化输入设备驱动:mouse */
mouse_init();
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER; /* 鼠标是指针设备 */
indev_drv.read_cb = mouse_read; /* 设置读取回调函数 */
lv_indev_drv_register(&indev_drv); /* 最终注册驱动 */
/********************
* 3. 创建用户界面
********************/
/* 获取当前活动屏幕(默认只有一个屏幕) */
lv_obj_t * scr = lv_scr_act();
/* 创建一个标签(Label)对象,父对象是屏幕 */
lv_obj_t * label = lv_label_create(scr);
/* 设置标签的文本 */
lv_label_set_text(label, "Hello, LVGL!");
/* 将标签在屏幕上居中 */
lv_obj_center(label);
/********************
* 4. 设置心跳和主循环
********************/
/* 创建一个定时器来模拟系统心跳,它告诉LVGL时间流逝 */
SDL_CreateThread(lv_tick_task, "tick", NULL);
/* 主循环 */
while(1) {
lv_timer_handler(); /* 让LVGL引擎工作:处理定时器、重绘、输入等 */
SDL_Delay(5); /* 短暂延时,避免占用100% CPU */
}
return 0;
}
/**
* 一个独立的任务,用于每毫秒调用lv_tick_inc(1),为LVGL提供心跳
*/
static void lv_tick_task(void *arg) {
(void)arg; /* 避免未使用参数警告 */
while(1) {
SDL_Delay(LV_TICK_PERIOD_MS); /* 休眠1毫秒 */
lv_tick_inc(LV_TICK_PERIOD_MS); /* 告诉LVGL已经过去了1毫秒 */
}
}
2.4 配置与运行
-
修改PlatformIO配置:打开项目根目录下的
platformio.ini文件,确保其内容如下。这确保了LVGL和SDL驱动被正确链接。[env:native] platform = native lib_deps = lvgl/lvgl@^8.3 lvgl/lv_drivers@^1.0 build_flags = -DLV_LVGL_H_INCLUDE_SIMPLE -DLV_CONF_INCLUDE_SIMPLE -
构建并运行:
- 点击VS Code底部状态栏的 PlatformIO: Build(对勾图标)来编译项目。
- 编译成功后,点击 PlatformIO: Upload(右箭头图标)来运行程序。由于是模拟器,“Upload”实际上就是执行。
如果一切顺利,你将看到一个窗口弹出,屏幕上清晰地显示着 “Hello, LVGL!”。你可以用鼠标在窗口内点击和拖动。
2.5 代码深潜:理解“核心三要素”
让我们回过头来剖析代码中的关键部分,它们对应着LVGL运行的“核心三要素”:
-
LVGL库本身 (
lv_init())- 这是所有故事的开始。它初始化了LVGL的内部数据结构,为后续所有操作做好准备。
-
显示驱动 (
disp_drv)- 角色:负责“画图”。
- 核心部件:
draw_buf:绘制缓冲区。LVGL先在内存中的这块区域绘制好界面。flush_cb:刷新回调函数。当缓冲区内容需要更新到屏幕上时,LVGL会调用此函数。在我们的例子中,monitor_flush函数将缓冲区内容复制到SDL窗口。
-
输入设备驱动 (
indev_drv)- 角色:负责“感知”。
- 核心部件:
read_cb:读取回调函数。LVGL周期性地调用此函数来获取输入设备(如触摸屏、鼠标、按键)的状态。在我们的例子中,mouse_read函数从SDL获取鼠标的位置和按键状态。
心跳 (lv_tick_inc) 与 任务处理器 (lv_timer_handler) 是让这个系统运转起来的“发动机”和“传动轴”。它们必须在你程序的主循环中被正确调用,正如我们在代码和流程图中展示的那样。
本章小结与挑战
恭喜你!你已经成功地迈出了LVGL学习的第一步。你现在已经知道:
- LVGL是什么以及它的优势。
- 如何在PC上使用VS Code和PlatformIO搭建LVGL模拟器开发环境。
- 理解并实现了LVGL运行的“核心三要素”:库初始化、显示驱动、输入驱动。
- 理解了心跳和任务处理器的作用。
- 创建并运行了第一个显示“Hello World”的LVGL程序。
小挑战(动手试一试):
- 修改文本:将
"Hello, LVGL!"改为其他你喜欢的文字,重新编译运行。 - 改变位置:注释掉
lv_obj_center(label);这行,然后手动设置标签的坐标,例如lv_obj_set_pos(label, 50, 100);。 - 观察循环:尝试在主循环中增加
SDL_Delay的值(如改为50ms),观察界面响应是否会变“卡顿”,从而理解lv_timer_handler调用频率对流畅度的影响。
在下一篇文章中,我们将深入探讨LVGL的对象模型和样式系统,开始创建更复杂、更漂亮的界面元素。
备注:本文档中的代码和配置基于LVGL v8.3.x版本。随着版本更新,API可能会有细微变化,请以 LVGL官方文档 为准。
研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)
3116

被折叠的 条评论
为什么被折叠?



