显示对接
主要包括三部分:
-
绘制 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;
