显示对接
主要包括三部分:
-
绘制 buffer 初始化
-
flush_cb 对接
-
2D 硬件加速对接
绘制 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 两种:
-
全刷新模式,每一帧都刷新整个显示屏
图 2. 全刷新模式 在虚线框中为 cb 中处理部分,在全刷新的流程中,直接通过 pan_display 接口送当前绘制 buffer 到显示,然后等待 vsync 中断, 等到中断后,当前的绘制 buffer 就真正的在显示屏中显示出来,然后调用 ready 通知 LVGL 框架已经 flush 结束, 最后在 LVGL 框架中会进行绘制 buffer 的交换。
-
局部刷新,每一帧只刷新需要更新的无效区域(可以有多个无效区域)
图 3. 无效区域 图 4. 局部刷新模式 上图中的示例,为了方便描述每一帧都有两个无效区域(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) { int index = 0; 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) { if (disp->driver->direct_mode) aicos_dcache_clean_invalid_range((unsigned long *)info.framebuffer, (unsigned long)info.smem_len * 2); else aicos_dcache_clean_invalid_range((unsigned long *)color_p, (unsigned long)info.smem_len); if ((void *)color_p == (void *)info.framebuffer) index = 0; else index = 1; mpp_fb_ioctl(g_fb, AICFB_PAN_DISPLAY , &index); mpp_fb_ioctl(g_fb, AICFB_WAIT_FOR_VSYNC, 0); 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 硬件加速对接
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)() | 绘制多边形 | 否 |
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;
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
static lv_disp_drv_t disp_drv; void lv_port_disp_init(void) { void *buf1 = RT_NULL; void *buf2 = RT_NULL; uint32_t fb_Size; rt_err_t result; g_fb = mpp_fb_open(); if (g_fb == 0) { LOG_E("can't find aic framebuffer device!"); return; } result = mpp_fb_ioctl(g_fb, AICFB_GET_SCREENINFO, &info); if (result != RT_EOK) { LOG_E("get device fb info failed!"); return; } g_ge = mpp_ge_open(); if (!g_ge) { LOG_E("ge open fail\n"); return; } fb_Size = info.height * info.stride; buf1 = (void *)info.framebuffer; buf2 = (void *)((uint8_t *)info.framebuffer + fb_Size); lv_disp_draw_buf_init(&disp_buf, buf2, buf1, info.width * info.height); lv_disp_drv_init(&disp_drv); /*Set a display buffer*/ disp_drv.draw_buf = &disp_buf; /*Set the resolution of the display*/ disp_drv.hor_res = info.width; disp_drv.ver_res = info.height; disp_drv.full_refresh = 0; disp_drv.direct_mode = 1; disp_drv.flush_cb = fbdev_flush; disp_drv.draw_ctx_init = lv_draw_aic_ctx_init; disp_drv.draw_ctx_deinit = lv_draw_aic_ctx_deinit; disp_drv.draw_ctx_size = sizeof(lv_draw_aic_ctx_t); /*Finally register the driver*/ lv_disp_drv_register(&disp_drv); }
disp_drv.full_refresh = 0; disp_drv.direct_mode = 1;
disp_drv.full_refresh = 1; disp_drv.direct_mode = 0;