赞
踩
准备工作
一、使用CubeMX生成FreeRTOS工程
二、获取LVGL源码
三、删减修改LVGL源文件
四、将LVGL源码和裸机驱动代码加入Keil工程
五、修改lv_conf.h相关配置
六、修改LVGL相关显示配置及显示设备接口
七、修改LVGL输入设备配置和输入设备接口
八、在FreeRTOS中运行LVGL
九、编译下载观察屏幕是否正常
十、结语
本文介绍了如何将LVGL源码移植到STM32的FreeRTOS工程下,从而方便读者进行快速开发,仅作为教学示例。
操作步骤中有些操作非必要或需要读者根据实际项目情况进行修改的部分,已详细说明,望读者仔细阅读。
本教程不涉及LVGL的功能裁剪,所以会将所有文件进行移植,望读者知悉。
,需要本教程工程的读者,点赞、收藏、评论区留下邮箱,会发给大家。
切勿在实际项目中将本教程无脑照搬!
切勿在实际项目中将本教程无脑照搬!
切勿在实际项目中将本教程无脑照搬!
本教程需要如下外设:LTDC、DMA2D、TIM15(用于屏幕背光)、FMC(用于外部SDRAM)、I2C(用于触摸读取)
如要学习LTDC、DMA2D和FMC,后续发教程
由于此处的lvgl.h文件与主目录下的lvgl.h重名,为防止读者后续混淆,所以在此进行删除
实际我们可以看到,src目录下的lvgl.h也只是include主目录的lvgl.h,并没有实际作用
poring目录下的文件是接口文件,包括显示接口、输入设备接口、文件系统接口
因为修改了.h文件名,.c文件中引用的头文件名同步修改
恭喜!您已完成LVGL源文件的删减修改工作。
接下来将LVGL源码加入Keil工程,放心,跟着教程走,很快就可以全部完成了。
具体的工程目录结构和文件名称根据实际项目自行修改,此步骤仅为方便读者快速使用LVGL的一种示例
注意:使用V6版本编译器编译生成的FreeRTOS工程时会出现错误,具体解决办法查看以下博客
https://blog.csdn.net/Roger_717/article/details/128060949
本教程导入了LCD屏幕驱动和触摸驱动
用户导入的驱动代码需要实现以下函数(输入设备以触摸屏为例):
- LCD屏幕初始化函数
- LCD屏幕画点或填充矩形函数(非纯色矩形,而是将传入的数组显示到屏幕上)
- 是否触摸函数(bool值)
- 触摸点坐标读取函数(仅一个坐标点即可,LVGL仅支持单触摸点)
此教程不涉及LVGL的裁剪,故添加src目录下所有.c文件
此步骤花费时间较长,请读者耐心操作,点开所有scr下的文件夹(所有层级的文件夹)进行.c文件的查找和添加
动画仅展示部分操作
此步骤是本教程真正区别于其他无脑导入所有头文件教程的地方
LVGL源码的头文件有自己内部路径管理,Keil中仅需要导入主目录下 “lvgl.h” 和 "lv_conf.h"的路径即可
恭喜! 您已完成LVGL所有文件的导入。
接下来进行LVGL配置文件的修改,放心,跟着教程走,很快就可以全部完成了。
恭喜! 您已完成lv_conf.h的配置修改。
接下来进行LVGL接口文件的修改,放心,跟着教程走,很快就可以全部完成了。
因为我们在Keil中只导入了 lvgl.h 和 lvgl_conf.h 的路径(点此查看之前操作)
所以在 lv_port_disp.c 文件中#include "lv_port_disp.h"
需要修改为#include "/lv_port_disp.h"
,表示在 lv_port_disp.c 文件的同级目录下查找 lv_port_disp.h
修改 lv_port_disp.h 也是同理,将#include "lvgl/lvgl.h"
修改为#include "../lvgl.h"
,表示在 lv_port_disp.h 文件的上一级目录下查找 lvgl.h
读者自行在 lv_port_disp.h 中定义自己的屏幕尺寸,其中
MY_DISP_HOR_RES
表示屏幕水平方向上每行的像素数量,即屏幕宽度
MY_DISP_VER_RES
表示屏幕竖直方向上每列的像素数量,即屏幕高度
注意:尺寸不可以定义错误,如果宽度和高度定义反,会导致显示错乱
关于屏幕缓冲区的3种形式,在此不做详细解释,望读者知悉
本教程使用第2种屏幕缓冲形式,即双非全屏缓冲,一般在使用DMA时使用该形式
此形式需要提供2个屏幕缓冲区(即2个全生命周期的Buffer),大小为 屏幕宽度*用户自定义高度,此形式大概工作流程:屏幕是被两个Buffer里的内容轮流填入屏幕的
在 lv_port_disp.c 的120行,不要忘记修改为disp_drv.draw_buf = &draw_buf_dsc_2;
,表示使用第二种缓冲区形式
注意:
- 用户自定义高度除了在Buffer大小处用到以外,在缓冲区注册函数
lv_disp_draw_buf_init
中最后一个参数内也要修改- 用户自定义高度需要根据实际硬件资源以及实际测试效率进行修改
在 lv_port_disp.c 中导入用户的屏幕驱动头文件
在disp_init
函数中填入用户自己的LCD屏幕初始化函数(屏幕初始化操作,不是必须填在LVGL中,用户可自行选择何时初始化,但必须在运行LVGL前)
在disp_flush
中注释LVGL提供的刷屏函数示例,填入自己的刷屏函数,本教程刷屏函数是使用了DMA2D将屏幕缓冲区依次拷贝到显存中
在disp_flush
中还需要注释掉通知刷屏完成的lv_disp_flush_ready(disp_drv);
,因为本教程使用DMA2D进行刷屏,因此在DMA2D传输完成中断中进行刷屏完成通知
通知刷屏完成的代码位置由用户自行决定,本教程在DMA2D传输完成中断中
通知刷屏完成的意思是LVGL将一个屏幕缓冲区(即 6.3 小节中第二种缓冲区形式提供的两个全生命周期Buffer)内容已经更新到了显存中
#include "../LVGL/porting/lv_port_disp.h"
void HAL_DMA2D_XferCpltCallback(DMA2D_HandleTypeDef *hdma2d){
DMA2D->CR &= ~DMA2D_CR_TCIE;
DMA2D->IFCR |= 1<<1;//清除传输完成标志
__HAL_DMA2D_ENABLE_IT(hdma2d, DMA2D_CR_TCIE);
lv_disp_flush_ready(&disp_drv);//通知LVGL刷屏完成
}
恭喜! 您已完成修改LVGL相关显示配置及显示设备接口。
接下来进行修改LVGL输入设备配置和输入设备接口,放心,跟着教程走,很快就可以全部完成了。
LVGL支持5中输入设备,分别是:触摸板、鼠标、键盘、编码器、按键;本教程使用的屏幕带有触摸,因此使用触摸板作为输入设备
在输入设备创建代码中,屏蔽其他没有使用到的输入设备类型
用户需要提供以下3个功能函数
touchpad_init
:触摸板初始化(触摸板初始化操作,不是必须填在LVGL中,用户可自行选择何时初始化,但必须在运行LVGL前)touchpad_is_pressed
:触摸板是否被按下,返回值为booltouchpad_get_xy
:获取一个触摸板的坐标
注意:由于LVGL内部有断言检查ASSERT,如果不关闭断言编译会有错误
在Keil全局宏定义中添加NDEBUG
即可关闭LVGL断言,注意要使用英文逗号进行分割
恭喜! 您已完成修改LVGL输入设备相关内容。
接下来进行在FreeRTOS中运行LVGL,放心,跟着教程走,很快就可以全部完成了。
在文件 “freertos.c” 中找到 HAL库提供的Tick中断钩子函数vApplicationTickHook,在其中调用
lv_tick_inc(1)
lv_tick_inc()
填入的数字为调用此函数的周期,单位毫秒;由于FreeRTOS的Tick中断周期为1ms,所以为lv_tick_inc(1)
#include "lvgl.h"
#include "../LVGL/porting/lv_port_disp.h"
#include "../LVGL/porting/lv_port_indev.h"
osThreadId_t displayTaskHandle;
const osThreadAttr_t displayTask_attributes = {
.name = "displayTask",
.stack_size = 2*1024,
.priority = (osPriority_t) osPriorityNormal,
};
void displayTask(void *argument)
{
lv_init();
lv_port_disp_init();
lv_port_indev_init();
lv_demo_benchmark();
for(;;)
{
lv_task_handler();
osDelay(3);
}
}
displayTaskHandle = osThreadNew(displayTask, NULL, &displayTask_attributes);
此步骤其实为非必要步骤
由于LVGL线程不安全,即在实时系统中,同一时刻只能有一个任务或线程操作LVGL;
因此,如果用户有多个任务需要操作LVGL时,需要使用互斥锁来保证LVGL的线程安全;
如果用户仅有一个任务或线程运行LVGL,就不需要为LVGL创建互斥锁
osMutexId_t lvgl_MutexHandle;
const osMutexAttr_t lvgl_Mutex_attributes = {
.name = "lvgl_Mutex"
};
lvgl_MutexHandle = osMutexNew(&lvgl_Mutex_attributes);
void displayTask(void *argument)
{
lv_init();
lv_port_disp_init();
lv_port_indev_init();
lv_demo_benchmark();
for(;;)
{
osMutexAcquire(lvgl_MutexHandle, portMAX_DELAY);
lv_task_handler();
osMutexRelease(lvgl_MutexHandle);
osDelay(3);
}
}
实际上机效果参考以下视频
https://live.csdn.net/v/405742?spm=1001.2014.3001.5501
ok,各位读者,教程已到尾声,希望本教程在各位开发的路上能提供些许帮助,感谢大家的阅读,希望大家能帮忙点赞、收藏和评论,您的鼓励是我创作的动力!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。