STM32 CubeMX生成FreeRTOS工程,并移植LVGL运行Demo,使用触摸屏,非无脑导入所有文件(使用LTDC和DMA2D)

目录

  准备工作
  一、使用CubeMX生成FreeRTOS工程
  二、获取LVGL源码
  三、删减修改LVGL源文件
  四、将LVGL源码和裸机驱动代码加入Keil工程
  五、修改lv_conf.h相关配置
  六、修改LVGL相关显示配置及显示设备接口
  七、修改LVGL输入设备配置和输入设备接口
  八、在FreeRTOS中运行LVGL
  九、编译下载观察屏幕是否正常
  十、结语


写在前面

本文介绍了如何将LVGL源码移植到STM32的FreeRTOS工程下,从而方便读者进行快速开发,仅作为教学示例。
操作步骤中有些操作非必要或需要读者根据实际项目情况进行修改的部分,已详细说明,望读者仔细阅读。
本教程不涉及LVGL的功能裁剪,所以会将所有文件进行移植,望读者知悉。


,需要本教程工程的读者,点赞、收藏、评论区留下邮箱,会发给大家。

切勿在实际项目中将本教程无脑照搬!
切勿在实际项目中将本教程无脑照搬!
切勿在实际项目中将本教程无脑照搬!


准备工作

  1. Keil已安装

  2. CubeMX已安装

  3. 相应屏幕的裸机驱动程序已准备(具体实现哪些功能下面会讲)

  4. LVGL源码(具体步骤下面会讲)

一、使用CubeMX生成FreeRTOS工程 (已会可跳过)

1.1 使用Cubemx,选择STM32芯片型号,本教程以STM32H723ZGT6为例

1.2 进入配置页面,配置DEBUG为Serial Wire

在这里插入图片描述

1.3 使用外部晶振

在这里插入图片描述

1.4 配置相关外设,根据自身项目自行配置,此教程不做详细说明

本教程需要如下外设:LTDC、DMA2D、TIM15(用于屏幕背光)、FMC(用于外部SDRAM)、I2C(用于触摸读取)
如要学习LTDC、DMA2D和FMC,后续发教程

1.5 开启中间件FreeRTOS,Interface选择CMSIS_V2

在这里插入图片描述

1.6 开启FreeRTOS的Tick中断钩子函数(后续为LVGL提供心跳)

在这里插入图片描述

1.7 修改HAL库使用的定时器(HAL库默认使用的SysTick定时器已经自动分配给了FreeRTOS,所以需要重新为HAL库选择一个定时器,可与教程不一致)

在这里插入图片描述

1.8 配置时钟树,FMC给到275M, LTDC给到60M(根据项目实际情况自行配置,此参数仅用于教学使用)

在这里插入图片描述

1.9 修改堆大小为16KByte(根据实际项目需求进行修改,此参数仅为教程使用)

在这里插入图片描述

1.10 勾选“仅复制需要的库文件” 和 “为外设生成单独的.c .h文件”

在这里插入图片描述

1.11 生成代码

在这里插入图片描述

二、获取LVGL源码(本教程以 v8.3.11版本为例)

2.1 LVGL v8.3.11源码下载

在这里插入图片描述

2.2 解压zip文件

在这里插入图片描述

三、删减修改LVGL源文件

3.1 主目录下仅保留如图所示文件,其余全部删除

在这里插入图片描述

3.2 删除src目录下的lvgl.h文件 (这一步不是必须的)

由于此处的lvgl.h文件与主目录下的lvgl.h重名,为防止读者后续混淆,所以在此进行删除

实际我们可以看到,src目录下的lvgl.h也只是include主目录的lvgl.h,并没有实际作用

在这里插入图片描述

3.3 复制examples/porting文件至主目录,并删除examples目录

poring目录下的文件是接口文件,包括显示接口、输入设备接口、文件系统接口

在这里插入图片描述

3.3 修改poting目录下接口文件名(必要步骤,因为其他文件include的文件名不含_template,.h文件必须修改,.c文件为统一所以修改)

在这里插入图片描述

3.4 修改porting目录下的.c文件内容

因为修改了.h文件名,.c文件中引用的头文件名同步修改

在这里插入图片描述

3.4 修改主目录下的lv_conf_template.h文件名为lv_conf.h

在这里插入图片描述

恭喜!您已完成LVGL源文件的删减修改工作。
接下来将LVGL源码加入Keil工程,放心,跟着教程走,很快就可以全部完成了。


四、将LVGL源码和裸机驱动代码加入Keil工程

4.1 将删减后的LVGL源码复制到之前创建的Keil工程下,并更名为 “LVGL”

具体的工程目录结构和文件名称根据实际项目自行修改,此步骤仅为方便读者快速使用LVGL的一种示例

在这里插入图片描述

4.2 打开Keil工程,编译空的FreeRTOS工程

注意:使用V6版本编译器编译生成的FreeRTOS工程时会出现错误,具体解决办法查看以下博客
https://blog.csdn.net/Roger_717/article/details/128060949

在这里插入图片描述

4.3 导入裸机驱动代码 (此部分代码由读者自行在裸机工程下调试完成后提供)

本教程导入了LCD屏幕驱动和触摸驱动
用户导入的驱动代码需要实现以下函数(输入设备以触摸屏为例):

  1. LCD屏幕初始化函数
  2. LCD屏幕画点或填充矩形函数(非纯色矩形,而是将传入的数组显示到屏幕上)
  3. 是否触摸函数(bool值)
  4. 触摸点坐标读取函数(仅一个坐标点即可,LVGL仅支持单触摸点)

在这里插入图片描述

4.4 为LVGL创建4个分组:LVGL/Src、 LVGL/Port、 LVGL/Cfg、LVGL/Demo

在这里插入图片描述

4.5 将LVGL相关文件分别导入4个分组中

4.5.1 将LVGL源码文件夹中src目录下的所有层级目录中的.c文件添加到LVGL/Src组中

此教程不涉及LVGL的裁剪,故添加src目录下所有.c文件
此步骤花费时间较长,请读者耐心操作,点开所有scr下的文件夹(所有层级的文件夹)进行.c文件的查找和添加
动画仅展示部分操作

在这里插入图片描述

4.5.2 将LVGL源码文件夹中porting目录中的.c文件添加到LVGL/Port组中

在这里插入图片描述

4.5.3 将LVGL源码主目录下的 lv_conf.h 添加到LVGL/Cfg组中

在这里插入图片描述

4.5.4 将LVGL源码文件夹中的demos目录中的.c文件添加到LVGL/Demo组中

在这里插入图片描述

4.6 将LVGL主目录路径加入Keil的头文件路径

此步骤是本教程真正区别于其他无脑导入所有头文件教程的地方
LVGL源码的头文件有自己内部路径管理,Keil中仅需要导入主目录下 “lvgl.h” 和 "lv_conf.h"的路径即可

在这里插入图片描述

恭喜! 您已完成LVGL所有文件的导入。
接下来进行LVGL配置文件的修改,放心,跟着教程走,很快就可以全部完成了。


五、修改lv_conf.h相关配置

5.1 启用配置文件,修改颜色深度

在这里插入图片描述

5.2 修改内存管理方式,并设置LVGL可使用内存大小

在这里插入图片描述

5.3 修改屏幕刷新间隔【帧率】,修改输入设备读取间隔(可保持默认值,可根据实际项目需求修改)

在这里插入图片描述

5.4 修改DPI(一般不做修改,官方文档中也说过,作用不大)

在这里插入图片描述

5.5 开启CPU监控、FPS、内存使用率和内存碎片情况(仅用于监控,用户自行决定是否开启)

在这里插入图片描述

5.6 开启BENCHMARK示例Demo(读者可以开启任意示例Demo)

在这里插入图片描述

恭喜! 您已完成lv_conf.h的配置修改。
接下来进行LVGL接口文件的修改,放心,跟着教程走,很快就可以全部完成了。


六、修改LVGL相关显示配置及显示设备接口

6.1 启用 lv_port_disp.c 和 lv_port_disp.h ,修改头文件路径(头文件路径根据实际工程自行修改)

因为我们在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

在这里插入图片描述

6.2 宏定义屏幕像素尺寸

读者自行在 lv_port_disp.h 中定义自己的屏幕尺寸,其中
MY_DISP_HOR_RES 表示屏幕水平方向上每行的像素数量,即屏幕宽度
MY_DISP_VER_RES表示屏幕竖直方向上每列的像素数量,即屏幕高度

注意:尺寸不可以定义错误,如果宽度和高度定义反,会导致显示错乱

在这里插入图片描述

6.3 修改屏幕缓冲区形式 (重要!!!)

关于屏幕缓冲区的3种形式,在此不做详细解释,望读者知悉

本教程使用第2种屏幕缓冲形式,即双非全屏缓冲,一般在使用DMA时使用该形式

此形式需要提供2个屏幕缓冲区(即2个全生命周期的Buffer),大小为 屏幕宽度*用户自定义高度,此形式大概工作流程:屏幕是被两个Buffer里的内容轮流填入屏幕的

在 lv_port_disp.c 的120行,不要忘记修改为 disp_drv.draw_buf = &draw_buf_dsc_2;,表示使用第二种缓冲区形式

注意:

  1. 用户自定义高度除了在Buffer大小处用到以外,在缓冲区注册函数lv_disp_draw_buf_init中最后一个参数内也要修改
  2. 用户自定义高度需要根据实际硬件资源以及实际测试效率进行修改

在这里插入图片描述

6.4 修改显示初始化函数和刷屏函数(重要!!!)

在 lv_port_disp.c 中导入用户的屏幕驱动头文件
disp_init函数中填入用户自己的LCD屏幕初始化函数(屏幕初始化操作,不是必须填在LVGL中,用户可自行选择何时初始化,但必须在运行LVGL前)

disp_flush中注释LVGL提供的刷屏函数示例,填入自己的刷屏函数,本教程刷屏函数是使用了DMA2D将屏幕缓冲区依次拷贝到显存中
disp_flush中还需要注释掉通知刷屏完成的 lv_disp_flush_ready(disp_drv);,因为本教程使用DMA2D进行刷屏,因此在DMA2D传输完成中断中进行刷屏完成通知

在这里插入图片描述

6.5 将屏幕驱动描述符变量修改为全局变量(后续在DMA2D传输完成中断中通知刷屏完成要使用)

在这里插入图片描述

6.6 在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输入设备配置和输入设备接口

7.1 启用 lv_port_indev.c 和 lv_port_indev.h ,修改头文件路径(原理同 6.1 小节)

在这里插入图片描述

7.2 修改输入设备类型并提供输入设备相关功能函数 (重要!!!)

LVGL支持5中输入设备,分别是:触摸板、鼠标、键盘、编码器、按键;本教程使用的屏幕带有触摸,因此使用触摸板作为输入设备

在输入设备创建代码中,屏蔽其他没有使用到的输入设备类型

用户需要提供以下3个功能函数

  1. touchpad_init:触摸板初始化(触摸板初始化操作,不是必须填在LVGL中,用户可自行选择何时初始化,但必须在运行LVGL前)
  2. touchpad_is_pressed:触摸板是否被按下,返回值为bool
  3. touchpad_get_xy:获取一个触摸板的坐标

在这里插入图片描述

7.3 编译当前工程(阶段性检查,保证此时工程无错误,再进行后续操作)

注意:由于LVGL内部有断言检查ASSERT,如果不关闭断言编译会有错误
在Keil全局宏定义中添加 NDEBUG即可关闭LVGL断言,注意要使用英文逗号进行分割

在这里插入图片描述

在这里插入图片描述

恭喜! 您已完成修改LVGL输入设备相关内容。
接下来进行在FreeRTOS中运行LVGL,放心,跟着教程走,很快就可以全部完成了。


八、在FreeRTOS中运行LVGL

8.1 在FreeRTOS的Tick中断钩子函数中为LVGL提供“心跳”(重要!!!)

在文件 “freertos.c” 中找到 HAL库提供的Tick中断钩子函数vApplicationTickHook,在其中调用lv_tick_inc(1)

lv_tick_inc()填入的数字为调用此函数的周期,单位毫秒;由于FreeRTOS的Tick中断周期为1ms,所以为lv_tick_inc(1)

在这里插入图片描述

8.2 在FreeRTOS中创建LVGL显示任务

8.2.1 在 freertos.c 文件中导入LVGL相关头文件

#include "lvgl.h"
#include "../LVGL/porting/lv_port_disp.h"
#include "../LVGL/porting/lv_port_indev.h"

在这里插入图片描述

8.2.2 创建显示任务配置

osThreadId_t displayTaskHandle;
const osThreadAttr_t displayTask_attributes = {
  .name = "displayTask",
  .stack_size = 2*1024,
  .priority = (osPriority_t) osPriorityNormal,
};

在这里插入图片描述

8.2.3 创建显示任务实体

void displayTask(void *argument)
{
	lv_init();	
	lv_port_disp_init();
	lv_port_indev_init();
	
	lv_demo_benchmark();
	
  for(;;)
  {
	  lv_task_handler();
    osDelay(3);
  }
  
}

在这里插入图片描述

8.2.4 声明显示任务实体

在这里插入图片描述

8.2.5 创建显示任务

displayTaskHandle = osThreadNew(displayTask, NULL, &displayTask_attributes);

在这里插入图片描述

8.2.6 为LVGL创建互斥锁(非必要)

此步骤其实为非必要步骤
由于LVGL线程不安全,即在实时系统中,同一时刻只能有一个任务或线程操作LVGL;
因此,如果用户有多个任务需要操作LVGL时,需要使用互斥锁来保证LVGL的线程安全;
如果用户仅有一个任务或线程运行LVGL,就不需要为LVGL创建互斥锁

8.2.6.1 创建互斥锁配置
osMutexId_t lvgl_MutexHandle;
const osMutexAttr_t lvgl_Mutex_attributes = {
  .name = "lvgl_Mutex"
};

在这里插入图片描述

8.2.6.2 创建互斥锁
lvgl_MutexHandle = osMutexNew(&lvgl_Mutex_attributes);

在这里插入图片描述

8.2.6.3 使用互斥锁
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,各位读者,教程已到尾声,希望本教程在各位开发的路上能提供些许帮助,感谢大家的阅读,希望大家能帮忙点赞、收藏和评论,您的鼓励是我创作的动力!

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

握不住的草

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值