一、Intro
LVGL官方提供了lv_port_disp_template.c文件,包含显示初始化函数static void disp_init(void);
以及显示刷新函数static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
我们移植LVGL显示驱动的主要工作就是将上述两个函数用自己的屏幕驱动实现,比如我这里采用的是SPI接口的ST7735驱动。
二、移植过程
1.实现显示刷新函数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)
{
/* 打点填充 */
// int32_t x;
// int32_t y;
// 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)*/
// color_p++;
// }
// }
/* 区域填充 */
lcd_color_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t *)color_p);
/* 刷新完毕 */
lv_disp_flush_ready(disp_drv);
}
对于disp_flush
函数,有两种实现方法。一种是打点填充,这种方法每次写入输入都需要指定对应的地址,效率比较低。另一种方法则是区域填充,即在写入之前先指定起始和结束地址,然后一次性写入。推荐使用第二种方法来实现显示刷新操作。
待实现的填充函数lcd_color_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t *)color_p)
有5个参数,分别为起点xy坐标、终点xy坐标以及对应的颜色数组。lv_color_t
是一个typedef定义的类型,它被定义为union类型lv_color16_t
。lv_color16_t
类型如下所示。
typedef union {
struct {
#if LV_COLOR_16_SWAP == 0
uint16_t blue : 5;
uint16_t green : 6;
uint16_t red : 5;
#else
uint16_t green_h : 3;
uint16_t red : 5;
uint16_t blue : 5;
uint16_t green_l : 3;
#endif
} ch;
uint16_t full;
} lv_color16_t;
从lv_color16_t
的定义中我们可以知道其大小就相当于一个uint16_t类型。lv_color_t * color_p
指向了一段连续的颜色数组空间,我们可以使用color_p->full
取出每个对应的颜色数据,通过color_p++
来遍历操作。当然,也可以将其强制转换为uint16_t类型(uint16_t *) color_p
,直接使用*color_p
来取出对应的颜色数据。
2.在disp_init()
中放入我们的LCD初始化函数
static void disp_init(void)
{
/*You code here*/
lcd_init(); /* 初始化LCD */
}
3.修改宏定义适配自己的屏幕大小
#define MY_DISP_HOR_RES (128) /* 屏幕宽度 */
#define MY_DISP_VER_RES (160) /* 屏幕高度 */
三、问题
本来以为到这里就万事大吉了,没想到跑起来后按钮控件直接裂开(字面意义上的)。然后,才发现在void lv_port_disp_init(void)
函数中同样需要修改屏幕分辨率,用来 初始化对象参数。
disp_drv.hor_res = MY_DISP_HOR_RES;
disp_drv.ver_res = MY_DISP_VER_RES;
这样修改后显示刷新就正常了。
(注:如果没有正确初始化分辨率,采用打点填充的方式,控件可能显示正常,但坐标会异常)