mirror of
https://github.com/lvgl/lvgl.git
synced 2024-12-03 14:43:48 +08:00
261 lines
10 KiB
ReStructuredText
261 lines
10 KiB
ReStructuredText
.. _display_interface:
|
|
|
|
=================
|
|
Display interface
|
|
=================
|
|
|
|
To create a display for LVGL call
|
|
:cpp:expr:`lv_disp_t * disp = lv_disp_create(hor_res, ver_res)`. You can create
|
|
a multiple displays and a different driver for each (see below),
|
|
|
|
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.
|
|
|
|
flush_cb
|
|
--------
|
|
|
|
An example ``flush_cb`` looks like this:
|
|
|
|
.. code:: c
|
|
|
|
void my_flush_cb(lv_disp_t * disp, const lv_area_t * area, lv_color_t * buf)
|
|
{
|
|
/*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.*/
|
|
int32_t x, y;
|
|
for(y = area->y1; y <= area->y2; y++) {
|
|
for(x = area->x1; x <= area->x2; x++) {
|
|
put_px(x, y, *buf);
|
|
buf++;
|
|
}
|
|
}
|
|
|
|
/* IMPORTANT!!!
|
|
* Inform LVGL that you are ready with the flushing and buf is not used anymore*/
|
|
lv_disp_flush_ready(disp);
|
|
}
|
|
|
|
Use :cpp:expr:`lv_disp_set_flush_cb(disp, my_flush_cb)` to set a new ``flush_cb``.
|
|
|
|
:cpp:expr:`lv_disp_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 :cpp:expr:`lv_disp_flush_is_last(disp)`.
|
|
|
|
Draw buffers
|
|
------------
|
|
|
|
The draw buffers can be set with
|
|
:cpp:expr:`lv_disp_set_draw_buffers(disp, buf1, buf2, buf_size_px, 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``
|
|
|
|
- :cpp:enumerator:`LV_DISP_RENDER_MODE_PARTIAL` Use the buffer(s) to render the
|
|
screen is 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.
|
|
- :cpp:enumerator:`LV_DISP_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 ares 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 and always the
|
|
changed areas will be redrawn.
|
|
- :cpp:enumerator:`LV_DISP_RENDER_MODE_FULL` The buffer can smaller or screen
|
|
sized but LVGL will always redraw the whole screen even is 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 framebuffer (``color_p`` parameter).
|
|
|
|
Example:
|
|
|
|
.. code:: c
|
|
|
|
static lv_color_t buf[LCD_HOR_RES * LCD_VER_RES / 10];
|
|
lv_disp_set_draw_buffers(disp, buf, NULL, sizeof(buf), LV_DISP_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 the content of the buffer is sent to the
|
|
display before drawing something new into it.
|
|
|
|
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.
|
|
|
|
Advanced options
|
|
****************
|
|
|
|
Resolution
|
|
----------
|
|
|
|
To set the resolution of the display after creation use
|
|
:cpp:expr:`lv_disp_set_res(disp, 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
|
|
:cpp:expr:`lv_disp_set_physical_res(disp, hor_res, ver_res)` and
|
|
:cpp:expr:`lv_disp_set_offset(disp, x, y)`
|
|
|
|
Rotation
|
|
--------
|
|
|
|
LVGL supports rotation of the display in 90 degree increments. You can
|
|
select whether you'd like software rotation or hardware rotation.
|
|
|
|
The orientation of the display can be changed with
|
|
``lv_disp_set_rotation(disp, LV_DISP_ROTATION_0/90/180/270, true/false)``.
|
|
LVGL will swap the horizontal and vertical resolutions internally
|
|
according to the set degree. If the last parameter is ``true`` LVGL will
|
|
rotate the rendered image. If it's ``false`` the display driver should
|
|
rotate the rendered image.
|
|
|
|
Color format
|
|
------------
|
|
|
|
Set the color format of the display. The default is
|
|
:cpp:enumerator:`LV_COLOR_FORMAT_NATIVE` which means LVGL render with the follow
|
|
formats depending on :c:macro:`LV_COLOR_DEPTH`:
|
|
|
|
- :c:macro:`LV_COLOR_DEPTH` ``32``: XRGB8888 (4 bytes/pixel)
|
|
- :c:macro:`LV_COLOR_DEPTH` ``24``: RGB888 (3 bytes/pixel)
|
|
- :c:macro:`LV_COLOR_DEPTH` ``16``: RGB565 (2 bytes/pixel)
|
|
- :c:macro:`LV_COLOR_DEPTH` ``8``: L8 (1 bytes/pixel)
|
|
|
|
The ``color_format`` can be changed with
|
|
:cpp:expr:`lv_disp_set_color_depth(disp, LV_COLOR_FORMAT_...)` to the following
|
|
values:
|
|
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_ALPHA`: Append an alpha byte to the native format resulting
|
|
in A8L8, ARGB8565, ARGB8888 formats.
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_REVERSE`: Reverse the byte order of the native format. Useful if the
|
|
rendered image is sent to the display via SPI and
|
|
the display needs the bytes in the opposite order.
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_L8`: Lightness only on 8 bit
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_A8`: Alpha only on 8 bit
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_I8`: Indexed (palette) 8 bit
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_A8L8`: Lightness on 8 bit with 8 bit alpha
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB2222`: ARGB with 2 bit for each channel
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`: 16 bit RGB565 format without alpha channel
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8565`: 16 bit RGB565 format and 8 bit alpha channel
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB1555`: 5 bit for each color channel and 1 bit for alpha
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB4444`: 4 bit for each channel
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`: 8 bit for each color channel with out alpha channel
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`: 8 bit for each channel
|
|
- :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888`: 8 bit for each color channel and 8 bit placeholder for the alpha channel
|
|
|
|
If the color format is set to non-native ``draw_ctx->buffer_convert``
|
|
function will be called before calling ``flush_cb`` to convert the
|
|
native color format to the desired, therefore rendering in non-native
|
|
formats has a negative effect on performance. Learn more about
|
|
``draw_ctx`` `here </porting/gpu>`__.
|
|
|
|
It's very important that draw buffer(s) should be large enough for both
|
|
the native format and the target color format. For example if
|
|
``LV_COLOR_DEPTH == 16`` and :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888` is selected
|
|
LVGL will choose the larger to figure out how many pixel can be
|
|
rendered at once. Therefore with :cpp:enumerator:`LV_DISP_RENDER_MODE_FULL` and the
|
|
larger pixel size needs to be chosen.
|
|
|
|
:cpp:enumerator:`LV_DISP_RENDER_MODE_DIRECT` supports only the
|
|
:cpp:enumerator:`LV_COLOR_FORMAT_NATIVE` format.
|
|
|
|
Antialiasing
|
|
------------
|
|
|
|
:cpp:expr:`lv_disp_set_antialiasing(disp, true/false)` enables/disables the
|
|
antialiasing (edge smoothing) on the given display.
|
|
|
|
User data
|
|
---------
|
|
|
|
With :cpp:expr:`lv_disp_set_user_data(disp, p)` a pointer to a custom data can
|
|
be stored in display object.
|
|
|
|
Events
|
|
******
|
|
|
|
:cpp:expr:`lv_disp_add_event(disp, event_cb, LV_DISP_EVENT_..., user_data)` adds
|
|
an event handler to a display. The following events are sent:
|
|
|
|
- :cpp:enumerator:`LV_DISP_EVENT_INVALIDATE_AREA` An area is invalidated (marked for redraw).
|
|
:cpp:expr:`lv_event_get_param(e)` returns a pointer to an :cpp:struct:`lv_area_t`
|
|
variable with the coordinates of the area to be invalidated. The area can
|
|
be freely modified is needed to adopt it the special requirement of the
|
|
display. Usually needed with monochrome displays to invalidate Nx8
|
|
lines at once.
|
|
- :cpp:enumerator:`LV_DISP_EVENT_RENDER_START`: Called when rendering starts.
|
|
- :cpp:enumerator:`LV_DISP_EVENT_RENDER_READY`: Called when rendering is ready
|
|
- :cpp:enumerator:`LV_DISP_EVENT_RESOLUTION_CHANGED`: Called when the resolution changes due
|
|
to :cpp:func:`lv_disp_set_resolution` or :cpp:func:`lv_disp_set_rotation`.
|
|
|
|
Other options
|
|
*************
|
|
|
|
Decoupling the display refresh timer
|
|
------------------------------------
|
|
|
|
Normally the dirty (a.k.a invalid) areas are checked and redrawn in
|
|
every :c:macro:`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:
|
|
|
|
.. code:: c
|
|
|
|
/*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_disp_refr_timer(NULL);
|
|
|
|
If you have multiple displays call :cpp:expr:`lv_disp_set_deafult(disp1)` to
|
|
select the display to refresh before :cpp:expr:`_lv_disp_refr_timer(NULL)`.
|
|
|
|
|
|
.. note:: that :cpp:func:`lv_timer_handler` and :cpp:func:`_lv_disp_refr_timer` can not run at the same time.
|
|
|
|
|
|
If the performance monitor is enabled, the value of :c:macro:`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.
|
|
|
|
Further reading
|
|
***************
|
|
|
|
- `lv_port_disp_template.c <https://github.com/lvgl/lvgl/blob/master/examples/porting/lv_port_disp_template.c>`__
|
|
for a template for your own driver.
|
|
- `Drawing </overview/drawing>`__ to learn more about how rendering
|
|
works in LVGL.
|
|
- `Display features </overview/display>`__ to learn more about higher
|
|
level display features.
|
|
|
|
API
|
|
***
|