显示对接
Read time: 4 minute(s)
主要包括三部分:
- 绘制 buffer 初始化。函数如下:
void lv_disp_draw_buf_init(lv_disp_draw_buf_t * draw_buf, void * buf1, void * buf2, uint32_t size_in_px_cnt)
- buf1:当为单缓冲或多缓冲的时候,都要设置此 buffer
- buf2:当选择双缓冲的时候,需要配置此 buffer,单缓冲不需要
- size_in_px_cnt: 以像素为单位的 buf 大小
- flush_cb 对接
下方以双缓冲为例说明 flush_cb 回调函数的处理流程。绘制模式有 refresh 和 direct_mode 两种:
-
全刷新模式,每一帧都刷新整个显示屏
在虚线框中为 cb 中处理部分,在全刷新的流程中,直接通过 pan_display 接口送当前绘制 buffer 到显示,然后等待 vsync 中断, 等到中断后,当前的绘制 buffer 就真正的在显示屏中显示出来,然后调用 ready 通知 LVGL 框架已经 flush 结束, 最后在 LVGL 框架中会进行绘制 buffer 的交换。
-
局部刷新,每一帧只刷新需要更新的无效区域(可以有多个无效区域)
上图中的示例,为了方便描述每一帧都有两个无效区域(invalid area0 和 area1),LVGL 可以支持更多的无效区域,到了最后一个无效区域, 说明当前帧的数据已经处理完,才把绘制 buffer 送显示,然后进行 buffer 交换
flush_cb 的实现代码 flush 如下:static void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t *color_p) { lv_disp_t * disp = _lv_refr_get_disp_refreshing(); lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); if (!disp->driver->direct_mode || draw_buf->flushing_last) { struct fb_var_screeninfo var = {0}; if (ioctl(g_fb, FBIOGET_VSCREENINFO, &var) < 0) { LV_LOG_WARN("ioctl FBIOGET_VSCREENINFO"); return; } if (color_p == (lv_color_t *)g_frame_buf[0]) { var.xoffset = 0; var.yoffset = 0; } else { var.xoffset = 0; var.yoffset = disp_drv.ver_res; } if (ioctl(g_fb, FBIOPAN_DISPLAY, &var) == 0) { int zero = 0; if (ioctl(g_fb, AICFB_WAIT_FOR_VSYNC, &zero) < 0) { LV_LOG_WARN("ioctl AICFB_WAIT_FOR_VSYNC fail"); return; } } else { LV_LOG_WARN("pan display err"); } if (drv->direct_mode == 1) { for (int i = 0; i < disp->inv_p; i++) { if (disp->inv_area_joined[i] == 0) { sync_disp_buf(drv, color_p, &disp->inv_areas[i]); } } } lv_disp_flush_ready(drv); } else { lv_disp_flush_ready(drv); } }
-
- 2D 硬件加速对接,主要对接 lv_draw_ctx_t 中的绘制函数
成员 说明 是否硬件加速 void *buf 当前要绘制的 buffer - const lv_area_t * clip_area 绘制区域裁剪(以屏幕为参考的绝对坐标) - void (*draw_rect)() 绘制矩形(包括圆角、阴影、渐变等) 否 void (*draw_arc)() 绘制弧形 否 void (*draw_img_decoded)() 绘制已经解码后的图像 是 lv_res_t (*draw_img)() 绘制图像(包括图片解码) 是 void (*draw_letter)() 绘制文字 否 void (*draw_line)() 绘制直线 否 void (*draw_polygon)() 绘制多边形 否 在 t(重定义了 t)结构体中包含 t 和 blend 函数:typedef struct { lv_draw_ctx_t base_draw; /** Fill an area of the destination buffer with a color*/ void (*blend)(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); } lv_draw_sw_ctx_t;
在 rect、draw_line 等操作的功能由多个步骤组成,虽然我们没有对这些接口进行硬件加速,但是这些操作的部分实现 会调用到 blend,我们对 blend 接口进行了硬件加速对接:void lv_draw_aic_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) { lv_draw_sw_init_ctx(drv, draw_ctx); lv_draw_aic_ctx_t * aic_draw_ctx = (lv_draw_aic_ctx_t *)draw_ctx; aic_draw_ctx->blend = lv_draw_aic_blend; aic_draw_ctx->base_draw.draw_img = lv_draw_aic_draw_img; aic_draw_ctx->base_draw.draw_img_decoded = lv_draw_aic_img_decoded; return; }
先调用 ctx 函数把所有绘制操作都初始化为软件实现,然后对可以硬件加速的接口重新实现, 覆盖原来的软件实现。
-
所有的显示相关功能都包含在 t 结构体中:
-
通过 init 来初始化 lv_disp_drv_t 结构体
-
通过 init 初始化绘制 buffer
-
通过回调 cb 来注册显示接口
-
通过 init 来注册 2D 硬件加速相关接口
-
通过 register 来注册 lv_disp_drv_t
在源文件 c 中的函数 lv_port_disp_init 配置刷新模式,局部刷新模式配置如下:disp_drv.full_refresh = 0; disp_drv.direct_mode = 1;
全刷新模式参数配置如下:disp_drv.full_refresh = 1; disp_drv.direct_mode = 0;
-