LVGL官方文档-7.11.0-4-Porting-Display interface

为了配置一个Display,一个lv_disp_buf_t类型的变量和一个lv_disp_drv_t类型的变量必须要初始化。

  • lv_disp_buf_t 包含内部的图像buffer
  • lv_disp_drv_t 包含与Display交互的回调函数,并进行绘图相关的操作

Display buffer

lv_disp_buf_t可以像这样初始化:

/*A static or global variable to store the buffers*/
static lv_disp_buf_t disp_buf;

/*Static or global buffer(s). The second buffer is optional*/
static lv_color_t buf_1[MY_DISP_HOR_RES * 10];
static lv_color_t buf_2[MY_DISP_HOR_RES * 10];

/*Initialize `disp_buf` with the buffer(s) */
lv_disp_buf_init(&disp_buf, buf_1, buf_2, MY_DISP_HOR_RES*10);

关于buffer的大小,有三种可能的配置:

  1. 单buffer LVGL绘制屏幕的内容,然后发送给display。buffer可以比屏幕小。这种情况下,大的区域会被分成小块来绘制。如果只有小块的区域发生了变化(例如按钮被按下),那么只有这些区域会刷新。
  2. 双非屏幕大小的buffer 有了两个buffer,LVGL就可以在其中一个buffer绘制的同时,在后台将另一个buffer中的内容发送给Display。为了能够让绘制和发送同时运行,需要使用DMA或者其他硬件。这种情况下,渲染和刷新是并行的。与单buffer类似,如果buffer比需要绘制的区域小,LVGL也是分块绘制的。
  3. 双屏幕大小的buffer 与上一种情况相比,LVGL总是一次提供整个屏幕的内容,而不是分块。这种情况下,驱动可以仅仅是将frame buffer的地址改成从LVGL接收到的buffer的地址。因此,在MCU有LCD/TFT接口,frame buffer位于RAM的情况下,这种方式是最佳的。

可以用benchmask来测试你显示配置的性能。

Display driver

一旦buffer初始化完成,就需要初始化Display Driver了。在最简单的情况下,只需要设置lv_disp_drv_t的下述两个域:

  • buffer 指向lv_disp_buf_t变量的指针
  • flush_cb 一个用于将buffer的内容复制到Display特定区域的回调函数。lv_disp_flush_ready()需要在回调函数的最后调用。LVGL可能会分块渲染屏幕,因此flush_ch可能会被多次调用。可以调用lv_disp_flush_is_last()来查看当前是否是渲染的最后一块。

下面是一些可选的域:

  • hor_res Display的水平分辨率。默认值是lv_conf.h中定义的LV_HOR_RES_MAX。
  • ver_res Display的垂直分辨率。默认值是lv_conf.h中定义的LV_VER_RES_MAX。
  • color_chroma_key 透明图像的颜色。默认值是lv_conf.h中定义的LV_COLOR_TRANSP。
  • user_data driver的用户数据,其类型可以在lv_conf.h中修改。
  • anti-aliasing 是否使用反锯齿(平滑边缘)。默认值是lv_conf.h中定义的LV_ANTIALIAS。
  • rotatedsw_rotate 见rotation这一章节。
  • screen_transp 如果设置为1,屏幕就能有透明或者半透明的style。lv_conf.h中的LV_COLOR_SCREEN_TRANSP必须要打开。

为了使用GPU,可以使用下面的这些回调:

  • gpu_fill_cb 用颜色填充内存中的一块区域。
  • gpu_blent_cb 用不透明度渲染两个内存buffers。
  • gpu_wait_cb 当GPU还在处理LVGL数据的时候,如果任何GPU函数返回了,就会使用这个函数以确保GPU渲染准备就绪。

需要注意的是,这些函数需要将图形绘制到内存,而不是display。

当使用单色屏,灰度屏和其他非标准RGB屏幕的时候,还有一些可选的回调能让工作更简单。

  • rounder_cb 改变区域的坐标以重新绘制。例如,2X2的像素点可以变换成2X8。如果显示控制器只能刷新刷新固定高度或者固定宽度区域的时候,这个回调就会有用(例如8像素高的单色屏)。
  • set_px_cb 写display buffer的定制函数。当display是特殊颜色格式的时候,可以用于存储像素。(例如1位的单色屏,2位的灰度屏)。这种情况下,lv_disp_buf_t中的buffer大小只需要是给定区域的数据量大小。这个回调在“双屏幕大小的buffer”这一配置下不生效。
  • monitor_cb 从这个回调中可以知道多长时间刷新了多少数据。
  • clean_dcache_cb 清除display相关的任意cache。

为了设置lv_disp_drv_t类型的变量的域,这个变量首先要用lv_disp_drv_init(&disp_drv)来初始化,最后要用lv_disp_drv_register(&disp_drv)来注册。

以上这些组合在一起,是这个样子的:

lv_disp_drv_t disp_drv;                 /*A variable to hold the drivers. Can be local variable*/
lv_disp_drv_init(&disp_drv);            /*Basic initialization*/
disp_drv.buffer = &disp_buf;            /*Set an initialized buffer*/
disp_drv.flush_cb = my_flush_cb;        /*Set a flush callback to draw to the display*/
lv_disp_t * disp;
disp = lv_disp_drv_register(&disp_drv); /*Register the driver and save the created display objects*/

下面是一些回调的示例:

void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
    int32_t x, y;
    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            put_px(x, y, *color_p)
            color_p++;
        }
    }

    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);
}

void my_gpu_fill_cb(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, const lv_area_t * dest_area, const lv_area_t * fill_area, lv_color_t color);
{
    /*It's an example code which should be done by your GPU*/
    uint32_t x, y;
    dest_buf += dest_width * fill_area->y1; /*Go to the first line*/

    for(y = fill_area->y1; y < fill_area->y2; y++) {
        for(x = fill_area->x1; x < fill_area->x2; x++) {
            dest_buf[x] = color;
        }
        dest_buf+=dest_width;    /*Go to the next line*/
    }
}

void my_gpu_blend_cb(lv_disp_drv_t * disp_drv, lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa)
{
    /*It's an example code which should be done by your GPU*/
    uint32_t i;
    for(i = 0; i < length; i++) {
        dest[i] = lv_color_mix(dest[i], src[i], opa);
    }
}

void my_rounder_cb(lv_disp_drv_t * disp_drv, lv_area_t * area)
{
  /* Update the areas as needed. Can be only larger.
   * For example to always have lines 8 px height:*/
   area->y1 = area->y1 & 0x07;
   area->y2 = (area->y2 & 0x07) + 8;
}

void my_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa)
{
    /* Write to the buffer as required for the display.
     * Write only 1-bit for monochrome displays mapped vertically:*/
 buf += buf_w * (y >> 3) + x;
 if(lv_color_brightness(color) > 128) (*buf) |= (1 << (y % 8));
 else (*buf) &= ~(1 << (y % 8));
}

void my_monitor_cb(lv_disp_drv_t * disp_drv, uint32_t time, uint32_t px)
{
  printf("%d px refreshed in %d ms\n", time, ms);
}

void my_clean_dcache_cb(lv_disp_drv_t * disp_drv, uint32)
{
  /* Example for Cortex-M (CMSIS) */
  SCB_CleanInvalidateDCache();
}

Rotation

LVGL支持以90度为增量的屏幕旋转。可以选择使用软件旋转或者硬件旋转。

如果选择软件旋转(sw_rotate标志位设置为1),LVGL会自动进行旋转。对驱动来说,屏幕的宽度和高度没有改变。正常的将像素刷新到display即可。软件旋转不需要改flush_cb回调。

软件旋转有可见的性能消耗,所以我们也需要使用硬件旋转。在这一模式下,LVGL就好像交换了屏幕的宽度和高度一样,将图像绘制到buffer。你需要自己来实现旋转像素。

初始化时候的默认旋转,可以用rotated标志位来设置。可用的选项有LV_DISP_ROT_NONE,LV_DISP_ROT_90,LV_DISP_ROT_180和LV_DISP_ROT_270。旋转的角度取决于硬件设备顺时针旋转的方向。这样LV_DISP_ROT_90意味着硬件设备顺时针旋转了90度,那么显示就要逆时针旋转90度。

对于从7.10.0以及更老版本升级上来的用户,上述的旋转枚举值与老系统用0和1来表示是否旋转90度是兼容的。所以老的代码也能使用。从兼容性考虑,软件旋转默认也是关闭的。

显示的旋转也可以在运行时用lv_disp_set_rotation(disp, rot)接口来改变。

支持软件旋转是一个新的特性,所以在你的配置下可能会存在bug。如果你遇到了bug,请在GitHub上提Issue。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值