电子说
一、
上周将lvgl粗略的移植到stm32f429上,界面刷新问题没有好好处理,看着非常非常卡顿,今天初步处理了这个问题,效果还算可以了,后边应该是可以更进一步优化。我们先在裸机上移植,以后移植到OS上。移植的视频效果在结尾处。
二、移植步骤
1、提前准备一个移植好LCD、触摸驱动的工程,并修改工程名。
2、获取lvgl图形库源码。
3、将源码添加到提前准备好的工程。
4、修改配置lv_conf.h文件。
5、调用lvgl的初始化程序 lv_init()。
6、在lvgl中注册显示和输入设备驱动程序。
7、在中断服务函数中调用lv_tick_inc(x)以告知lvgl经过的时间。
8、每隔几毫秒调用lv_timer_handle()来处理lvgl相关的任务。
三、提前准备一个移植好LCD、触摸驱动的工程,并修改工程名
我的板子是用的野火的,所以我直接找一个野火的例程进行修改,就用这个触摸屏的例程。
根据个人喜好更改文件夹名以及工程名,工程名需要是英文,这些我在这里不多说了,自己修改就好,当然也可以不修改。
修改工程的时候建议将Browse information勾上,勾上以后函数跳转很方便,不过勾上以后第一次编译程序会很慢,这个也看个人需求。
四、获取lvgl源码
新建文件夹使用git获取源码。
获取成功后可以看到一个lvgl的文件夹,这就是源码了。
不熟悉git的结尾处我也会提供一份源码。
五、将源码添加到提前准备好的工程,修改配置lv_conf.h文件
到目前我们准备好lvgl的源码以及准备移植的工程。
1、将lvgl文件夹直接放到lvgl_porting中,然后在新建一个lvgl_app文件夹,后边自己的lvgl应用程序可以放这里。
2、进入lvgl这个文件里可以看到以下文件
3、进入src文件夹后,按照下图文件夹名字在keil工程中添加目录。
4、然后除了以下目录下文件不添加,其他c文件全部添加,注意有的目录下还有文件夹,文件夹里的c文件也要添加,不要漏了。
gpu暂时也不需要添加。
5、将lv_conf_template.h复制到上一层目录,并修改lv_conf_template.h为lv_conf.h,这个是一定要修改的,官方介绍的也是这样,工程里也是使用的lv_conf.h的头文件。
6、打开改名后的lv_conf.h,使能文件。同时可以看到这里配置的色彩深度是16位的,对应RGB565格式,一会儿我们LCD屏也是配置RGB565使用。其他配置暂时先不用改。
7、将lvgl->examples->porting中选中的文件复制到与lv_conf.h文件同一目录下,并把template字眼去掉。分别为显示设备的驱动接口和输入设备的接口,我们一会儿完善。
8、在keil工程新建两个目录lvgl_porting,lvgl_demo。lvgl_porting放如下三个文件。lvgl_demo一会儿放例程。
9、添加所有目录下的头文件
10、然后开始编译一下工程,如果你C文件添加正确,以及头文件目录配置正确,这个时候已经可以编译通过了。如果这里编译报错可以参考我结尾处附上的工程文件进行参考。
六、按照LVGL的运行需求修改工程
Basically, every modern controller which is able to drive a display is suitable to run LVGL. The minimal requirements are:
16 MHz clock speed is recommended
1、设置堆栈大小
2、配置C99模式
七、在lvgl中注册显示驱动程序
到目前为止我们基本没修改任何代码,只是使能了一些文件的宏,同时最繁琐的事情我们已经做完了。接下来完善注册显示接口驱动以及设备输入接口驱动。
显示驱动程序
定义缓冲区流程
通过lv_disp_draw_buf_t
变量初始化,如下所示:
/* Example for 1) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/* Example for 2) */
static lv_disp_draw_buf_t draw_buf_dsc_2;
static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/* Example for 3) also set disp_drv.full_refresh = 1 below*/
static lv_disp_draw_buf_t draw_buf_dsc_3;
static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/
static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/
lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2,
MY_DISP_VER_RES * LV_VER_RES_MAX); /*Initialize the display buffer*/
注意,lv_disp_draw_buf_t
需要是静态的、全局的或动态分配的,而不是超出范围时销毁的局部变量。
如上所示,有三个示例。绘制缓冲区可以比屏幕小,如果刷新整个界面,需要刷新几次就好了,如果只有小区域发生变化(如只按下按钮),则只刷新该区域。官方建议缓冲区的大小至少为屏幕宽度的1/10。
如果使用 一个缓冲区 ,LVGL 将屏幕内容绘制到该绘制缓冲区中并将其发送到显示器。 这样 LVGL 需要等到缓冲区的内容发送到显示器,然后再在其中绘制新内容。
如果使用 两个缓冲区 ,LVGL 可以绘制到一个缓冲区中,而另一个缓冲区的内容被发送到后台显示。应使用 DMA 或其他硬件将数据传输到显示器,让 MCU 同时绘制。这样,显示的渲染和刷新变得并行。
注册显示驱动流程
其实打开lv_port_disp.c文件中描述的比较清楚如下代码。
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
/*Set the resolution of the display*/
disp_drv.hor_res = 480;
disp_drv.ver_res = 320;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_1;
/*Required for Example 3)*/
//disp_drv.full_refresh = 1
/* Fill a memory array with a color if you have GPU.
* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
* But if you have a different GPU you can use with this callback.*/
//disp_drv.gpu_fill_cb = gpu_fill;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
注意,lv_disp_drv_t
也需要是静态的、全局的或动态分配的,而不是超出范围时销毁的局部变量。
实际操作
1、将lv_port_disp.c和lv_port_disp.h文件宏都打开,并修改.c文件中相应头文件名字,将template去掉。
2、找到lv_port_disp_init()初始化函数,我们使用第一种方法,并修改屏幕宽度,我的屏幕是800*480的,所以我改为800。
如果你有外扩sram或者sdram这里可以配置与屏幕一样大。
我这里有sdram我就放到sdram中了。
3、修改分辨率
回调函数disp_flush完善
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
知道了这两个我们使用到参数后,我们需要编写一个刷新函数LCD_FillRect_color()的。因为我们使用的这个工程已经配置好了DMA2D,我们直接用DMA2D传输,函数实现如下。
void LCD_FillRect_Color(int16_t x1, int16_t y1, int16_t x2, int16_t y2, lv_color_t *color)
{
uint32_t dst_addr = 0;//需要更新显存中内容的起始地址
dst_addr = Ltdc_Handler.LayerCfg[ActiveLayer].FBStartAdress + 2*(800*y1 + x1);//计算需要更新的目标地址 800是屏幕宽度,2是每个像素占2个字节
/* DMA2D Config */
DMA2D- >CR = 0x00000000UL; //配置DMA2D的传输模式为 内存到内存
DMA2D- >FGMAR = (uint32_t)color; //拷贝的源地址,绘制缓存区
DMA2D- >OMAR = (uint32_t)dst_addr; //拷贝的目标地址,即显存位置
DMA2D- >FGOR = 0; //源地址的偏移地址,它被添加到每一行的末尾以确定下一行的起始地址
DMA2D- >OOR = (800 - (x2 - x1 +1)); //目标地址的偏移地址,它被添加到每一行的末尾以确定下一行的起始地址
DMA2D- >FGPFCCR = LTDC_PIXEL_FORMAT_RGB565; //设置颜色格式;
DMA2D- >NLR = (uint32_t)((x2 - x1 + 1) < < 16) | (uint16_t)(y2 - y1 + 1);// 设置拷贝数据的长度和宽度
/* Start */
DMA2D- >CR |= DMA2D_CR_START;
while(DMA2D- >CR & DMA2D_CR_START) {}
}
将我们准备好的绘制函数LCD_FillRect_Color添加进来。
/*Flush the content of the internal buffer the specific area on the display
*You can use DMA or any hardware acceleration to do this operation in the background but
*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
LCD_FillRect_Color(area- >x1, area- >y1, area- >x2, area- >y2, (lv_color_t *)color_p);
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
回调函数完事儿。
显示屏移植已经完成了,可以添加一个demo先点亮屏幕。
添加demo并修改main()函数
在main.c文件中添加头文件
#include "./lvgl.h"
#include "../lv_port_disp.h"
#include "lv_demo_widgets.h"
修改显示屏配置
调用如下初始化函数
添加demo文件,路径:lvgldemoswidgets,并添加头文件路径。
进入lv_demo_widgets.c文件中,使能宏
将此处均设置为1
这个时候编译不报错是正确的。
八、在中断服务函数中调用lv_tick_inc(x)以告知lvgl经过的时间
需要添加头文件
#include "../lvgl/src/hal/lv_hal_tick.h"
九、每隔几毫秒调用lv_timer_handle()来处理lvgl相关的任务
操作到现在,可以编译下载测试了。
十、在lvgl中注册输入设备驱动程序
输入设备可以是触摸板、鼠标、键盘、编码器、按键,我们使用触摸板。
打开lv_port_indev.c文件,只留下触摸板相关程序,其他程序全部删除。
我们只需要把touchpad_read回调函数补充完整即可。
主要是提供当前的按压状态,释放还是压下,以及按下时的坐标即可,这里自己添加就好。
然后将lv_port_indev_init()添加到main()函数中即可。
到此已经移植完成了,剩下就是优化刷新程序以及lvgl控件的应用了。
全部0条评论
快来发表一下你的评论吧 !