代码资料请到www.wujique.com上找
例程放在百度云.
2020.06.21
本文档建议用Typora浏览。
LVGL是啥?
lvgl是一个GUI,或者,可以认为是一个应用层的输入框架。
github
demo
https://github.com/lvgl/lv_examples
更新比较频繁,现在已经是7.0版本。
移植
网络很多人移植了,但是都只是说,额,我移植了lvgl。
下面这个,说的还算详细,不过,LVGL现在7.0了,LVGL的代码文件夹组织方式已经改动较大。
以前大家就经常吐槽它逻辑不好。
https://blog.csdn.net/qq_42992084/article/details/105882751
之前
本次移植到STM32H750VB,没有外挂RAM。在移植之前,需要说明一些问题。
显存
显存就是RAM。
经常有人问,H750VBT能支持多大的LCD?
- 如果是MCU接口的LCD,本来显存就是放在LCD控制器上,所以,理论上说,多大的LCD都可以点亮。但是越大的屏,刷新时间也长。
- RGB接口屏,这种屏通常是没有显存的。比如STM32的LTDC控制器,控制一个RGB接口的屏,就需要在芯片内部开辟显存。
- 一个16bit的RGB屏,1个点需要2个字节,480x272像素的LCD,就需要480x272x2字节,也就是255K。
- 如果RAM充足,通常做两层显存,一层用于刷新显示,一层用于修改,修改好后,切换为刷新显存,原来显示的显存改为用于修改。
- STM32H750VB 号称 内置有1M RAM,那是不是就可以开辟两层显存呢?
H750的内存
是否可以在H750上开辟两层480*272像素的显存?理论可以,目前没搞定!
为什么?我们先看看H750的架构。
文档:dm00314099-stm32h742-stm32h743753-and-stm32h750-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
看上面这个图,1M的内存,分为多块,功能都有差异。跟LTDC连在一起的,只有512K,AXI SRAM。
在STM32CUBEIDE的编译结果中其实也可以看到:
也就是说,LTDC,可以用作显存的RAM,至多也就512K,看起来能开辟两层显存。
但是,CPU直接连接的也只有AXI,堆栈这种东西,估计也是放在这里,除去510K显存,剩2K,我觉得可能不够的。
待研究,待研究。
lvgl内存
分两部分,显存和内存消耗。
内存消耗,不算显存,内存要几十K。
显存呢?由我们定。
在lv_port_disp_.c
文件的lv_port_disp_init
函数中,有这样一段话:
/* LVGL requires a buffer where it draws the objects. The buffer's has to be greater than 1 display row *
There are three buffering configurations:
- Create ONE buffer with some rows:
LVGL will draw the display's content here and writes it to your display
- Create TWO buffer with some rows:
LVGL will draw the display's content to a buffer and writes it your display.
You should use DMA to write the buffer's content to the display.
It will enable LVGL to draw the next part of the screen to the other buffer while
the data is being sent form the first buffer. It makes rendering and flushing parallel.
- Create TWO screen-sized buffer:
Similar to 2) but the buffer have to be screen sized. When LVGL is ready it will give the
whole frame to display. This way you only need to change the frame buffer's address instead of
copying the pixels.
*/
翻译为中文就是:
LVGL需要显存,有3种方法:
- 单个局部显存。
- 双局部显存
- 双全显存。
双全显存就是前面说的双显存。
局部显存的数据逻辑就是,LCD驱动要有一个全显存, LVGL只要局部显存,将局部显存的内容填到LCD显存完成显示。
开工
现在正是开始测试LVGL的一个DEMO。基于STM32H750VB,无外挂RAM,480*272像素的RGB屏,电容触摸。
程序基于STM32H750 RGB屏的DEMO。
下载
https://github.com/lvgl/lv_examples
下载后解压,文件夹改名为lvgl和lv_examples,丢到
H750_cube_demo_LVGL\User\
目录下。清理
examples里面很多demo,我们只用一个。其他的删除。
demo在src目录,保留ex的3个文件夹和widgets文件夹。
配置
拷贝lv_examples目录的lv_ex_conf_templ.h到lv_examples外,改名为lv_ex_conf.h。
打开这个头文件,打开开头的条件编译。
#if 1 /*Set it to "1" to enable the content*/
配置demo widget
xxxxxxxxxx
/*Show some widget*/
#define LV_USE_DEMO_WIDGETS 1
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 1
#endif
拷贝lvgl目录的lv_conf_template.h文件到lvgl外,改名为lv_conf.h。
打开这个头文件,打开文件前面的条件编译。
x/*
* COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER
*/
#if 1 /*Set it to "1" to enable content*/
配置屏幕信息
xxxxxxxxxx
/*
ATK4342
*/
#define LV_HOR_RES_MAX (480)
#define LV_VER_RES_MAX (272)
/* Color depth:
* - 1: 1 byte per pixel
* - 8: RGB332
* - 16: RGB565
* - 32: ARGB8888
*/
#define LV_COLOR_DEPTH 16
PORT
PORT就是移植,就是实现接口。
在目录H750_cube_demo_LVGL\User\lvgl\porting\有例子,复制一份,文件名去掉_template,再修改。
总共有3个接口需要实现:LCD显示,触摸输入,文件系统。
首先,打开这几个文件的条件编译。
第二,LCD初始化函数lv_port_disp_init,选择显存模式,现在,我们先用第二种。
第三,实现
disp_flush
函数,不好意思,在这个demo中,只有这是我添加的。直接将显示内容复制到显存。xxxxxxxxxx
p = (uint16_t *)RGB565_480x272;
*(p+y*disp_drv->hor_res + x) = color_p->full;
xxxxxxxxxx
/* 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*/
uint16_t *p;
int32_t x;
int32_t y;
p = (uint16_t *)RGB565_480x272;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/* Put a pixel to the display. For example: */
*(p+y*disp_drv->hor_res + x) = color_p->full;
/* put_px(x, y, *color_p)*/
color_p++;
}
}
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
应用
这是main函数的一部分,在这之前,LCD已经初始化好。
xxxxxxxxxx
lv_init();
lv_port_disp_init();
lv_port_indev_init();
lv_demo_widgets();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
gt9147_task();
HAL_Delay(10);
lv_tick_inc(10);
lv_task_handler();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
触摸输入
本次移植基于电容屏的LCD。
程序已经完成GT9147驱动开发,能读到电容屏的触摸点值。
对接LVGL的文件在
lv_port_indev.c
文件。触摸屏相关的函数有两个:touchpad_is_pressed
、touchpad_get_xy
。touchpad_is_pressed
函数用于判断当前是否触摸。touchpad_get_xy
获取触摸点的xy坐标。功能理解起来不难,但是有个隐藏的技术点要提前了解:生产者和消费者的速度不匹配。
生产者,就是电容屏驱动,就是扫描到触摸点的函数。
消费者就是LVGL的
touchpad_read
函数。不匹配的表现是:
- 驱动扫描得到几个点了,LVGL不读取。
- LVGL连续读几次,却由于驱动没有扫描,所以读不到点。
匹配生产者个消费者的方法,可以用缓冲。缓冲的原理就是削峰填谷。
扫描到点后,先放到缓冲。LVGL读点也是从缓冲读取。
使用缓冲之后,对于触摸屏来说,就有一个问题需要认识:
读不到点,并不代表当前没有触摸。
具体实现请看代码。
效果
后记
在 stm32h750vb上移植了,全屏刷新时,不是很平滑。
是触屏问题还是刷屏问题?待优化。