Setting Up Your Display(s)
During system initialization, you must do the following for each physical display panel you want LVGL to use:
create an lv_display_t object for it,
assign its Draw Buffer(s), and
assign a Flush Callback for it.
Creating a Display
To create a display for LVGL:
lv_display_t * display1 = lv_display_create(hor_res, ver_res)
You can create multiple displays with a different driver for each (see below).
When an lv_display_t
object is created, with it are created 4 Screens set up
to help you manage layering of displayed Widgets. See Transparent Screens and
Screen Layers for more information.
Default Display
When the first Display (lv_display) object is created, it becomes the Default Display. If other Display Objects are created (to service additional Display Panels), the Default Display remains the first one created.
To set another Display (lv_display) as the Default Display, call lv_display_set_default()
.
See How Many Displays Can LVGL Use? for more information about using multiple displays.
For many lv_display_...()
functions, passing NULL for the disp
argument will
cause the function to target the Default Display. Check the API documentation for
the function you are calling to be sure.
Draw Buffer(s)
During system initialization, you must set drawing buffers for LVGL to use for each display. Do so by calling:
lv_display_set_buffers(display1, buf1, buf2, buf_size_in_bytes, render_mode)
buf1
a buffer to which LVGL can render pixelsbuf2
a second optional buffer (see below)buf_size_in_bytes
size of buffer(s) in bytesrender_mode
is one of the following:LV_DISPLAY_RENDER_MODE_PARTIAL
Use the buffer(s) to render to the display using buffers smaller than the size of the display. Use of buffers at least 1/10 display size is recommended. In Flush Callback 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 size(es) must match the size of the display. LVGL will render into the correct location of the buffer. Using this method the buffer(s) always contain the whole display image. If two buffer are used, the rendered areas are automatically copied to the other buffer after flushing. Due to this in Flush Callback 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 size(es) must match the size of the display. LVGL will always redraw the whole screen even if only 1 pixel has been changed. If two display-sized draw buffers are provided, LVGL's display handling works like "traditional" double buffering. This means the Flush Callback callback only has to update the address of the frame buffer to thepx_map
parameter.
Simple Example
/* Declare buffer for 1/10 screen size; BYTES_PER_PIXEL will be 2 for RGB565. */
#define BYTES_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565))
static uint8_t buf1[MY_DISP_HOR_RES * MY_DISP_VER_RES / 10 * BYTES_PER_PIXEL];
/* Set display buffer for display `display1`. */
lv_display_set_buffers(display1, buf1, NULL, sizeof(buf1), 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 Callback. LVGL
then waits until lv_display_flush_ready()
is called
(that is, the content of the buffer has been 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. Doing so allows rendering and refreshing the display to become parallel operations.
Flush Callback
Draw buffer(s) are simple array(s) that LVGL uses to render the display's content. Once rendering is has been completed, the content of the draw buffer is sent to the display using a Flush Callback function.
An example looks like this:
void my_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map)
{
/* The most simple case (also the slowest) to send all rendered 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 flushing is complete so buffer can be modified again. */
lv_display_flush_ready(display);
}
During system initialization, tell LVGL you want that function to copy pixels from rendered pixel-buffers to a particular display by doing the following:
lv_display_set_flush_cb(display1, my_flush_cb)
Note that which display is targeted is passed to the function, so you can use the same function for multiple displays, or use different functions for multiple displays. It's up to you.
Note
lv_display_flush_ready(display1) needs to be called when flushing is complete to inform LVGL that the buffer is available again to render new content into it.
LVGL might render the screen in multiple chunks and therefore call your Flush Callback multiple times. To see whether the current call is for the last chunk being rendered, use lv_display_flush_is_last(display1).
Flush-Wait Callback
By using lv_display_flush_ready()
LVGL will normally 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 waiting for the flush to be completed. The callback
need not call lv_display_flush_ready()
since the caller takes care of
that (clearing the display's flushing
flag) when your callback returns.
However, if a Flush-Wait Callback is not set, LVGL assumes that
lv_display_flush_ready()
is called after the flush has completed.