本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站。作者:hehung
之前发帖
【GD32F427开发板试用】1. 串口实现scanf输入控制LED
【GD32F427开发板试用】2. RT-Thread标准版移植
【GD32F427开发板试用】3. 硬件IIC0驱动OLED显示中文
【GD32F427开发板试用】4. ADC采集摇杆模块移动量
【GD32F427开发板试用】5. SPI驱动TFTLCD屏幕
【GD32F427开发板试用】6. 定时器运用之精确定时1s
前言
LVGL是一个开源的轻量级图形显示库,非常适合资源比较少的单片机进行移植,上一次使用SPI驱动了一块TFTLCD屏幕,这一次就想着将LVGL移植上来,可以显示一些好看的界面。
本文实现了如下功能:
- 移植基于RT-Thread OS(使用RT_Thread OS移植起来十分的方便,都不需要自己去github下载源码了);
- 开发工具基于RT-Thread Stduio;
- 实现了LVGL功能移植,并使用label(标签)显示字符串;
- 如果没有使用RT-Thread OS,使用其他的系统或者是裸机,本文还是有一定的参考价值。
开发流程
环境准备
- 准备一个可以正常驱动LCD或者其他显示屏幕的工程,其中需要用到画点函数,画点函数是LVGL能够正常工作的基础,如果没有这个函数,需要先实现一个。 使用GD32F427V-START驱动TFT LCD的过程可以参考我之前发的帖子(本文最上部分);
- RT-Thread Studio开发环境,如果使用标准版RT-Thread OS,其他开发环境也是一样的操作,但是配置方面需要使用RT-Thread 提供的ENV工具;如果没有使用RT-Thread OS,使用其他的系统或者是裸机,本文还是有一定的参考价值;
移植过程
添加LVGL包
如下图,添加LVGL,如果使用ENV,则按照ENV的方式添加;
如果使用逻辑,需要在github上下载,电脑安装git,然后输入命令:
git clone --recursive https://github.com/lvgl/lvgl.git
创建显示文件
如下图,将这两个文件移植到application目录下,并更新名字,不然会导致编译错误。
注:还有四个文件根据实际情况选择是否需要复制到application,和文件系统以及触摸屏有关,我所使用的LCD屏幕不带触摸功能,所以没有移植,如果带有触摸功能,可以将最后两个文件一并移植到application文件夹下备用,关于触摸文件的修改,本文不做说明。
更新显示文件
修改头文件包含以及头文件内容
- 针对app_port_disp.c文件,修改头文件包含
- 针对app_port_disp.h文件,修改如下部分
移植(重要部分)
现在开始最重要的部分了,这部分是lvgl移植的核心,需要和我们的驱动文件配合起来。具体方式见下面:
- 修改显示屏的长和宽
我所使用的显示屏是320X240的,所以做如下修改。
如下图:
- lv_port_disp_init函数修改
定位到该函数,初始化buffer,官方提供了三种方式,并详细描述了每方式的区别,本文使用第一种方式,最为简单,我们将buffr扩大了一些,其实默认也是可以的。
屏蔽不需要的其他两种方式。
如下图:
- disp_init函数修改
定位到disp_init,这里面添加驱动函数的初始化函数即可。
修改如下:
其中,lcd_init是我的LCD屏幕的驱动函数;
lcd_set_region是我的驱动函数中设置屏幕长宽的函数,这里根据自己的驱动函数实现逻辑添加即可,对于我的驱动代码,lcd_set_region是必须要加的,不然屏幕显示不正确。
/*Initialize your display and the required peripherals.*/ static void disp_init(void) { /*You code here*/ /* Initialize lcd */ extern void lcd_init(void); lcd_init(); extern void lcd_set_region(uint16_t x_start,uint16_t y_start,uint16_t x_end,uint16_t y_end); lcd_set_region(0,0,MY_DISP_HOR_RES - 1,MY_DISP_VER_RES - 1); } - 更新disp_flush函数
该函数是LVGL显示的核心函数,需要将我们自己的驱动代码写到这里,主要是画一个实心矩形的驱动代码,官方提供了一个接口,我们可以直接在两个for循环内部加上我们的画点函数,或者是自己实现一个画实心矩形的逻辑。我修改的代码如下。
其中,
- LCD_CS_CLR与LCD_CS_SET是我的LCD特有的操作,不一定每个驱动代码都会这样写;
- lcd_draw_point是我的画点函数,color_p是颜色变量,一般的颜色都是16位的,所以直接转换成了uint16即可,颜色的位数可以进行配置,后面再说。
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(disp_flush_enabled) {
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int32_t x;
int32_t y;
LCD_CS_CLR;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/*Put a pixel to the display. For example:*/
/*put_px(x, y, *color_p)*/
extern void lcd_draw_point(uint16_t x,uint16_t y,uint16_t data);
lcd_draw_point(x, y, (uint16_t)color_p->full);
color_p++;
}
}
LCD_CS_SET;
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
到目前为止,基本上以及算是移植完成了显示部分了,但是现在还是不能使用,因为还没有进行配置,接下来描述这部分。
RT-Thread配置
如果没有使用RT-Thread OS可以忽略这步骤,后面的步骤(lv_conf.h文件中)也可以对这部分内容进行配置。
打开RT-Thread配置页面,配置lvgl相关参数,配置想很少,根据实际情况配置。
第一项配置优先级;
第二项配置LVGL使用RT-Thread任务的栈空间;
第三项配置,用于配置屏幕刷新速率,单位是ms。默认是5。其实没必要,因为SPI总线的刷新速度跟不上,对于GD32F427V,SPI最快也要二分频,连接到了APB1总线,所以最高频率为25MHz。这里设置20或者30都是可以的,本来刷新速率也跟不上。
lv_conf.h文件创建
lv_conf.h文件是用于配置LVGL中的一些功能是否使能的。模板中已经提供了,我们只需要复制过来,更新一下名字即可。
- 使能该文件
- 设置配置
默认配置就能够使用了,但是很多选择项都是打开的,很占用资源,可以对其中一些不使用的显示模块进行禁用,本文就不做过多说明。
注: 在编译的时候还是会报一些警告或者是编译错误,直接将lv_conf.h中报错的宏定义禁掉就可以了,因为很多都是重定义,在其他地方已经定义过了,这里无需再次定义,然后在此编译就不会报错了。
举例如下:
关于宏LV_DPI_DEF的计算公式如下:
DPI = 根号下(长边像素的平方+短边像素的平方)/英寸。
对于我的LCD,2.2寸,320X240,所以计算公式为
DPI = 根号下(320_320 + 240_240) / 2.2 ≈ 182.
到目前为止,整个移植过程都结束了,接下来就是写测试用例来测试移植是否成功了。
测试代码
如果用的是RT-Thread OS,官方已经提供了测试代码,如下,并且会自动参与编译,我们就无须手动创建了,没有使用RT-Thread需要手动创建一个文件,本文不做说明:
官方已经帮我们创建好了显示任务,我们不需要再次创建,只需要往里面添加显示内容即可。
禁掉其中无用的内容。
只需要修改lvgl_thread_entry文件。
禁掉lv_port_indev_init以及lv_user_gui_init;
lv_user_gui_init起止可以不用禁掉,在里面写我们的代码,但是不知道为什么我这边总是编译不通过,所以就禁掉了,然后添加了函数
Gui_LvglEntry,这个函数需要我们自己实现。
static void lvgl_thread_entry(void *parameter)
{
#if LV_USE_LOG
lv_log_register_print_cb(lv_rt_log);
#endif /* LV_USE_LOG */
lv_init();
lv_port_disp_init();
// lv_port_indev_init();
// lv_user_gui_init();
extern void Gui_LvglEntry(void);
Gui_LvglEntry();
/* handle the tasks of LVGL */
while(1)
{
lv_task_handler();
rt_thread_mdelay(PKG_LVGL_DISP_REFR_PERIOD);
}
}
实现Gui_LvglEntry
在application里面新建一个文件app_gui.c,然后新建一个函数Gui_lvgl_Test,内容如下:
该函数我实现了显示不同颜色的字符串;见效果展示。
/*
* @hehung
* 2022-12-13
* 转载请注明出处
* */
#include "lvgl.h"
static void Gui_lvgl_Test(void);
/* My GUI entry */
void Gui_LvglEntry(void)
{
Gui_lvgl_Test();
}
/* Lvgl test for porting to GD32F427V */
static void Gui_lvgl_Test(void)
{
lv_obj_t * label;
/*Create a label below the slider*/
label = lv_label_create(lv_scr_act());
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); /*Break the long lines*/
lv_label_set_recolor(label, true); /*Enable re-coloring by commands in the text*/
lv_label_set_text(label, "#0000ff GD32F427V-START#\n"
"#ff00ff aijishu.com#\n "
"#ff0000 Lvgl Porting Test#\n"
" --hehung");
lv_obj_set_width(label, 150); /*Set smaller width to make the lines wrap*/
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}
至此,整个LVGL移植过程结束。下面试验证。
验证
效果见下方。
试了一下刷新,速度还是比较慢,后期还需要优化,使用DMA应该会快一些。