Connecting LVGL to Your Hardware
Initializing LVGL
After you have:
added the appropriate LVGL files to your project, and
created a lv_conf.h file for your project,
you will need to complete a few more steps to get your project up and running with LVGL.
Initialize LVGL once early during system execution by calling
lv_init()
. This needs to be done before making any other LVGL calls.Initialize your drivers.
Connect the Tick Interface.
Connect the Display Interface.
Connect the Input-Device Interface.
Drive LVGL time-related tasks by calling
lv_timer_handler()
every few milliseconds to manage LVGL timers. See Timer Handler for different ways to do this.Optionally set a theme with
lv_display_set_theme()
.Thereafter #include "lvgl/lvgl.h" in source files wherever you need to use LVGL functions.
Tick Interface
LVGL needs awareness of what time it is (i.e. elapsed time in milliseconds) for all of its tasks for which time is a factor: refreshing displays, reading user input, firing events, animations, etc.
There are two ways to provide this information to LVGL:
Supply LVGL with a callback function to retrieve elapsed system milliseconds by calling lv_tick_set_cb(my_get_milliseconds). my_get_milliseconds() needs to return the number of milliseconds elapsed since system start up. Many platforms have built-in functions that can be used as they are. For example:
SDL:
lv_tick_set_cb(SDL_GetTicks);
Arduino:
lv_tick_set_cb(my_tick_get_cb);
, wheremy_tick_get_cb
is:static uint32_t my_tick_get_cb(void) { return millis(); }
FreeRTOS:
lv_tick_set_cb(xTaskGetTickCount);
STM32:
lv_tick_set_cb(HAL_GetTick);
ESP32:
lv_tick_set_cb(my_tick_get_cb);
, wheremy_tick_get_cb
is a wrapper foresp_timer_get_time() / 1000;
Call lv_tick_inc(x) periodically, where
x
is the elapsed milliseconds since the last call. Iflv_tick_inc()
is called from an ISR, it should be from either a high priority interrupt or an interrupt that cannot be missed when the system is under high load.Note
lv_tick_inc()
is only one of two LVGL functions that may be called from an interrupt if writing to auint32_t
value is atomic on your platform. See below and the Threading Considerations section to learn more.
Either way, the writing of the uint32_t
Tick value must be atomic,
which is usually the case with a 32- or 64-bit platform. If you are using a 16-bit
system (causing the update of the Tick value to not be atomic) and your platform uses
the Harvard instruction set, you can set a function like this as the callback passed
to lv_tick_set_cb(my_get_milliseconds):
/**
* @brief Safe read from 'elapsed_power_on_time_in_ms'
*/
uint32_t my_get_milliseconds()
{
register uint32_t u32result;
/* Disable priority 1-6 interrupts for 2 Fcys. */
__builtin_disi(2);
u32result = elapsed_power_on_time_in_ms; /* Cost: 2 Fcys */
/* Generally looks like this in assembly:
* mov elapsed_power_on_time_in_ms, W0
* mov 0x7898, W1
* requiring exactly 2 clock cycles.
* Now value is copied to register pair W0:W1
* where it can be written to any destination. */
return u32result;
}
Reliability
Advancing the tick value should be done in such a way that its timing is reliable and
not dependent on anything that consumes an unknown amount of time. For an example of
what not to do: this can "seem" to work, but LVGL's timing will be incorrect
because the execution time of lv_timer_handler()
varies from call to call and
thus the delay it introduces cannot be known.
// Bad idea
lv_timer_handler();
lv_tick_inc(5);
my_delay_ms(5);
Display Interface
LVGL needs to be supplied with knowledge about each display panel you want it to use. Specifically:
its pixel format and size (Creating a Display),
where to render pixels for it (Draw Buffer(s)), and
how to send those rendered pixels to it (Flush Callback).
See the respective links for how to supply LVGL with this knowledge.
Input-Device Interface
LVGL needs to know how to get input from all user-input devices that will be used in your project. LVGL supports a wide variety of user-input devices:
touch-screens,
touch-pads,
mice,
crowns,
encoders,
keypads,
keyboards,
etc.
See Creating an Input Device to see how to do this.