一、引言
在当今嵌入式系统与图形界面开发的广阔领域中,轻量级图形库 LVGL(Light and Versatile Graphics Library)恰似一颗璀璨耀眼的明星,正日益受到开发者们的热烈推崇与追逐。它以小巧精致之姿、高效卓越之能以及丰富多元之功能,为各类设备赋予了强大无比的图形显示实力。本文将深入探寻 LVGL 的显示原理,同时详细介绍如何运用 C++进行移植,引领开发者踏上 LVGL 的精彩征程。
二、LVGL 显示原理
-
分层架构
- LVGL 采用了极为精妙的分层架构设计,主要涵盖以下几个层次:
- 驱动层:此层与底层硬件紧密交互,承担着控制显示屏、读取输入设备等重要任务。通常情况下,这一层需要依据具体的硬件平台进行精心适配。
- 核心层:这里蕴含着 LVGL 的核心功能,诸如对象管理、事件处理以及动画引擎等。作为 LVGL 的核心部分,它提供了极为丰富的图形绘制与交互功能。
- 应用层:开发者在这一层精心构建属于自己的图形界面,通过调用 LVGL 的 API 来创建诸如按钮、标签、图表等各式各样的对象。
- LVGL 采用了极为精妙的分层架构设计,主要涵盖以下几个层次:
-
绘制机制
- LVGL 的绘制机制基于“脏矩形”(Dirty Rectangle)技术。当某个对象的状态发生变化时,LVGL 仅仅重新绘制该对象所在的矩形区域,而非整个屏幕。如此一来,在处理复杂界面时,能够大幅提高绘制效率。
-
事件处理
- LVGL 借助事件驱动的方式来处理用户输入和系统事件。当用户触摸屏幕、按下按钮或者发生其他事件时,LVGL 会将相应的事件传递给应用程序进行处理。开发者可以通过注册事件回调函数来响应各种丰富多彩的事件。
三、LVGL 运行机制
关于 LVGL 在 main.c 中的初始化
在 LVGL 的程序运行入口 main.c 中,首先会调用 lv_init()
函数来初始化 LVGL(Light and Versatile Graphics Library)图形库。此函数执行一系列重要操作,包括设置全局变量、初始化内部数据结构以及加载配置等,从而为后续对 LVGL 的使用做好充分准备。
硬件抽象层初始化
接着,调用 hal_init()
函数以初始化 LVGL 所需的硬件抽象层。该函数会依据具体的硬件平台,进行显示屏的初始化、输入设备的配置以及设置系统时钟(tick)等操作。这样能够确保 LVGL 与底层硬件之间进行正确的交互。
以下是 main.c 的代码示例:
int main(int argc, char **argv)
{
(void)argc; /*Unused*/
(void)argv; /*Unused*/
/*Initialize LVGL*/
lv_init();
/*Initialize the HAL (display, input devices, tick) for LVGL*/
hal_init();
/*处理定时器事件:检查和执行已注册的定时器回调函数,实现定时任务的触发。例如,可以用于动画的更新、周期性的任务执行等。
处理动画更新:如果有正在运行的动画,这个函数会更新动画的状态,确保动画的平滑过渡。
分发用户输入事件:如果有用户输入(如触摸屏幕、按下按钮等),将这些事件分发给相应的对象进行处理。 */
while (1)
{
/* Periodically call the lv_task handler.
* It could be done in a timer interrupt or an OS task too.*/
lv_timer_handler();
usleep(5 * 1000);
}
return 0;
}
使用 lvgl 绘制一个按钮的官网样例代码如下:
lv_obj_t *srceen = lv_scr_act();
lv_obj_t * btn = lv_btn_create(srceen);
lv_obj_set_pos(btn, 10, 10);
lv_obj_set_size(btn, 120, 50);
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_center(label);
绘制步骤如下:
- 获取当前显示屏幕对象句柄。
- 调用 lvgl 函数将对象绘制到屏幕上。
- 设置组件之间的相对位置,左上角为原点。
- 还可以为按钮添加各种事件以及处理函数处理。
由于 lvgl 组件编码难度较高,并且效率也比较低下,在实际开发中从 lvgl 的函数中去实现一个组件的代码量和时间比较多,因此需要进行一个有效的封装。在参考现有的开源仓库中,找到了一个比较完善的基于 C++封装项目 lvglpp。将该项目引入代码中,就能实现一套类似于 QWidget 界面开发方式。
项目源码地址获取:
git clone https://github.com/vpaeder/lvglpp.git
三、引入 lvglpp 到 lvgl
将 lvglpp 放在项目同 lvgl 下面,如下图所示:
在项目主的 CMakeLists.txt 里面引入 lvglpp 编译模块,如下图所示:
基于 lvglpp 实现 led 组件,示例代码如下:
Object root = Object(lv_scr_act(), false);
static auto led1 = Led(root);
led1.align(LV_ALIGN_CENTER, -80, 0);
led1.off();
/*Copy the previous LED and set a brightness*/
static auto led2 = Led(root);
led2.align(LV_ALIGN_CENTER, 0, 0);
led2.set_brightness(150);
led2.set_color(palette::main(Color::Red));
/*Copy the previous LED and switch it ON*/
static auto led3 = Led(root);
led3.align(LV_ALIGN_CENTER, 80, 0);
led3.on();
auto event_cb = [](Event &e) {
if(led3.get_brightness() == LV_LED_BRIGHT_MIN) {
led3.on();
} else {
led3.off();
}
};
led3.add_event_cb(event_cb, LV_EVENT_CLICKED);
其中创建了 3 个 led 组件,并且给 led3 绑定了一个点击函数,用来改变 led 灯的状态。
总结
lvgl 是一个比较适合嵌入式设备的图形框架。通过封装,可以将复杂的底层函数调用转化为更加直观、易用的接口,极大地提高了开发效率。开发者不再需要深入钻研底层代码的实现细节,而是能够以更简洁的方式构建出精美的图形界面。同时,封装也降低了开发难度,使得即使是经验相对不足的开发者也能轻松上手,快速开发出高质量的嵌入式图形界面应用。。