[English]

Display interface(显示接口)

显示原文

To create a display for LVGL call lv_display_t * display = lv_display_create(hor_res, ver_res). You can create a multiple displays and a different driver for each (see below),


要为 LVGL 创建显示,请调用 lv_display_t * display = lv_display_create(hor_res, ver_res)。您可以创建多个显示器和每个显示器的不同驱动程序(见下文),

Basic setup(基本设置)

显示原文

Draw buffer(s) are simple array(s) that LVGL uses to render the screen's content. Once rendering is ready the content of the draw buffer is sent to the display using the flush_cb function.


绘制缓冲区是 LVGL 用来渲染屏幕的简单数组 内容。渲染准备就绪后,将发送绘制缓冲区的内容使用 flush_cb 功能显示。

flush_cb

显示原文

An example flush_cb looks like this:

void my_flush_cb(lv_display_t * display, const lv_area_t * area, void * px_map)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one
     *`put_px` is just an example, it needs to be implemented by you.*/
    uint16_t * buf16 = (uint16_t *)px_map; /*Let's say it's a 16 bit (RGB565) display*/
    int32_t x, y;
    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            put_px(x, y, *buf16);
            buf16++;
        }
    }

    /* IMPORTANT!!!
     * Inform LVGL that you are ready with the flushing and buf is not used anymore*/
    lv_display_flush_ready(disp);
}

Use lv_display_set_flush_cb(disp, my_flush_cb) to set a new flush_cb.

lv_display_flush_ready(disp) needs to be called when flushing is ready to inform LVGL the buffer is not used anymore by the driver and it can render new content into it.

LVGL might render the screen in multiple chunks and therefore call flush_cb multiple times. To see if the current one is the last chunk of rendering use lv_display_flush_is_last(display).


示例 flush_cb 如下所示:

void my_flush_cb(lv_display_t * display, const lv_area_t * area, void * px_map)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one
     *`put_px` is just an example, it needs to be implemented by you.*/
    uint16_t * buf16 = (uint16_t *)px_map; /*Let's say it's a 16 bit (RGB565) display*/
    int32_t x, y;
    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            put_px(x, y, *buf16);
            buf16++;
        }
    }

    /* IMPORTANT!!!
     * Inform LVGL that you are ready with the flushing and buf is not used anymore*/
    lv_display_flush_ready(disp);
}

使用 lv_display_set_flush_cb(disp, my_flush_cb) 设置新的 flush_cb

lv_display_flush_ready(disp) 需要在冲洗准备就绪时调用通知 LVGL 缓冲区不再被驱动程序使用,它可以将新内容渲染到其中。

LVGL 可能会在多个块中呈现屏幕,因此会多次调用 flush_cb。查看当前块是否是最后一个块 渲染使用的 lv_display_flush_is_last(display)

Draw buffers(绘制缓冲区)

显示原文

The draw buffers can be set with lv_display_set_buffers(display, buf1, buf2, buf_size_byte, render_mode)

  • buf1 a buffer where LVGL can render

  • buf2 a second optional buffer (see more details below)

  • buf_size_byte size of the buffer(s) in bytes

  • render_mode

    • LV_DISPLAY_RENDER_MODE_PARTIAL Use the buffer(s) to render the screen in smaller parts. This way the buffers can be smaller then the display to save RAM. At least 1/10 screen size buffer(s) are recommended. In flush_cb the rendered images needs to be copied to the given area of the display. In this mode if a button is pressed only the button's area will be redrawn.

    • LV_DISPLAY_RENDER_MODE_DIRECT The buffer(s) has to be screen sized and LVGL will render into the correct location of the buffer. This way the buffer always contain the whole image. If two buffer are used the rendered areas are automatically copied to the other buffer after flushing. Due to this in flush_cb typically only a frame buffer address needs to be changed. If a button is pressed only the button's area will be redrawn.

    • LV_DISPLAY_RENDER_MODE_FULL The buffer(s) has to be screen sized and LVGL will always redraw the whole screen even if only 1 pixel has been changed. If two screen sized draw buffers are provided, LVGL's display handling works like "traditional" double buffering. This means the flush_cb callback only has to update the address of the frame buffer to the px_map parameter.

Example:


绘制缓冲区可以用 :cpp:expr:`lv_display_set_buffers(display, buf1, buf2, buf_size_byte, render_mode)`进行设置

  • buf1 LVGL 可以渲染的缓冲区

  • buf2 第二个可选缓冲区(请参阅下面的更多详细信息)

  • buf_size_byte 缓冲区的大小(以字节为单位)

  • render_mode

  • LV_DISPLAY_RENDER_MODE_PARTIAL 使用缓冲区呈现屏幕以较小的部分进行。这样,缓冲区可以更小显示用于节省 RAM。至少 1/10 的屏幕大小缓冲区是推荐。在 flush_cb 渲染图像中需要复制到显示器的给定区域。在此模式下,如果按下按钮 只有按钮的区域将被重新绘制。

  • LV_DISPLAY_RENDER_MODE_DIRECT 缓冲区必须是 screen size 和 LVGL 将渲染到缓冲区。这样,缓冲区始终包含整个图像。如果两个缓冲区被使用,渲染区域会自动复制到冲洗后的其他缓冲区。因此,由于 ``flush_cb``中的这一点,通常只需更改帧缓冲器地址。如果按下按钮 只有按钮的区域将被重新绘制。

  • LV_DISPLAY_RENDER_MODE_FULL 缓冲区必须是 screen size 和 LVGL 将始终重绘整个屏幕,即使只有 1 像素已更改。如果两个屏幕大小的绘制缓冲区 前提是LVGL的显示处理工作方式类似于“传统”双缓冲。这意味着 flush_cb 回调只需要更新帧缓冲区到 px_map 参数的地址。

例如:

static uint16_t buf[LCD_HOR_RES * LCD_VER_RES / 10];
lv_display_set_buffers(disp, buf, NULL, sizeof(buf), LV_DISPLAY_RENDER_MODE_PARTIAL);

One buffer(一个缓冲区)

显示原文

If only one buffer is used LVGL draws the content of the screen into that draw buffer and sends it to the display via the flush_cb. LVGL then needs to wait until lv_display_flush_ready is called (that is the content of the buffer is sent to the display) before drawing something new into it.


如果只使用一个缓冲区,LVGL 将屏幕内容绘制到该绘制缓冲区中并通过 flush_cb 将其发送到显示器。 然后需要等待,直到调用 :cpp:expr:`lv_display_flush_ready`(即缓冲区的内容被发送到显示),然后在其中绘制新内容。

Two buffers(两个缓冲区)

显示原文

If two buffers are used LVGL can draw into one buffer while the content of the other buffer is sent to the display in the background. DMA or other hardware should be used to transfer data to the display so the MCU can continue drawing. This way, the rendering and refreshing of the display become parallel operations.


如果使用两个缓冲区,LVGL 可以绘制到一个缓冲区中,而另一个缓冲区的内容被发送到后台显示。 应使用 DMA 或其他硬件将数据传输到显示器,因此MCU可以继续绘制。 这样,显示的渲染和刷新变得并行。

Advanced options(高级选项)

Resolution(分辨率)

显示原文

To set the resolution of the display after creation use lv_display_set_resolution(display, hor_res, ver_res)

It's not mandatory to use the whole display for LVGL, however in some cases the physical resolution is important. For example the touchpad still sees the whole resolution and the values needs to be converted to the active LVGL display area. So the physical resolution and the offset of the active area can be set with lv_display_set_physical_resolution(disp, hor_res, ver_res) and lv_display_set_offset(disp, x, y)


要在创建后设置显示器的分辨率,请使用 lv_display_set_resolution(display, hor_res, ver_res)

对于 LVGL,不强制要求使用整个显示器,但在某些物理分辨率很重要。例如触摸板仍然看到整个分辨率,并且需要将值转换为活动 LVGL 显示区域。所以物理分辨率和偏移量的有效区域可以用 lv_display_set_physical_resolution(disp, hor_res, ver_res)lv_display_set_offset(disp, x, y) 进行设置

Flush wait callback(刷新等待回调)

显示原文

By using lv_display_flush_ready LVGL will spin in a loop while waiting for flushing.

However with the help of lv_display_set_flush_wait_cb a custom wait callback be set for flushing. This callback can use a semaphore, mutex, or anything else to optimize while the waiting for flush.

If flush_wait_cb is not set, LVGL assume that lv_display_flush_ready is used.


通过使用 lv_display_flush_ready 在等待冲洗时LVGL将在循环中旋转。

但是,借助 lv_display_set_flush_wait_cb 自定义 等待回调设置为刷新。此回调可以使用信号量、互斥量、 或其他任何在等待冲洗时进行优化的内容。

如果 flush_wait_cb 未设置,则 LVGL 假定使用了 lv_display_flush_ready

Rotation(旋转)

显示原文

LVGL supports rotation of the display in 90 degree increments. You can select whether you would like software rotation or hardware rotation.

The orientation of the display can be changed with lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_0/90/180/270). LVGL will swap the horizontal and vertical resolutions internally according to the set degree. When changing the rotation LV_EVENT_SIZE_CHANGED is sent to the display to allow reconfiguring the hardware. In lack of hardware display rotation support lv_draw_sw_rotate can be used to rotate the buffer in the flush_cb.

lv_display_rotate_area(display, &area) rotates the rendered area according to the current rotation settings of the display.

Note that in LV_DISPLAY_RENDER_MODE_DIRECT the small changed areas are rendered directly in the frame buffer so they cannot be rotated later. Therefore in direct mode only the whole frame buffer can be rotated. The same is true for LV_DISPLAY_RENDER_MODE_FULL.

In the case of LV_DISPLAY_RENDER_MODE_PARTIAL the small rendered areas can be rotated on their own before flushing to the frame buffer.


LVGL 支持以 90 度为增量旋转显示器。您可以选择是要软件轮换还是硬件轮换。

可以使用 lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_0/90/180/270) 更改显示器的方向。 LVGL 将在内部交换水平和垂直分辨率 根据设定的度数。更改旋转时,LV_EVENT_SIZE_CHANGED 被发送到显示器以允许重新配置硬件。在缺少硬件显示旋转支持的情况下,可以使用 lv_draw_sw_rotate 来旋转 flush_cb 中的缓冲区。

lv_display_rotate_area(display, &area) 旋转渲染区域根据显示器当前的旋转设置。

请注意 LV_DISPLAY_RENDER_MODE_DIRECT 中的小变化区域直接在帧缓冲区中渲染,因此无法后来轮换了。 因此,在直接模式下,只能旋转整个帧缓冲区。对于 LV_DISPLAY_RENDER_MODE_FULL 也是如此。

LV_DISPLAY_RENDER_MODE_PARTIAL 的情况下,小的渲染区域在刷新到帧缓冲区之前可以自行旋转。

Color format(颜色格式)

显示原文

The default color format of the display is set according to LV_COLOR_DEPTH (see lv_conf.h)

  • LV_COLOR_DEPTH 32: XRGB8888 (4 bytes/pixel)

  • LV_COLOR_DEPTH 24: RGB888 (3 bytes/pixel)

  • LV_COLOR_DEPTH 16: RGB565 (2 bytes/pixel)

  • LV_COLOR_DEPTH 8: L8 (1 bytes/pixel)

  • LV_COLOR_DEPTH 1: I1 (1 bit/pixel) Only support for horizontal mapped buffers. See :refr:`monochrome` for more details:

The color_format can be changed with lv_display_set_color_depth(display, LV_COLOR_FORMAT_...). Besides the default value LV_COLOR_FORMAT_ARGB8888 can be used as a well.

It's very important that draw buffer(s) should be large enough for any selected color format.


显示器的默认颜色格式是根据 LV_COLOR_DEPTH (请参阅 lv_conf.h)

  • LV_COLOR_DEPTH 32: XRGB8888(4 字节/像素)

  • LV_COLOR_DEPTH 24: RGB888(3 字节/像素)

  • LV_COLOR_DEPTH 16: RGB565(2字节/像素)

  • LV_COLOR_DEPTH 8:L8(1 字节/像素)

  • LV_COLOR_DEPTH 1:I1(每个像素1比特)仅支持水平映射缓冲区。有关更多详细信息,请参阅 :refr:`monochrome` :

颜色格式可以通过调用 lv_display_set_color_depth(display, LV_COLOR_FORMAT_...) 来更改。除了默认值 LV_COLOR_FORMAT_ARGB8888 也可以使用。

非常重要的是,绘图缓冲区(或缓冲区)的大小对于任何选定的颜色格式来说都应该是足够大的。

Swap endianness(交换字节序)

显示原文

In case of RGB565 color format it might be required to swap the 2 bytes because the SPI, I2C or 8 bit parallel port periphery sends them in the wrong order.

The ideal solution is configure the hardware to handle the 16 bit data with different byte order, however if it's not possible lv_draw_sw_rgb565_swap(buf, buf_size_in_px) can be called in the flush_cb to swap the bytes.

If you wish you can also write your own function, or use assembly instructions for the fastest possible byte swapping.

Note that this is not about swapping the Red and Blue channel but converting

RRRRR GGG | GGG BBBBB

to

GGG BBBBB | RRRRR GGG.


如果是 RGB565 颜色格式,则可能需要交换 2 个字节 因为 SPI、I2C 或 8 位并行端口外设以错误的顺序发送它们。

理想的解决方案是配置硬件以处理具有不同字节顺序的 16 位数据, 但是,如果不可能,可以在 flush_cb 中调用 lv_draw_sw_rgb565_swap(buf, buf_size_in_px) 来交换字节。

如果你愿意,你也可以编写自己的函数,或者使用汇编指令来 尽可能快的字节交换。

请注意,这不是交换红色和蓝色通道,而是转换

RRRRR GGG | GGG BBBBB

GGG BBBBB | RRRRR GGG.

Monochrome Displays(单色显示器)

显示原文

LVGL supports rendering directly in a 1-bit format for monochrome displays. To enable it, set LV_COLOR_DEPTH 1 or use lv_display_set_color_format(display, LV_COLOR_FORMAT_I1).

The LV_COLOR_FORMAT_I1 format assumes that bytes are mapped to rows (i.e., the bits of a byte are written next to each other). The order of bits is MSB first, which means:

Ensure that the LCD controller is configured accordingly.

Internally, LVGL rounds the redrawn areas to byte boundaries. Therefore, updated areas will:

  • Start on an Nx8 coordinate.

  • End on an Nx8 - 1 coordinate.

When setting up the buffers for rendering (lv_display_set_buffers()), make the buffer 8 bytes larger. This is necessary because LVGL reserves 2 x 4 bytes in the buffer, as these are assumed to be used as a palette.

To skip the palette, include the following line in your flush_cb function: px_map += 8.

As usual, monochrome displays support partial, full, and direct rendering modes as well. In full and direct modes, the buffer size should be large enough for the whole screen, meaning (horizontal_resolution x vertical_resolution / 8) + 8 bytes. As LVGL can not handle fractional width make sure to round the horizontal resolution to 8- (For example 90 to 96)


LVGL 支持直接以 1 位格式渲染,适用于单色显示器。 要启用它,设置 LV_COLOR_DEPTH 1 或使用 lv_display_set_color_format(display, LV_COLOR_FORMAT_I1)

LV_COLOR_FORMAT_I1 格式假设字节映射到行(即,一个字节的位彼此相邻)。

位的顺序是首先为 MSB(最高位有效),这意味着:

请确保 LCD 控制器相应地配置。

在内部,LVGL 将重绘区域四舍五入到字节边界。因此,更新区域将:

  • Nx8 坐标开始。

  • Nx8 - 1 坐标结束。

在设置渲染缓冲区 (lv_display_set_buffers()) 时,使缓冲区增大 8 字节。 这是必要的,因为 LVGL 保留缓冲区中的 2 x 4 字节,这些字节假定用作调色板。

要跳过调色板,在您的 flush_cb 函数中包含以下行: px_map += 8

像往常一样,单色显示器也支持部分、全屏和直接渲染模式。 在全屏和直接模式下,缓冲区大小应足够容纳整个屏幕,即 (水平分辨率 x 垂直分辨率 / 8)+ 8 字节。 由于 LVGL 不能处理分数宽度,请确保将水平分辨率四舍五入到 8 的倍数 (例如,90 四舍五入到 96)

User data(用户数据)

显示原文

With lv_display_set_user_data(disp, p) a pointer to a custom data can be stored in display object.


使用 lv_display_set_user_data(disp, p) 指向自定义数据的指针可以 存储在显示对象中。

Decoupling the display refresh timer

显示原文

Normally the dirty (a.k.a invalid) areas are checked and redrawn in every LV_DEF_REFR_PERIOD milliseconds (set in lv_conf.h). However, in some cases you might need more control on when the display refreshing happen, for example to synchronize rendering with VSYNC or the TE signal.

You can do this in the following way:

/*Delete the original display refresh timer*/
lv_display_delete_refr_timer(disp);

/*Call this anywhere you want to refresh the dirty areas*/
_lv_display_refr_timer(NULL);

If you have multiple displays call lv_display_set_default(disp1) to select the display to refresh before _lv_display_refr_timer(NULL).

备注

that lv_timer_handler() and _lv_display_refr_timer() cannot run at the same time.

If the performance monitor is enabled, the value of LV_DEF_REFR_PERIOD needs to be set to be consistent with the refresh period of the display to ensure that the statistical results are correct.


通常,脏区(又称无效区)会被检查并重新绘制每 LV_DEF_REFR_PERIOD 毫秒(设置在 lv_conf.h 中)。 但是,在某些情况下,您可能需要对显示时间进行更多控制刷新发生,例如将渲染与 VSYNC 同步或 TE 信号。

您可以通过以下方式执行此操作:

/*Delete the original display refresh timer*/
lv_timer_delete(disp->refr_timer);
disp->refr_timer = NULL;


/*Call this anywhere you want to refresh the dirty areas*/
_lv_display_refr_timer(NULL);

如果您有多个显示器,请调用 lv_display_set_default(disp1)_lv_display_refr_timer(NULL) 之前选择要刷新的显示器。

注意

lv_timer_handler()_lv_display_refr_timer() 不能同时运行。.

如果启用了性能监视器,则需要将 LV_DEF_REFR_PERIOD 的值设置为与显示器的刷新周期一致,以确保统计结果正确无误。

Force refreshing(强制刷新)

显示原文

Normally the invalidated areas (marked for redraw) are rendered in lv_timer_handler() in every LV_DEF_REFR_PERIOD milliseconds. However, by using lv_refr_now(display)() you can ask LVGL to redraw the invalid areas immediately. The refreshing will happen in lv_refr_now() which might take longer time.

The parameter of lv_refr_now() is a display to refresh. If NULL is set the default display will be updated.


通常,无效区域(标记为重绘)在每个中的 lv_timer_handler() 中呈现 :cpp:macro:`LV_DEF_REFR_PERIOD` 毫秒。但是,通过使用 lv_refr_now(display)(),您可以要求LVGL立即重新绘制无效区域。刷新将发生在 lv_refr_now() 中,这可能需要更长的时间。

lv_refr_now() 的参数是要刷新的显示。如果设置了 NULL ,则将更新默认显示。

Events(事件)

显示原文

lv_display_add_event_cb(disp, event_cb, LV_EVENT_..., user_data) adds an event handler to a display. The following events are sent:


lv_display_add_event_cb(disp, event_cb, LV_EVENT_..., user_data) 添加显示的事件处理程序。将发送以下事件:

Further reading(深入学习)

显示原文

API

lv_types.h

lv_display.h