LVGL简介
LVGL(Light and Versatile Graphics Library)是一款开源的嵌入式图形库,专为资源受限的嵌入式系统设计,支持低内存占用(最低配置要求约 16KB RAM 和 64KB Flash)和高性能渲染。它提供丰富的UI组件和动画效果,适用于智能手表、工业HMI、物联网设备等场景。
核心特点
- 跨平台:支持裸机运行或与RTOS(如FreeRTOS、Zephyr)集成,可移植到多种MCU(如STM32、ESP32)或Linux系统。
- 多语言支持:内置UTF-8编码,支持中文等复杂文本渲染。
- 硬件加速:可配置GPU加速(如STM32的Chrom-ART)。
- 丰富的组件:按钮、图表、列表、滑块等30+控件,支持自定义样式和事件回调。
- 低依赖:仅需基础的显示驱动(帧缓冲区或绘制API)和输入设备驱动(触摸屏、编码器)。
基础架构
LVGL采用分层设计:
- 硬件层:显示驱动(如SPI屏幕)、输入驱动(如触摸IC)。
- 核心层:任务调度、内存管理、对象系统(基于C语言面向对象设计)。
- 组件层:预置控件和动画效果。
- 应用层:用户界面逻辑。示例代码(初始化)
应用场景
- 智能家居:触摸屏控制面板。
- 穿戴设备:动态表盘界面。
- 工业控制:实时数据监控仪表盘。
LVGL通过模块化设计平衡性能与灵活性,适合快速开发轻量级嵌入式GUI。
一、移植LVGL的前期准备
第一步:STM32 LVGL版本选择
由于我使用的是STM32F429,推荐使用 LVGL v8.3.11,因为这个版本在性能、兼容性和 bug 修复方面较为稳定,具体原因如下:
STM32F429推荐使用 LVGL v8.3.11 的原因
-
官方稳定版本:
- LVGL v8.3.11 是 LVGL v8.3 系列的最终修复版,修复了之前版本的一些 bug,提高了稳定性,资源可控。
- 运行 demo 约 64KB heap,1~2KB 栈,无残缺模块,CubeMX / ST 官方示例可用,文档齐全。
-
支持 SquareLine Studio:
- SquareLine Studio 是目前最流行的 LVGL UI 设计工具,且最高支持 LVGL v8.3.x。
- 如果你想通过 可视化界面拖拽设计 UI,使用 LVGL v8.3.11 兼容性最佳。
- 我想最主要的原因还是由于NXP公司开发的中文界面GUI设计工具GUI Guider 对于LVGL v8最高只能最高支持 LVGL v8.3.x。
-
支持最新的 LVGL 组件和优化:
- LVGL v8.3 提供了更多的 UI 控件、动画效果,以及更好的输入设备适配(例如触摸屏驱动)。
- 适用于 LCD、触摸屏、SPI DMA 传输 等场景。
STM32其他系列的芯片推荐的 LVGL 版本见下表:
STM32 芯片 | 推荐 LVGL 版本 | 原因 |
---|---|---|
STM32F1/F3/F4(如 F103、F407、F429) | LVGL v8.3 | 功能完善 + 稳定,资源可控 |
STM32H7(带 DCache/高性能 LCD) | LVGL v8.3 或 v9.x | 支持 DMA2D、高分屏等特性 |
资源紧张(Flash < 256K,RAM < 64K) | LVGL v7.11 或更早 | 更轻,适合小屏 |
资源充足 + 追求新特性 | LVGL v9.0+(需谨慎) | 支持 C++、事件系统等,但接口差异大 |
第二步:LVGL 源码下载
1. 官方 GitHub 下载(推荐)
方式 1:GitHub 页面手动下载(我先着重使用手动下载方式,更适合小白)
- 打开 LVGL GitHub 仓库https://github.com/lvgl/lvgl
- 点击选择“v8.3”版本
- 点击 “Code” 按钮
- 选择 “Download ZIP” 下载
下载后,解压缩即可使用。
二、LVGL 源码移植
1.准备一个STM32 Keil基础工程
在移植LVGL前,我们必须先有一个STM32 Keil的基础工程。
2.裁剪LVGL源码
(1)新建lvgl文件夹
解压好刚才下载好的LVGL_v8.3.11源码,如图:
为了管理方便,我把LVGL源码放在我的工程的Middlewares\LVGL_v8.3.11路径下的lvgl文件夹,如图:
(注意: LVGL源码必须放在lvgl文件夹下,因为LVGL头文件使用了相对路径里面有lvgl,所以为了后面改动少点,建议新建lvgl文件夹,把LVGL源码放里面)
(2)精简LVGL源码
看到上面的LVGL是不是感觉有点晕?怎么这么多文件呀?问题不大!不要慌!!!其实里面就3个文件夹、2个头文件是必须需要的!如下图所示:
(3)重命名LVGL相关template文件
打开刚才建立的lvgl文件夹,把 lv_conf_templete.h 重命名为 lv_conf.h
打开刚才建立的lvgl文件夹里面的examples\porting,把里面所有文件的“_template”后缀去掉重命名:
3.显示(和触摸)驱动准备
在 STM32 上移植 LVGL 之前,需要先准备好 显示驱动(LCD) 和 触摸驱动(如果有触摸)。
以下是主要的要点:
(1)确保屏幕的驱动可用,并且可以实现在屏幕上 画点,矩形填充的功能
(2)确保屏幕的触摸驱动可用(如果有触摸功能),并且可以实现获取触摸的XY坐标的功能
LCD显示驱动实现功能如图:
为了使新手小白更深入的学会和理解LVGL如何在STM32上移植,我先选择使用自己编写的触摸屏驱动用于lvgl移植!
我手上的屏幕是2.8寸,240x320分辨率,16位色,RGB565,驱动IC为ILI9341,通信接口SPI,触摸IC是 XPT2046,通信接口SPI。
(注意:实际要根据你自己的驱动文件夹来模仿编写,从而把你的屏幕驱动文件链接到STM32工程里面)
4.添加LCD驱动和LVGL源码到工程
(1)建立显示屏和LVGL源码分组
为了简洁明了的管理LVGL,我就只添加了5个分组:
Middlewares/LVGL_Src | 分组存放LVGL 的所有底层c文件 |
Middlewares/LVGL_Port | 分组存放LVGL 的接口文件, 如显示、触摸屏、键盘等 |
Middlewares/LVGL_Config | 分组存放LVGL 的2个头文件 |
Middlewares/LVGL_Demo | 分组存放LVGL 的官方demo |
Middlewares/LVGL_myGui | 分组存放用户自己的界面代码文件 |
(我的显示屏驱动我放在了BSP分组,没有专门给屏幕建立一个分组)
(2)添加显示屏和LVGL源码到分组
✅添加LVGL Config配置相关文件到LVGL_Config分组(lv_conf.h)
(备注:就是把之前 lv_conf_templete.h重命名为 lv_conf.h 的配置文件添加到LVGL_Config分组,文件在LVGL源码的路径下)
✅添加LVGL Porting接口相关文件到LVGL_Porting分组(lv_port_**.c)
(备注:就是把之前 lv_port_**_templete.c重命名为 lv_port_**.c 的几个接口文件添加到LVGL_Porting分组,文件在LVGL源码的examples\porting路径)
✅添加LVGL Src源码相关文件到LVGL_Src分组,在LVGL源码的src文件夹下,有如下lvgl源码:
重要的事情说三遍!!!
src文件夹里面 、子文件夹 、子子文件夹里面的所有.c文件都需要加入到LVGL_Src分组!
src文件夹里面 、子文件夹 、子子文件夹里面的所有.c文件都需要加入到LVGL_Src分组!
src文件夹里面 、子文件夹 、子子文件夹里面的所有.c文件都需要加入到LVGL_Src分组!
粗略估计需要添加200+ 的.c文件哦!
务必细心添加,不要遗漏了,否则后面编译报错会比较多哦!!
✅添加LVGL 官方Demo源码相关文件到LVGL_Demo分组
(初步移植可以先直接跳过LVGL_Demo分组该步骤!)
LVGL 官方提供了一些Demo例子,用于测试LVGL是否移植成功,如果需要使用到Demo的话就把 demos文件夹和子文件夹下的所有.c文件加入LVGL_Demo分组。
到时需要用到哪个Demo,可以在lv_config.h里面打开对应的宏开关。
5.添加显示屏和LVGL源码头文件路径
打开keil的头文件配置菜单,依次把LVGL源码根路径,src路径、examples路径、porting路径、demos路径添加好,如下:
(备注:由于LVGL源码里面在include头文件时是使用的相对路径,所以,我们不需要说把所有文件夹和其子文件夹路径都添加进去)
后续移植成功LVGL后,使用LVGL官方的Demo,如果错找不到Demo里面相关的函数什么的,那还需要挨个添加Demo的头文件路径:(正常情况是不用挨个添加头文件路径的,因为demo里面也是使用相对路径include头文件的)
三.LVGL 显示驱动接口移植
核心任务:
1.初始化 LCD 硬件(SPI 或并口驱动)
2.实现 flush_cb(LVGL → LCD 数据传输)
3.配置 LVGL 的显示缓冲区
4.注册显示驱动
对于LVGL显示驱动接口的移植,LVGL源码里面提供了移植示例,我们找到我们前面创建的LVGL_Port 分组,里面有3个C文件:
他们分别是 :
显示驱动移植示例代码(lv_port_disp)
文件系统移植示例代码(lv_port_fs)
输入设备驱动移植示例代码(lv_port_indev)
1.lv_port_disp.c(显示驱动移植)
这个文件提供了一个 模板,帮助开发者移植 LCD 屏幕(SPI、RGB、8080并口),主要完成 LVGL → 屏幕的数据传输
第一步:启用接口文件
打开lv_port_disp.c文件,找到文件开头的“#if 0”条件编译,将其改为“#if 1”,这样就启用了该接口文件,然后把#include "lv_port_disp_template.h"改为#include "lv_port_disp.h"如图:
同理,打开打开lv_port_disp.h文件,找到文件开头的“#if 0”条件编译,将其改为“#if 1”,这样就启用了该接口头文件,然后把#include "lvgl/lvgl.h"改为#include "lvgl.h"如图:
第二步:配置屏幕的分辨率
找到lv_port_disp.c文件里面的
MY_DISP_HOR_RES MY_DISP_VER_RES 宏定义,它们分别是设置屏幕水平分辨率和垂直分辨率(我手上的屏幕分辨率是320*240),请根据您的屏幕的实际分辨率填写即可:
第三步:配置LVGL 的显存缓冲区
在 lv_port_disp.c文件的lv_port_disp_init() 里,"Create a buffer for drawing" 指的是 LVGL 的显存缓冲区,用于 存放 LCD 需要刷新的数据。选择合适的缓冲区大小 影响性能和内存使用,需要综合考虑 STM32 的 RAM 资源、屏幕大小、刷新效率。
查看后我们可以清楚看到LVGL提供了缓冲区的三种选择:
要是您对这3种LVGL显示缓存区的具体区别有疑问,请移步查看:
LVGL各种缓冲区分析比较(源码角度)_lvgl源码分析-CSDN博客https://blog.csdn.net/weixin_42914339/article/details/129640435
为了方便移植讲解,我们先使用第一种显示缓存模式:单缓存模式!如图:
然后我们需要配置好显示缓存指针,选择模式几显存就配置模式几的显存指针!
(注意:如果您想要使用其他2种显存缓存模式,记得要在disp_drv.draw_buf = &draw_buf_dsc_1; 处把缓存的指针改成您想使用的缓存指针,如图:)
第四步:显示屏IC驱动初始化
找到lv_port_disp.c文件里面的
disp_init(),在里面添加我们写好的LCD屏幕初始化的接口:
(备注:记得要在lv_port_disp.c文件开头处添加好LCD驱动代码的头文件哦!,如图:
)
第五步:LVGL 显示接口的flush_cb
刷屏回调函数配置(disp_flush)
查看disp_flush()函数里面的示例刷屏方式,我们发现这是画点方式来实现刷屏的,这个方式效率极低,但是确实是万能的!
但是现在是为了更好的理解如何移植LVGL,使用先化繁求简,先使用画点方式刷屏!如图:
至此,我们LVGL显示屏的显示驱动接口就已经移植完成!!!移植已经成功了一大半了!
四、LVGL 输入接口移植(例如:触摸驱动)
先跳过触摸驱动移植,可以先完成显示驱动移植后,验证好LVGL没有问题之后回过来移植触摸驱动!
在 LVGL中,lv_port_indev.c 主要负责某某输入设备的适配和初始化,STM32 的应用场景下,输入设备通常包括:
触摸屏(电容触摸/电阻触摸)
物理按键
编码器(旋钮)
鼠标(调试用)
lv_port_indev.c 作用:
1️⃣ 初始化输入设备(如触摸屏、按键)
2️⃣ 注册到 LVGL(让 LVGL 识别输入设备)
3️⃣ 定期读取输入状态(触摸、按键按下等)
4️⃣ 数据转换(将原始数据转换成 LVGL 需要的格式)
1.lv_port_indev.c输入驱动移植(触摸作为输入为例)
第一步:启用输入接口文件
打开lv_port_disp.c文件,找到文件开头的“#if 0”条件编译,将其改为“#if 1”,这样就启用了该接口文件,然后把#include "lv_port_indev_template.h" 改为 #include "lv_port_indev.h",把#include "../../lvgl.h" 改为 #include "lvgl.h" 如图:
同理,打开打开lv_port_indev.h文件,找到文件开头的“#if 0”条件编译,将其改为“#if 1”,这样就启用了该接口头文件,然后把#include "lvgl/lvgl.h"改为#include "lvgl.h"如图:
第二步:输入设备(触摸)驱动初始化
注意:要在 lv_port_indev.c文件头部添加你自己的屏幕触摸的驱动的头文件哦!以及包含
lv_port_indev.h头文件,(我的屏幕触摸IC是XPT2046)如图:
完成触摸驱动头文件添加后,我们往下翻代码,可以看到LVGL提供的触摸输入移植接口:
然后我们找到lv_port_indev.c文件里面的
touchpad_init(),在里面添加我们写好的显示屏触摸初始化的接口:
第三步:输入获取接口(获取xy)函数配置
找到lv_port_indev.c文件里面的
touchpad_read(); 我们很容易可以发现lvgl在读取触摸屏的触摸点的xy之前会判断触摸屏是否被按下。
所以 在移植获取触摸xy坐标接口函数前,我们还需要完成 检查触摸屏是否被按下的功能函数:touchpad_is_pressed(); 的实现,如图:
注意:这个触摸屏是否被按下的检测函数需要估计您的触摸屏来自己实现哦!
紧接着,我们就该移植touchpad_get_xy();获取触摸屏xy坐标的函数接口了:
第四步:清理输入接口文件中未用到的输入设备相关的代码(lv_port_indev.c)
重要事情说3遍!!!这一步 非常重要!非常重要!!非常重要!!!
比如我们现在使用触摸为输入,那一定要清理掉 按键、编码器、鼠标等没有用到的输入设备代码!!!尤其是在lv_port_indev_init();输入接口初始化函数里面!如果不清理掉,那么lvgl的输入设备句柄(static lv_indev_drv_t indev_drv;)就会被后面的按键、编码器和鼠标的驱动初始化所覆盖,导致我们的触摸屏失效。清理过后只留下触摸屏相关输入代码的lv_port_indev.c 文件:
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_indev.h"
#include "lvgl.h"
#include "./BSP/LCD/bsp_lcd.h"
#include "./BSP/TOUCH/bsp_touch.h"
#include "./LCD_GUI/lcd_gui.h"
static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);
lv_indev_t * indev_touchpad;
void lv_port_indev_init(void)
{
static lv_indev_drv_t indev_drv;
/*------------------
* Touchpad
* -----------------*/
/*Initialize your touchpad if you have*/
touchpad_init();
/*Register a touchpad input device*/
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
indev_touchpad = lv_indev_drv_register(&indev_drv);
}
/*------------------
* Touchpad
* -----------------*/
/*Initialize your touchpad*/
static void touchpad_init(void)
{
/*Your code comes here*/
TP_Init(); //触摸屏初始化
}
/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
static lv_coord_t last_x = 0;
static lv_coord_t last_y = 0;
/*Save the pressed coordinates and the state*/
if(touchpad_is_pressed()) {
touchpad_get_xy(&last_x, &last_y);
data->state = LV_INDEV_STATE_PR;
}
else {
data->state = LV_INDEV_STATE_REL;
}
/*Set the last pressed coordinates*/
data->point.x = last_x;
data->point.y = last_y;
}
/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
/*Your code comes here*/
if(TOUCH_IRQ()==0) return true;
return false;
}
/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
uint16_t xtemp,ytemp = 0;
// (*x) = 0;
// (*y) = 0;
TP_Read_XY(&xtemp,&ytemp); //读取触摸坐标
if(xtemp>=0 && xtemp <= lcddev.width && ytemp>=0 && ytemp <= lcddev.height ) //判断触摸坐标是否正确
{
*x = xtemp;
*y = ytemp;
//printf("(%d,%d)\r\n",*x,*y);
}
}
#else /*Enable this file at the top*/
/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
五、LVGL 时基单元配置(LV_TICK)
LVGL 使用 时基(LV_TICK) 作为它的核心时间管理机制,用于驱动动画、任务(lv_task)、输入处理等功能。在 STM32 上移植 LVGL 时,必须正确配置 LV_TICK,否则 UI 可能会卡顿或无法正常工作。
1. LV_TICK 作用
LV_TICK 负责:
. 驱动 LVGL 内部任务(lv_task_handler())
. 管理动画(如按钮点击的过渡效果)
. 处理输入设备(如触摸屏、按键)
. 定时更新 UI
2. 配置LVGL时基 LV_TICK 方法
配置一个1ms的STM32定时器,在定时器的中断回调函数里面调用lv_tick_inc(1);
3. 确保 lv_task_handler()
在main()执行
无论你选择哪种 LV_TICK 方法,都必须在 main()
函数里调用lv_task_handler()
:
while (1)
{
lv_timer_handler();
delay_ms(1);
}
六、LVGL 的核心配置文件(lv_conf.h)
lv_conf.h 是 LVGL 的核心配置文件,用于 启用 / 禁用 LVGL 功能、优化性能、减少内存占用。
1.lv_conf.h 的关键作用
控制 LVGL 组件启用 / 禁用(如按钮、滑块、进度条等)。
设置颜色格式(RGB565、ARGB8888 等),影响 屏幕颜色显示。
定义缓冲区大小,优化 内存管理。
启用 / 关闭动画、字体、日志,优化 性能与调试。
选择时基单元(Tick Source),确保 LVGL 正确计时。
2.如何配置lv_conf.h
第一步:启用lv_conf.h
打开我们工程的LVGL_Config分组,里面有一个我们之前添加的lv_conf.h文件,找到文件开头的
“#if 0”条件编译,将其改为“#if 1”,这样就启用了该头文件了,如图:
第二步:设置颜色格式(LV_COLOR_DEPTH)
我的屏幕是16位色的RGB565屏幕,所以我的设置为#define LV_COLOR_DEPTH 16,加上我的屏幕的通讯接口为SPI,每次只能发8位的颜色数据,所以如果后面在 STM32 上使用 LVGL 时,颜色显示异常(如蓝色变成绿色),就需要#define LV_COLOR_16_SWAP 1 。(当通过字节导向接口(如 SPI)发送 16 位颜色时,您可能需要此功能。由于 16 位数字以小端格式存储(低位字节在低地址),因此接口将首先发送低位字节。但是,显示器通常需要先发送高位字节。字节顺序不匹配会导致颜色严重失真。)
参考于LVGL源码里面的lvgl\docs\overview\color.md说明:
第三步:设置LVGL内存大小(LV_MEM_SIZE)
对于LVGL内存大小设置,我建议是在初步验证移植的时候尽量设置较小!特别是对于内存RAM空间较小的MCU。我建议是先设置为16KB,如图:
(备注:后续需要运行大型的测试Demo的话,要过来这里把LV_MEM_SIZE相应改大点)
如果对 lv_conf.h还有其他的疑问,可以查阅下面的博客:
LVGL配置文件lv_conf.h详解-CSDN博客https://blog.csdn.net/qq_28576837/article/details/136333681温馨提示:
一般STM32初始的.S汇编启动文件里面设置的堆栈大小都比较小,为了使LVGL能正常的运行,我们必须先修改下MCU的堆栈大小,如图:
如果堆栈空间设置不合理的话,会导致堆栈溢出:
轻则 LVGL会运行一段时间突然卡死(堆空间过小),重则 直接屏幕黑屏(栈或者堆空间过小)
栈空间设置过小会导致程序一启动就直接黑屏显示:
堆空间设置过小会导致程序运行一段时间屏幕显示就卡死不动了:
七、LVGL 的使用和移植测试
在使用LVGL前,我们必须先初始化LVGL、显示以及输入设备(如果有)。
1.初始化LVGL和输入接口
第一步:添加LVGL相关头文件
(备注: 如果没有触摸屏的情况下就不用添加“lv_port_indev.h”)
第二步:初始化LVGL、显示和输入接口
(备注: 如果没有触摸屏的情况下就不用添加“lv_port_indev_init(); ”)
第三步:编写测试控件
为了更简单高效的测试LVGL是否移植成功,我们可以创建一个按钮控件,如:
/*在屏幕中间创建一个120*50大小的按钮*/
lv_obj_t* switch_obj = lv_switch_create(lv_scr_act());
lv_obj_set_size(switch_obj, 120, 50);
lv_obj_align(switch_obj, LV_ALIGN_CENTER, 0, 0);
特别注意:
我们用户自己创建的UI,要在LVGL初始化之后,main()函数while(1)前调用哦!
完整的main()函数:
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(360, 25, 2, 8); /* 设置时钟,180Mhz */
delay_init(180); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
LED_GPIO_Init();
KEY_GPIO_Init();
BASE_TIMx_Init(TIM6,9000-1,10-1); /* 配置定时器6为1ms周期,为LVGL提供时基 */
lv_init(); /* 初始化LVGL图形库 */
lv_port_disp_init(); /* lvgl显示接口初始化,放在lv_init()的后面 */
lv_port_indev_init(); /* lvgl输入接口初始化,放在lv_init()的后面 */
// printf("LVGL Version: %d.%d.%d\r\n", LVGL_VERSION_MAJOR, LVGL_VERSION_MINOR, LVGL_VERSION_PATCH);
/*在屏幕中间创建一个120*50大小的按钮*/
lv_obj_t* switch_obj = lv_switch_create(lv_scr_act());
lv_obj_set_size(switch_obj, 120, 50);
lv_obj_align(switch_obj, LV_ALIGN_CENTER, 0, 0);
while (1)
{
lv_timer_handler();
delay_ms(1);
}
}
到此!移植已经完全!激动人心的编译时刻到了!
在编译前,我们需要设置勾选“C99 Mode”,如图:
非常幸运的我,一次编译就过了!哈哈哈哈......!
0 Error(s), 0 Warning(s)
下载验证,奥利给!!!如图所示:屏幕中间显示了一个开关按钮,如果您移植了触摸驱动,那此时您点击开关便可打开或者关闭按扭
如果出现了下面图片的控件不显示或者颜色错乱情况,
那就是LVGL的LV_COLOR_DEPTH 或者 LV_COLOR_16_SWAP 设置不正确:
LV_COLOR_DEPTH 颜色深度设置不正确,可能出现控件不能显示:
LV_COLOR_16_SWAP 是否交换16位颜色高低8位设置不正确,会导致颜色显示错误:
八、LVGL 官方Demo的使用和测试
1.添加Demo源码到LVGL_Demos分组
我们找到demos文件夹,打开可以看到,LVGL官方给我们提供了5个Demo,分别:
您需要用到哪个Demo就把哪个Demo的.C文件(包括子文件夹的)全部加入到LVGL_Demos分组,如图:
2.添加Demo的头文件路径
由于LVGL源码里面在include头文件时是使用的相对路径,所以,我们不需要说把所有文件夹和其子文件夹路径都添加进去,只需要添加一个demos路径即可。
后续移植成功LVGL后,使用LVGL官方的Demo,如果错找不到Demo里面相关的函数什么的,那还需要挨个添加Demo的头文件路径:(正常情况是不用挨个添加头文件路径的,因为demo里面也是使用相对路径include头文件的)
3.启用Demo开关宏
进入LVGL_Config分组,打开之前添加到该分组的lv_conf.h文件,拉到文件最后面,我们可以看到有很多的demo宏开关,需要哪个就打开哪个就可以了,如图:
注意! 注意!! 注意!!!:
1.LVGL官方提供的有的Demo代码量是比较大的哦,尽量不要全部Demo都打开,否则flash小的MCU可能会爆ROM空间不够哦。
2.LVGL官方提供的有的Demo需要的ram会比较大哦,在使用demo前,我们必须确保mcu的堆栈空间以及LVGL的内存大小是否满足运行该demo。
例如 :官方提供的widgets demo:
相对应的我们就必须设置 LV_MEM_SIZE 以满足 widgets demo的正常运行:
最后,我们在main()里面调用 lv_demo_widgets() :
相对应的,还需添加好lv_demos.h头文件到main.c:
编译下载widgets demo到MCU,显示效果如图所示:
备注:Keil 5 在编译LVGL官方的Demo时,大概率会报“缺少字体”的错误,如图:
原因 :是LVGL的lv_conf.h文件里面只默认开启了 LV_FONT_MONTSERRAT_14 ,如图:
解决方法:缺啥补啥!缺啥补啥!!
九、LVGL 的优化
1.显示刷新帧率提高优化
为了方便我们优化刷新FPS,LVGL官方提供了一个CPU+FPS的监控插件,打开LVGL的lv_conf.h,找到 LV_USE_PERF_MONITOR,打开 即可:
#define LV_USE_PERF_MONITOR 1
打开LV_USE_PERF_MONITOR后,显示屏右下角就可以显示出来CPU使用率和显示刷新FPS,如图:
对于提高显示屏刷新帧率,我们可以从以下几个方面做出优化:
(1).减小显示刷新周期
减小 LV_DISP_DEF_REFR_PERIOD 显示刷新周期
以下纯属个人观点,如有异议,直接忽略划线部分:
特别注意:LVGL自带的显示FPS,该FPS的值是指:每秒钟 显示缓存 的刷新帧数!!!
即:实际的全屏刷新帧数 = 显示的FPS*(显示屏全屏大小/显示缓存大小)
例如:
我的屏幕是320*240大小,如果我的显示缓存是320*20的话,LVGL的FPS监控显示100fps。
那么 实际全屏刷新FPS = 100*((320*240)/(320*20)) = 8 fps
(2).使用 双缓冲 刷新显示
打开lv_conf.h,使用模式2的双缓存,如图:
(备注:对于ram比较大的MCU,可以适当增大buf_2_1,buf_2_2的大小,以提高刷新速度)
(3).使用 硬件SPI + DMA
对于STM32的MCU,基本上都已经支持硬件SPI和DMA了,我们可以使用硬件SPI+DMA来驱动我们的显示屏,刷新帧率会大大提高!(IIC的屏幕就使用硬件IIC)
(4). 使用 DMA2D 硬件加速
(5).适当提高lv_timer_handler的调用频率
适当减少调用 lv_timer_handler()的时间间隔
,你可以控制在5ms以内调用,如图:
(注意:lv_timer_handler调用间隔
不是越快越好,CPU 会变忙,得不偿失)
2.触摸灵敏度提高优化
减小 LV_INDEV_DEF_READ_PERIOD 触摸刷新周期
3.待更新.........
十、LVGL 移植常见问题解决
1.颜色显示错乱
LV_COLOR_16_SWAP 设置不正确
2.屏幕显示不全
MY_DISP_HOR_RES 和 MY_DISP_VER_RES设置不正确
3.显示花屏
可能1:disp_flush接口里面移植出问题了
可能2:由于DMA参与,数据没有同步好,可能导致DMA在搬运颜色数据时MCU也在处理颜色数据,导致颜色错乱。
解决办法:等待DMA把数据搬运完成后再发送新数据
5.显示黑屏
栈空间设置过小会导致程序一启动就直接黑屏显示:
备注:更改STM32的堆栈大小:
6.显示卡死
堆空间设置过小会导致程序运行一段时间屏幕显示就卡死不动了:
备注:更改STM32的堆栈大小:
7.颜色显示缺失
LV_COLOR_DEPTH 颜色深度设置不正确,可能出现控件不能显示: