2.1. LVGL移植¶
本文档仅介绍LVGL移植到D211平台的流程和步骤,没有涉及到基于LVGL应用的开发
2.1.1. LVGL简介¶

LVGL(轻量级和通用图形库)是一个免费和开源的图形库,它提供了创建嵌入式 GUI 所需的一切,具有易于使用的图形元素,美丽的视觉效果和低内存占用
2.1.1.1. 主要特性¶
丰富且强大的模块化图形组件:按钮、图标、列表、互动条、图片等
先进的图形界面:动画、抗锯齿、透明度、平滑滚动等效果
支持不同的输入设备包括键盘,鼠标,触摸屏,编码器等
UTF-8编码支持多语言
多显示器支持,可以同时使用多个TFT或单色显示
可以通过类 CSS的方式来设计、布局图形界面
不限制芯片类型、硬件,可在各种微控制器或显示器上使用LVGL
配置可裁剪(最低资源占用:64 kB Flash,16 kB RAM)
支持操作系统、外部存储和GPU,但都不是硬性要求
即使单缓冲区(frame buffer)也能实现高级图形效果
不需要嵌入式硬件环境在PC模拟器就可以调试GUI
支持 Micropython 编程
有用于快速GUI设计的教程、示例、主题
详尽的文档以及 API 参考手册,可线上查阅或可下载为 PDF 格式
在 MIT 许可下免费和开源
2.1.1.2. 配置要求¶
16、32或64位微控制器或处理器
最低 16 MHz 时钟频率
Flash/ROM: >64 kB(建议 180 kB)
RAM: 8 kB(建议 24 kB)
显示缓冲区: >水平分辨率像素(建议为1/10屏幕大小)
支持C99编程
具备基本的C或C++知识
2.1.2. LVGL移植¶
软件模块的移植一般包括下载代码、平台配置、应用开发、编译等几个步骤,本章节也从这几个方面进行描述
2.1.2.1. 获取代码¶
一个LVGL应用项目工程的代码分为两部分,一部分为LVGL图形库代码lvgl目录,一部分为驱动支持部分代码lv_driver目录。由于LVGL的图形库非常轻量级,所以这部分是以源码的形式集成到应用项目中。两部分都可以从GitHub获取最新的源码。
lvgl源码获取:https://github.com/lvgl/lvgl
lvgl_driver源码获取:https://github.com/lvgl/lv_drivers
注解
本文档中移植示例中使用的lvgl版本和lv_driver版本均为v8.2版本
创建一个名称为lvgl的工程目录,把所从GitHub上所获取的两部分代码放入工程目录,结构如下:
lvgl
|-- lv_drivers --> 驱动支持总目录
| |-- CMakeLists.txt
| |-- display
| |-- docs
| |-- gtkdrv
| |-- indev
| |-- library.json
| |-- LICENSE
| |-- lv_drivers.mk
| |-- lv_drv_conf_template.h --> 驱动配置模板
| |-- README.md
| |-- sdl
| |-- wayland
| |-- win32drv
| |-- win_drv.c
| `-- win_drv.h
`-- lvgl --> 图形库总目录
|-- CMakeLists.txt
|-- component.mk
|-- demos --> 应用示例
|-- docs
|-- env_support
|-- examples
|-- idf_component.yml
|-- Kconfig
|-- library.json
|-- library.properties
|-- LICENCE.txt
|-- lv_conf_template.h --> 图形库配置模板
|-- lvgl.h
|-- lvgl.mk
|-- README.md
|-- README_zh.md
|-- SConscript
|-- scripts
|-- src --> 源码目录
`-- tests
2.1.2.2. 配置代码¶
lvgl配置
基于模板创建配置文件:
cp lvgl/lv_conf_template.h ./lv_conf.h
修改lv_conf.h如下:
--- a/lv_conf.h
+++ b/lv_conf.h
@@ -12,7 +12,7 @@
*/
/* clang-format off */
-#if 0 /*Set it to "1" to enable content*/
+#if 1 /*Set it to "1" to enable content*/ --> 使能配置文件内容
#ifndef LV_CONF_H
#define LV_CONF_H
@@ -85,10 +85,10 @@
/*Use a custom tick source that tells the elapsed time in milliseconds.
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
-#define LV_TICK_CUSTOM 0
+#define LV_TICK_CUSTOM 1 --> 使能客户custom_tick_get函数
#if LV_TICK_CUSTOM
- #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
- #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
+ #define LV_TICK_CUSTOM_INCLUDE <stdint.h> /*Header for the system time function*/
+ #define LV_TICK_CUSTOM_SYS_TIME_EXPR (custom_tick_get()) /*Expression evaluating to current system time in ms*/
#endif /*LV_TICK_CUSTOM*/
/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
@@ -705,7 +705,7 @@
====================*/
/*Show some widget. It might be required to increase `LV_MEM_SIZE` */
-#define LV_USE_DEMO_WIDGETS 0
+#define LV_USE_DEMO_WIDGETS 1 --> 打开widgets示例demo
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 0
#endif
lv_drivers配置
基于模板创建配置文件:
cp lv_drivers/lv_drv_conf_template.h ./lv_drv_conf.h
修改lv_drv_conf.h如下:
--- a/lv_drv_conf.h
+++ b/lv_drv_conf.h
@@ -8,7 +8,7 @@
*/
/* clang-format off */
-#if 0 /*Set it to "1" to enable the content*/
+#if 1 /*Set it to "1" to enable the content*/ --> 使能配置文件内容
#ifndef LV_DRV_CONF_H
#define LV_DRV_CONF_H
@@ -316,7 +316,7 @@
* Linux frame buffer device (/dev/fbx)
*-----------------------------------------*/
#ifndef USE_FBDEV
-# define USE_FBDEV 0
+# define USE_FBDEV 1 --> 使用/dev/fb设备节点
#endif
#if USE_FBDEV
@@ -439,7 +439,7 @@
* Mouse or touchpad as evdev interface (for Linux based systems)
*------------------------------------------------*/
#ifndef USE_EVDEV
-# define USE_EVDEV 0
+# define USE_EVDEV 1 --> 使用/dev/fb设备节点
#endif
#ifndef USE_BSD_EVDEV
2.1.2.3. 编写应用¶
应用程序主题main.c如下:
#include "lvgl/lvgl.h"
#include "lvgl/demos/lv_demos.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_drivers/indev/evdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#define DISP_BUF_SIZE (128 * 1024)
int main(void)
{
/*LittlevGL init*/
lv_init();
/*Linux frame buffer device init*/
fbdev_init();
/*A small buffer for LittlevGL to draw the screen's content*/
static lv_color_t buf[DISP_BUF_SIZE];
/*Initialize a descriptor for the buffer*/
static lv_disp_draw_buf_t disp_buf;
lv_disp_draw_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);
/*Initialize and register a display driver*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.flush_cb = fbdev_flush;
disp_drv.hor_res = 1024;
disp_drv.ver_res = 600;
lv_disp_drv_register(&disp_drv);
evdev_init();
static lv_indev_drv_t indev_drv_1;
lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/
indev_drv_1.type = LV_INDEV_TYPE_POINTER;
/*This function will be called periodically (by the library) to get the mouse position and state*/
indev_drv_1.read_cb = evdev_read;
lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1);
/*Create a Demo*/
lv_demo_widgets();
/*Handle LitlevGL tasks (tickless mode)*/
while(1) {
lv_timer_handler();
usleep(5000);
}
return 0;
}
/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{
static uint64_t start_ms = 0;
if(start_ms == 0) {
struct timeval tv_start;
gettimeofday(&tv_start, NULL);
start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
}
struct timeval tv_now;
gettimeofday(&tv_now, NULL);
uint64_t now_ms;
now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;
uint32_t time_ms = now_ms - start_ms;
return time_ms;
}
注解
本章节所展示的应用基于lvgl自带的demo程序:lv_port_linux_frame_buffer,可从GitHub获取:https://github.com/lvgl/lv_port_linux_frame_buffer
2.1.2.4. 编译运行¶
编译脚本Makefile内容如下:
CC = arm-linux-gnueabihf-gcc
LVGL_DIR_NAME ?= lvgl
LVGL_DIR ?= ${shell pwd}
CFLAGS ?= -O3 -g0 -I$(LVGL_DIR)/ -Wall -Wshadow -Wundef -Wmissing-prototypes -Wno-discarded-qualifiers -Wall -Wextra -Wno-unused-function -Wno-error=strict-prototypes -Wpointer-arith -fno-strict-aliasing -Wno-error=cpp -Wuninitialized -Wmaybe-uninitialized -Wno-unused-parameter -Wno-missing-field-initializers -Wtype-limits -Wsizeof-pointer-memaccess -Wno-format-nonliteral -Wno-cast-qual -Wunreachable-code -Wno-switch-default -Wreturn-type -Wmultichar -Wformat-security -Wno-ignored-qualifiers -Wno-error=pedantic -Wno-sign-compare -Wno-error=missing-prototypes -Wdouble-promotion -Wclobbered -Wdeprecated -Wempty-body -Wtype-limits -Wstack-usage=2048 -Wno-unused-value -Wno-unused-parameter -Wno-missing-field-initializers -Wuninitialized -Wmaybe-uninitialized -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wtype-limits -Wsizeof-pointer-memaccess -Wno-format-nonliteral -Wpointer-arith -Wno-cast-qual -Wmissing-prototypes -Wunreachable-code -Wno-switch-default -Wreturn-type -Wmultichar -Wno-discarded-qualifiers -Wformat-security -Wno-ignored-qualifiers -Wno-sign-compare
LDFLAGS ?= -lm
BIN = demo
#Collect the files to compile
MAINSRC = ./main.c
include $(LVGL_DIR)/lvgl/lvgl.mk
include $(LVGL_DIR)/lv_drivers/lv_drivers.mk
OBJEXT ?= .o
AOBJS = $(ASRCS:.S=$(OBJEXT))
COBJS = $(CSRCS:.c=$(OBJEXT))
MAINOBJ = $(MAINSRC:.c=$(OBJEXT))
SRCS = $(ASRCS) $(CSRCS) $(MAINSRC)
OBJS = $(AOBJS) $(COBJS)
## MAINOBJ -> OBJFILES
all: default
%.o: %.c
@$(CC) $(CFLAGS) -std=c99 -c $< -o $@
@echo "CC $<"
default: $(AOBJS) $(COBJS) $(MAINOBJ)
$(CC) -o $(BIN) $(MAINOBJ) $(AOBJS) $(COBJS) $(LDFLAGS)
clean:
rm -f $(BIN) $(AOBJS) $(COBJS) $(MAINOBJ)
在应用工程目录下运行makem命令后, 生成可执行程序demo文件,把二进制文件demo通过adb推送到开发板,运行:./demo,程序运行效果如下:
