背景介绍
最近接触到了一个开源的显示框架lvgl,在跑起了demo的时刻,发现这确实是个酷酷的东西。
LVGL的作者是来自匈牙利的Gabor Kiss-Vamosikisvegabor,LVGL用C语言编写,以实现最大的兼容性(与C ++兼容),模拟器可在没有嵌入式硬件的PC上启动嵌入式GUI设计,同时LVGL作为一个图形库,它自带着接近三十多种小工具可以供开发者使用。这些强大的构建块按钮搭配上带有非常丝滑的动画以及可以做到平滑滚动的高级图形,同时兼具着不高的配置要求以及开源属性,显著的优势使得LVGL蔚然成风,成为广大开发者在选择GUI时的第一选择。
demo的样子
这流畅的动画和体面的输入,一个字–绝。
代码移植
这个项目的代码,是在官方的qq群里得到的,当时是移植好了框架并增加了显示部分。我所做的工作就是适配了自己的显示屏幕,并且增加了触摸屏的移植。
整体移植工作来看包括以下内容:
lvgl库的移植
LVGL 可在 GitHub 上获得:https://github.com/lvgl/lvgl。下载之后,内容如下
有些目录实际工作不需要,就可以不复制,整体移植过程主要是配置文件的修改,可以参考百问网上的说明《 Set-up a project(设置项目)》
修改lv_conf.h的名字,然后打开内部的宏定义,根据系统修改一些宏定义,其中我这里遇到了颜色不对的问题,可以修改这个宏定义。
记得之前做NES模拟器的时候,也遇到过这个,当时是修改了调色板的内容
显示与输入设备
这里比较关键的就是两个设备的接口对接,之前说到在example/porting目录下,提供了移植的demo
显示初始化函数 void lv_port_disp_init(void)
这个函数头有段注释,介绍了三种初始化的方法。
/**
* LVGL requires a buffer where it internally draws the widgets.
* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
* The buffer has to be greater than 1 display row
*
* There are 3 buffering configurations:
* 1. Create ONE buffer:
* LVGL will draw the display's content here and writes it to your display
*
* 2. Create TWO buffer:
* 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.
*
* 3. Double buffering
* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
* This way LVGL will always provide the whole rendered screen in `flush_cb`
* and you only need to change the frame buffer's address.
*/
其实我猜也就是根据系统资源来决定的。毕竟也就是预留的内存大小不同。
这里采用了第二种方式
void lv_port_disp_init(void)
{
static lv_disp_draw_buf_t draw_buf_dsc_2;
static lv_color_t buf_2_1[LV_HOR_RES_MAX * 60]; /*A buffer for 10 rows*/
static lv_color_t buf_2_2[LV_HOR_RES_MAX * 60]; /*An other buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, 60 * LV_HOR_RES_MAX); /*Initialize the display buffer*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
//设置长宽
disp_drv.hor_res = LV_HOR_RES_MAX;
disp_drv.ver_res = LV_VER_RES_MAX;
//设置显示回调
disp_drv.flush_cb = disp_flush;
disp_drv.draw_buf = &draw_buf_dsc_2;
//注册设备
lv_disp_drv_register(&disp_drv);
}
然后就是刷新显示屏函数 static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
uint16_t width, height = 0;
width = (area->x2 - area->x1) + 1;
height = (area->y2 - area->y1) + 1;
//调用LCD显示图片的方法
extern void LCD_ShowPicture(uint16_t x, uint16_t y, uint16_t width, uint16_t hight, uint8_t *data);
LCD_ShowPicture(area->x1,area->y1,width,height,(uint8_t *)color_p);
lv_disp_flush_ready(disp_drv);
}
输入控制(触摸屏)
移植范例中提供了4类设备,我们选择第一个触摸屏。
这里需要4个接口
static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);
- 初始化 static void touchpad_init(void);接口我们可以为空,将初始化放在系统的初始化中。
然后下面三个函数是一个整体,touchpad_read是分别调用下面两个。我们只需要修改下面两个即可。
- 检测是否按下static bool touchpad_is_pressed(void);
结合我们前一篇文章《单片机—HLK-W801驱动触摸屏》,我们可以通过判断PEN引脚的高低电平,查看是否有触摸发生。
- 获取坐标static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);
这里就直接使用我们封装好的就可以了。
核心代码
static bool touchpad_is_pressed(void)
{
return key_flag;
}
/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
HAL_Delay(20);
if(HAL_GPIO_ReadPin(P_TOUCH_PORT, PEN) == GPIO_PIN_RESET)
{
int a,b;
while(1)
{
if(get_map_x_y(&a,&b)!=1)
{
continue;
}
break;
}
*x=a;
*y=240-b;
printf("a:%d,b:%d,x:%d,y:%d\n",a,b,(*x),(*y));
}
if(HAL_GPIO_ReadPin(P_TOUCH_PORT, PEN) != GPIO_PIN_RESET)
{
key_flag = 0;
}
}
遇到坐标漂移或者其他问题,可以通过过滤一些夸张坐标的方式,进行人为干预。
demo测试
在demos中,包含了测试代码,可以测试一些控件的显示。
拷贝到工程中,进行编译。记住打开LV_USE_DEMO_WIDGETS这个宏,或者注释掉都可以。整个部分,只提供了一个接口
lv_demo_widgets();
在所有初始化完成之后,就可以调用了。
主函数
最后,就是整个lvgl在主函数中的初始化,及运行。
int main(void)
{
SystemClock_Config(CPU_CLK_240M);
HAL_Init();
//屏幕初始化
LCD_GPIO_Init();
LCD_Init();
//触摸屏初始化
TOUCH_GPIO_Init();
//lvgl初始化
lv_init();
//显示接口对接初始化
lv_port_disp_init();
//触摸输入接口对接初始化
lv_port_indev_init();
//测试demo
lv_demo_widgets();
//开始刷新
while (1)
{
lv_tick_inc(10);
lv_task_handler();
HAL_Delay(5);
}
}
结束语
过了2月2,各种抬头了。
疫情抬头,一天几百人,看来以后真要与病毒共存了。
油价也抬头了,几天就进一大关。
2022也是不平凡的一年了。
还是继续好好学习吧。