Edit online

显示对接

主要包括三部分:

  1. 绘制 buffer 初始化

  2. flush_cb 对接

  3. 2D 硬件加速对接

绘制 buffer 初始化

绘制 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 大小

../images/double_frame1.png
1. 双缓冲

flush_cb 对接

flush_cb 回调函数的处理流程,我们以双缓冲为例进行说明,绘制模式有 refresh 和 direct_mode 两种:

  1. 全刷新模式,每一帧都刷新整个显示屏

    ../images/full_flush_cb1.png
    2. 全刷新模式

    在虚线框中为 cb 中处理部分,在全刷新的流程中,直接通过 pan_display 接口送当前绘制 buffer 到显示,然后等待 vsync 中断, 等到中断后,当前的绘制 buffer 就真正的在显示屏中显示出来,然后调用 ready 通知 LVGL 框架已经 flush 结束, 最后在 LVGL 框架中会进行绘制 buffer 的交换。

  2. 局部刷新,每一帧只刷新需要更新的无效区域(可以有多个无效区域)

    ../images/invalid_area1.png
    3. 无效区域
    ../images/direct_flush_cb1.png
    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)() 绘制多边形
在 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 结构体中:

  1. 通过 init 来初始化 lv_disp_drv_t 结构体

  2. 通过 init 初始化绘制 buffer

  3. 通过回调 cb 来注册显示接口

  4. 通过 init 来注册 2D 硬件加速相关接口

  5. 通过 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;