feat(docs): reorganize docs (#7136)

This commit is contained in:
Victor Wheeler
2024-10-23 12:53:33 -06:00
committed by GitHub
parent c61ca42a2a
commit 9b6f6d23f1
212 changed files with 6314 additions and 5806 deletions

View File

@@ -0,0 +1,28 @@
.. _building_lvgl:
=============
Building LVGL
=============
Make and CMake
**************
LVGL also supports ``make`` and ``CMake`` build systems out of the box.
To add LVGL to your Makefile based build system add these lines to your
main Makefile:
.. code-block:: make
LVGL_DIR_NAME ?= lvgl #The name of the lvgl folder (change this if you have renamed it)
LVGL_DIR ?= ${shell pwd} #The path where the lvgl folder is
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/lvgl.mk
For integration with CMake take a look this section of the
:ref:`Documentation <build_cmake>`.
Managed builds
**************
TODO

View File

@@ -0,0 +1,93 @@
.. _configuration:
=============
Configuration
=============
.. _lv_conf:
lv_conf.h
*********
Creating lv_conf.h
------------------
When setting up your project for the first time, copy ``lvgl/lv_conf_template.h`` to
``lv_conf.h`` next to the ``lvgl`` folder. Change the first ``#if 0`` to ``1`` to
enable the file's content and set the :c:macro:`LV_COLOR_DEPTH` define to align with
the color depth used by your display panel. See comments in ``lv_conf.h`` for
details.
The layout of the files should look like this::
lvgl/
lv_conf.h
other files and folders in your project
Alternatively, ``lv_conf.h`` can be copied to another place but then you
should add the :c:macro:`LV_CONF_INCLUDE_SIMPLE` define to your compiler
options (e.g. ``-DLV_CONF_INCLUDE_SIMPLE`` for GCC compiler) and set the
include path manually (e.g. ``-I../include/gui``). In this case LVGL
will attempt to include ``lv_conf.h`` simply with ``#include "lv_conf.h"``.
You can even use a different name for ``lv_conf.h``. The custom path can
be set via the :c:macro:`LV_CONF_PATH` define. For example
``-DLV_CONF_PATH="/home/joe/my_project/my_custom_conf.h"``. If this define
is set :c:macro:`LV_CONF_SKIP` is assumed to be ``0``.
If :c:macro:`LV_CONF_SKIP` is defined, LVGL will not try to include
``lv_conf.h``. Instead you can pass the config defines using build
options. For example ``"-DLV_COLOR_DEPTH=32 -DLV_USE_BUTTON=1"``. Unset
options will get a default value which is the same as the content of
``lv_conf_template.h``.
LVGL also can be used via ``Kconfig`` and ``menuconfig``. You can use
``lv_conf.h`` together with Kconfig as well, but keep in mind that the values
from ``lv_conf.h`` or build settings (``-D...``) override the values
set in Kconfig. To ignore the configs from ``lv_conf.h`` simply remove
its content, or define :c:macro:`LV_CONF_SKIP`.
.. _configuration_settings:
Configuration Settings
----------------------
Once the ``lv_conf.h`` file is in place, you can modify this header to configure
LVGL's behavior, disable unused modules and features, adjust the size of buffers, etc.
The comments in ``lv_conf.h`` explain the meaning of each setting. Be sure
to at least set :c:macro:`LV_COLOR_DEPTH` according to your display's color
depth. Note that the examples and demos explicitly need to be enabled
in ``lv_conf.h`` if you need them.
TODO: Add all things related to ``lv_conf.h`` file and its contents.
Multiple Instances of LVGL
~~~~~~~~~~~~~~~~~~~~~~~~~~
It is possible to run multiple, independent isntances of LVGL. To enable its
multi-instance feature, set :c:macro:`LV_GLOBAL_CUSTOM` in ``lv_conf.h``
and provide a custom function to :cpp:func:`lv_global_default` using ``__thread`` or
``pthread_key_t``. It will allow running multiple LVGL instances by storing LVGL's
global variables in TLS (Thread-Local Storage).
For example:
.. code-block:: c
lv_global_t * lv_global_default(void)
{
static __thread lv_global_t lv_global;
return &lv_global;
}
Kconfig
*******
TODO: Add how to use LVGL with Kconfig.

View File

@@ -0,0 +1,131 @@
.. _connecting_lvgl:
================================
Connecting LVGL to Your Hardware
================================
.. _initializing_lvgl:
Initializing LVGL
*****************
After you have:
- :ref:`acquired LVGL <getting_lvgl>`,
- added the appropriate LVGL files to your project, and
- :ref:`created a lv_conf.h file <lv_conf>` for your project,
you will need to complete a few more steps to get your project up and running with LVGL.
1. Initialize LVGL once early during system execution by calling :cpp:func:`lv_init`.
This needs to be done before making any other LVGL calls.
2. Initialize your drivers.
3. Connect the :ref:`tick_interface`.
4. Connect the :ref:`display_interface`.
5. Connect the :ref:`indev_interface`.
6. Drive LVGL time-related tasks by calling :cpp:func:`lv_timer_handler` every few
milliseconds to manage LVGL timers. See :ref:`timer_handler` for different ways
to do this.
7. Optionally set a theme with :cpp:func:`lv_display_set_theme`.
8. Thereafter #include "lvgl/lvgl.h" in source files wherever you need to use LVGL
functions.
.. _tick_interface:
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.
.. image:: /misc/intro_data_flow.png
:scale: 75 %
:alt: LVGL Data Flow
:align: center
There are two ways to provide this information to LVGL:
1. Supply LVGL with a callback function to retrieve elapsed system milliseconds by
calling :cpp:expr:`lv_tick_set_cb(my_get_milliseconds_function)`.
:cpp:expr:`my_get_milliseconds_function()` 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);``, where ``my_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);``, where ``my_tick_get_cb`` is a
wrapper for ``esp_timer_get_time() / 1000;``
2. Call :cpp:expr:`lv_tick_inc(x)` periodically, where ``x`` is the elapsed
milliseconds since the last call. If :cpp:func:`lv_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:: :cpp:func:`lv_tick_inc` is only one of two LVGL functions that may be
called from an interrupt. See the :ref:`threading` section to learn more.
The ticks (milliseconds) should be independent from any other activities of the MCU.
For example this works, but LVGL's timing will be incorrect as the execution time of
:c:func:`lv_timer_handler` is not considered:
.. code-block:: c
// Bad idea
lv_timer_handler();
lv_tick_inc(5);
my_delay_ms(5);
.. _display_interface:
Display Interface
*****************
LVGL needs to be supplied with knowledge about each display panel you want it to use.
Specificially:
- its pixel format and size (:ref:`creating_a_display`),
- where to render pixels for it (:ref:`draw_buffers`), and
- how to send those rendered pixels to it (:ref:`flush_callback`).
See the respective links for how to supply LVGL with this knowledge.
.. _indev_interface:
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 :ref:`indev_creation` to see how to do this.
API
***
:ref:`lv_tick_h`

View File

@@ -0,0 +1,25 @@
.. _getting_lvgl:
============
Getting LVGL
============
LVGL is available on GitHub: https://github.com/lvgl/lvgl.
You can clone it or
`Download <https://github.com/lvgl/lvgl/archive/refs/heads/master.zip>`__
the latest version of the library from GitHub.
The graphics library itself is the ``lvgl`` directory. It contains several
directories but to use LVGL you only need the ``.c`` and ``.h`` files under
the ``src`` directory, plus ``lvgl/lvgl.h``, and ``lvgl/lv_version.h``.
Demos and Examples
------------------
The ``lvgl`` directory also contains an ``examples`` and a ``demos``
directory. If your project needs examples and/or demos, add the these
directories to your project. If ``make`` or :ref:`build_cmake` handle the
examples and demos directories, no extra action is required.

View File

@@ -0,0 +1,18 @@
.. _add_lvgl_to_your_project:
========================
Add LVGL to Your Project
========================
.. toctree::
:maxdepth: 2
getting_lvgl
building_lvgl
configuration
connecting_lvgl
timer_handler
threading
other_platforms

View File

@@ -0,0 +1,11 @@
.. _other_platforms:
=========================
Other Platforms and Tools
=========================
See :ref:`Integration <integration_index>` to see how to use LVGL on different
platforms. There, you will find many platform-specific descriptions e.g. for ESP32,
Arduino, NXP, RT-Thread, NuttX, etc.

View File

@@ -0,0 +1,277 @@
.. _threading:
========================
Threading Considerations
========================
.. _threading_definitions:
Definitions
***********
.. _thread:
Thread
In the context of this document, a thread is any sequence of CPU instructions.
In "bare-metal" implementations (i.e. no OS), threads include:
- the main thread executing a while(1) loop that runs the system, and
- interrupts.
When running under an OS, threads include:
- each task (or process),
- interrupts, and
- advanced OSes can have multiple "execution threads" within a processes.
.. _atomic operation:
Atomic Operation
If operation X is atomic, that means that any thread observing the operation will
see it either as not yet started, or as completed, and not in any state that is
partially completed.
If other threads can see the operation in a partially performed state, or
interfere with it, then operation X is not atomic.
If an atomic operation can fail, its implementation must return the the resource
back to the state before the operation was started. To other threads it must
appear as though the operation had not yet started.
.. _atomic data:
.. _atomic:
.. _non-atomic data:
Atomic Data
A datum (i.e. contents of a variable or data structure) is atomic if any thread
observing it will always see it in a consistent state, as if operations on it
have either not yet started, or have been successfully completed, and not in a
state that is partially changed or otherwise inconsistent.
When reading or writing a value is started and completed with 1 CPU instruction,
it is automatically atomic, since it can never been seen in an inconsistent
(partially-changed) state, even from a CPU interrupt or exception. With such
values, no special protection is required by programmers to ensure all threads
see it in a consistent state.
.. _lvgl_and_threads:
LVGL and Threads
****************
LVGL is **not thread-safe**.
That means it is the programmer's responsibility to see that no LVGL function is
called while another LVGL call is in progress in another thread. This includes calls
to :cpp:func:`lv_timer_handler`.
.. note::
Assuming the above is the case, it is safe to call LVGL functions in
- :ref:`event callbacks <events>`, and in
- :ref:`timer callbacks <timer>`
because the thread that drives both of these is the thread that calls
:cpp:func:`lv_timer_handler`.
Reason:
LVGL manages many complex data structures, and those structures are "system
resources" that must be protected from being "seen" by other threads in an
inconsistent state. A high percentage LVGL functions (functions that start with
``lv_``) either read from or change those data structures. Those that change them
place the data in an inconsistent state during execution (because such changes are
multi-step sequences), but return them to a consistent state before those functions
return. For this reason, execution of each LVGL function must be allowed to complete
before any other LVGL function is started.
.. _os_exception:
.. admonition:: Exceptions to the Above:
These two LVGL functions may be called from any thread:
- :cpp:func:`lv_tick_inc` (see :ref:`tick_interface` for more information) and
- :cpp:func:`lv_display_flush_ready` (see :ref:`flush_callback` for more information)
The reason this is okay is that the LVGL data changed by them is :ref:`atomic <atomic>`.
If an interrupt MUST convey information to part of your application that calls
LVGL functions, set a flag or other atomic value that your LVGL-calling thread
(or an :ref:`LVGL Timer <timer>` you create) can read from and take action.
If you are using an OS, there are a few other options. See below.
.. _tasks:
Tasks
*****
Under an OS, it is common to have many threads of execution ("tasks" in some OSes)
performing services for the application. In some cases, such threads can acquire
data that should be shown (or otherwise reflected) in the user interface, and doing
so requires making LVGL calls to get that data (or change) shown.
Yet it still remains the programmer's responsibility to see that no LVGL function is
called while another LVGL call is in progress.
How do you do this?
.. _gateway thread:
Method 1: Use a Gateway Thread
-------------------------------
A "Gateway Thread" (or "Gateway Task" in some OSes) is a thread (task) that the
system designer designates to *exclusively* manage a system resource. An example is
management of a remote chip, such as an EEPROM or other device that always needs to
be brought into a consistent state before something new is started. Another example
is management of multiple devices on an I2C bus (or any data bus). In this case the
I2C bus is the "exclusively-managed resource", and having only one thread managing it
guarantees that each action started is allowed to complete before another action with
it is started.
LVGL's data structures are a system resource that requires such protection.
Using this method, creation, modification and deletion of all Widgets and other
LVGL resources (i.e. all LVGL function calls excluding the :ref:`exceptions
<os_exception>` mentioned above) are called by that thread. That means
that thread is also the ONLY caller of :cpp:func:`lv_timer_handler`. (See
:ref:`add_lvgl_to_your_project` for more information.)
This ensures LVGL's data structures "appear" atomic_ (all threads using this data
"see" it in a consistent state) by the fact that no other threads are "viewing" those
data structures. This is enforced by programmer discipline that ensures the `Gateway
Thread`_ is the only thread making LVGL calls (excluding the :ref:`exceptions
<os_exception>` mentioned above).
If `atomic data`_ relevant to the user interface is updated in another thread (i.e.
by another task or in an interrupt), the thread calling LVGL functions can read that
data directly without worry that it is in an inconsistent state. (To avoid
unnecessary CPU overhead, a mechanism can be provided [such as a flag raised by the
updating thread] so that the user interface is only updated when it will result in a
change visible to the end user.)
If `non-atomic data`_ relevant to the user interface is updated in another thread
(i.e. by another task or in an interrupt), an alternate (and safe) way of convey that
data to the thread calling LVGL functions is to pass a private copy of that data to
that thread via a QUEUE or other OS mechanism that protects that data from being seen
in an inconsistent state.
Use of a `Gateway Thread`_ avoids the CPU-overhead (and coding overhead) of using a
MUTEX to protect LVGL data structures.
Method 2: Use a MUTEX
----------------------
A MUTEX stands for "MUTually EXclusive" and is a synchronization primative that
protects the state of a system resource from being modified or accessed by multiple
threads of execution at once. In other words, it makes data so protected "appear"
atomic (all threads using this data "see" it in a consistent state). Most OSes
provide MUTEXes.
The system designer assigns a single MUTEX to product a single system resource. Once
assigned, that MUTEX performs such protection by programmers:
1. acquiring the MUTEX (a.k.a. locking it) before accessing or modifying that
resource, and
2. releasing the MUTEX (a.k.a. unlocking it) after that access or modification
is complete.
If a thread attempts to acquire (lock) the MUTEX while another thread "owns" it,
that thread waits on the other thread to release (unlock) it before it is allowed
to continue execution.
To be clear: this must be done *both* by threads that READ from that resource, and
threads that MODIFY that resource.
If a MUTEX is used to protect LVGL data structures, that means *every* LVGL function
call (or group of function calls) must be preceeded by #1, and followed by #2,
including calls to :cpp:func:`lv_timer_handler`.
.. note::
If your OS is integrated with LVGL (the macro :c:macro:`LV_USE_OS` has a value
other than ``LV_OS_NONE`` in ``lv_conf.h``) you can use :cpp:func:`lv_lock()` and
:cpp:func:`lv_unlock()` to perform #1 and #2.
When this is the case, :cpp:func:`lv_timer_handler` calls :cpp:func:`lv_lock()`
and :cpp:func:`lv_unlock()` internally, so you do not have to bracket your
calls to :cpp:func:`lv_timer_handler` with them.
If your OS is NOT integrated with LVGL, then these calls either return
immediately with no effect, or are optimized away by the linker.
To enable :cpp:func:`lv_lock()` and :cpp:func:`lv_unlock()`, set ``LV_USE_OS``
to a value other than ``LV_OS_NONE``.
This pseudocode illustrates the concept of using a MUTEX:
.. code-block:: c
void lvgl_thread(void)
{
while(1) {
uint32_t time_till_next;
time_till_next = lv_timer_handler(); /* lv_lock/lv_unlock is called internally */
thread_sleep(time_till_next); /* sleep for a while */
}
}
void other_thread(void)
{
/* You must always hold (lock) the MUTEX while calling LVGL functions. */
lv_lock();
lv_obj_t *img = lv_image_create(lv_screen_active());
lv_unlock();
while(1) {
lv_lock();
/* Change to next image. */
lv_image_set_src(img, next_image);
lv_unlock();
thread_sleep(2000);
}
}
.. _sleep_management:
Sleep Management
****************
The MCU can go to sleep when no user input has been received for a certain period.
In this case, the main ``while(1)`` could look like this:
.. code-block:: c
while(1) {
/* Normal operation (no sleep) in < 1 sec inactivity */
if(lv_display_get_inactive_time(NULL) < 1000) {
lv_timer_handler();
}
/* Sleep after 1 sec inactivity */
else {
timer_stop(); /* Stop the timer where lv_tick_inc() is called */
sleep(); /* Sleep the MCU */
}
my_delay_ms(5);
}
You should also add the following lines to your input device read
function to signal a wake-up (press, touch, click, etc.) has happened:
.. code-block:: c
lv_tick_inc(LV_DEF_REFR_PERIOD); /* Force task execution on wake-up */
timer_start(); /* Restart timer where lv_tick_inc() is called */
lv_timer_handler(); /* Call `lv_timer_handler()` manually to process the wake-up event */
In addition to :cpp:func:`lv_display_get_inactive_time` you can check
:cpp:func:`lv_anim_count_running` to see if all animations have finished.

View File

@@ -0,0 +1,63 @@
.. _timer_handler:
=============
Timer Handler
=============
To drive the timers of LVGL you need to call :cpp:func:`lv_timer_handler`
periodically in one of the following:
- *while(1)* of *main()* function, or
- an OS task periodically. (See :ref:`lvgl_and_threads`.)
.. image:: /misc/intro_data_flow.png
:scale: 75 %
:alt: LVGL Data Flow
:align: center
Example:
.. code-block:: c
while(1) {
uint32_t time_till_next = lv_timer_handler();
my_delay_ms(time_till_next);
}
If you want to use :cpp:func:`lv_timer_handler` in a super-loop, a helper
function :cpp:func:`lv_timer_handler_run_in_period` is provided to simplify
supplying LVGL with time awareness:
.. code-block:: c
while(1) {
...
lv_timer_handler_run_in_period(5); /* run lv_timer_handler() every 5ms */
...
}
Or use the sleep time automatically calculated by LVGL:
.. code-block:: c
while(1) {
...
lv_timer_periodic_handler();
...
}
In an OS environment, you can use it together with the **delay** or
**sleep** provided by OS to release CPU whenever possible:
.. code-block:: c
while (1) {
uint32_t time_till_next = lv_timer_handler();
os_delay_ms(time_till_next); /* delay to avoid unnecessary polling */
}
See :ref:`timer` section to learn more about timers.
API
***

468
docs/intro/basics.rst Normal file
View File

@@ -0,0 +1,468 @@
.. _lvgl_basics:
===========
LVGL Basics
===========
LVGL (Light and Versatile Graphics Library) is a free and open-source graphics
library providing everything you need to create an embedded GUI with easy-to-use
graphical elements, beautiful visual effects, and a low memory footprint.
How does it do this?
.. _basic_data_flow:
Overview of LVGL's Data Flow
****************************
.. figure:: /misc/intro_data_flow.png
:scale: 75 %
:alt: LVGL Data Flow
:align: center
:figwidth: image
Overview of LVGL Data Flow
You create one :ref:`display` for each physical display panel, create
:ref:`basics_screen_widgets` on them, add :ref:`basics_widgets` onto those Screens.
To handle touch, mouse, keypad, etc., you :ref:`create an Input Device <indev_creation>`
for each. The :ref:`tick_interface` tells LVGL what time is it. :ref:`timer_handler`
drives LVGL's timers which, in turn, perform all of LVGL's time-related tasks:
- periodically refreshes displays,
- reads input devices,
- fires events,
- runs any animations, and
- runs user-created timers.
.. _applications_job:
Application's Job
-----------------
After initialization, the application's job is merely to create Widget Trees when
they are needed, manage events those Widgets generate (by way of user interaction
and other things), and delete them when they are no longer needed. LVGL takes care
of the rest.
.. _basics_major_concepts:
Major Concepts
**************
.. _display-vs-screen:
Display vs Screen
-----------------
Before we get into any details about Widgets, let us first clarify the difference
between two terms that you will hereafter see frequently:
- A **Display** or **Display Panel** is the physical hardware displaying the pixels.
- A :ref:`display` object is an object in RAM that represents a **Display** meant
to be used by LVGL.
- A **Screen** is the "root" Widget in the Widget Trees mentioned above, and are
"attached to" a particular :ref:`display`.
Default Display
---------------
When the first :ref:`display` object is created, it becomes the Default Display.
Many functions related to Screen Widgets use the default display.
See :ref:`default_display` for more information.
.. _basics_screen_widgets:
Screen Widgets
--------------
In this documentation, the term "Screen Widget" is frequently shortened to just
"Screen". But it is important to understand that a "Screen" is simply any
:ref:`Widget <widgets>` created without a parent --- the "root" of each Widget Tree.
See :ref:`screens` for more details.
Active Screen
-------------
The Active Screen is the screen (and its child Widgets) currently being displayed.
See :ref:`active_screen` for more information.
.. _basics_widgets:
Widgets
-------
After LVGL is initialized (see :ref:`initializing_lvgl`), to create an interactive
user interface, an application next creates a tree of Widgets that LVGL can render to
the associated dislay, and with which the user can interact.
Widgets are "intelligent" LVGL graphical elements such as :ref:`Base Widgets
<base_widget_overview>` (simple rectangles and :ref:`screens`), Buttons, Labels,
Checkboxes, Switches, Sliders, Charts, etc. Go to :ref:`widgets` to see the full
list.
To build this Widget Tree, the application first acquires a pointer to a Screen Widget.
A system designer is free to use the default Screen created with the :ref:`display`
and/or create his own. To create a new Screen Widget, simply create a Widget passing
NULL as the parent argument. Technically, this can be any type of Widget, but in
most cases it is a :ref:`base_widget_overview`. (An example of another type of
Widget being used as a Screen is an :ref:`lv_image` Widget to supply an image for the
background.)
The application then adds Widgets to this Screen as children in the tree. Widgets
are automatically added as children to their parent Widgets at time of creation ---
the Widget's parent is passed as the first argument to the function that creates
the Widget. After being so added, we say that the parent Widget "contains" the
child Widget.
Any Widget can contain other Widgets. For example, if you want a Button to have
text, create a Label Widget and add it to the Button as a child.
Each child Widget becomes "part of" its parent Widget. Because of this relationship:
- when the parent Widget moves, its children move with it;
- when the parent Widget is deleted, its children are deleted with it;
- a child Widget is only visible within its parent's boundaries; any part of a child
outside its parent's boundaries is clipped (i.e. not rendered).
Screens (and their child Widgets) can be created and deleted at any time *except*
when the Screen is the :ref:`active_screen`. If you want to delete the current Screen
as you load a new one, call :cpp:func:`lv_screen_load_anim` and pass ``true`` for the
``auto_del`` argument. If you want to keep the current Screen in RAM when you load a
new Screen, pass ``false`` for the ``auto_del`` argument, or call
:cpp:func:`lv_screen_active` to load the new screen.
A system designer is free to keep any number of Screens (and their child Widgets) in
RAM (e.g. for quick re-display again later). Doing so:
- requires more RAM, but
- can save the time of repeatedly creating the Screen and its child Widgets;
- can be handy when a Screen is complex and/or can be made the :ref:`active_screen` freqently.
If multiple Screens are maintained in RAM simultaneously, it is up to the system
designer as to how they are managed.
.. _basics_creating_widgets:
Creating Widgets
~~~~~~~~~~~~~~~~
Widgets are created by calling functions that look like this::
lv_<type>_create(parent)
The call will return an :cpp:type:`lv_obj_t` ``*`` pointer that can be used later to
reference the Widget to set its attributes.
For example:
.. code-block:: c
lv_obj_t * slider1 = lv_slider_create(lv_screen_active());
.. _basics_modifying_widgets:
Modifying Widgets
~~~~~~~~~~~~~~~~~
Attributes common to all Widgets are set by functions that look like this::
lv_obj_set_<attribute_name>(widget, <value>)
For example:
.. code-block:: c
lv_obj_set_x(slider1, 30);
lv_obj_set_y(slider1, 10);
lv_obj_set_size(slider1, 200, 50);
Along with these attributes, widgets can have type-specific attributes which are
set by functions that look like this::
lv_<type>_set_<attribute_name>(widget, <value>)
For example:
.. code-block:: c
lv_slider_set_value(slider1, 70, LV_ANIM_ON);
To see the full API visit the documentation of the Widget in question under
:ref:`widgets` or study its related header file in the source code, e.g.
- lvgl/src/widgets/slider/lv_slider.h
or view it on GitHub, e.g.
- https://github.com/lvgl/lvgl/blob/master/src/widgets/slider/lv_slider.h .
.. _basics_deleting_widgets:
Deleting Widgets
~~~~~~~~~~~~~~~~
To delete any widget and its children::
lv_obj_delete(lv_obj_t * widget)
.. _basics_events:
Events
------
Events are used to inform the application that something has happened with a Widget.
You can assign one or more callbacks to a Widget which will be called when the
Widget is clicked, released, dragged, being deleted, etc.
A callback is assigned like this:
.. code-block:: c
lv_obj_add_event_cb(btn, my_btn_event_cb, LV_EVENT_CLICKED, NULL);
...
void my_btn_event_cb(lv_event_t * e)
{
printf("Clicked\n");
}
:cpp:enumerator:`LV_EVENT_ALL` can be used instead of :cpp:enumerator:`LV_EVENT_CLICKED`
to invoke the callback for all events. (Beware: there are a LOT of events! This can
be handy for debugging or learning what events occur for a given Widget, or indeed
if the application needs to process all events for some reason.)
Event callbacks receive the argument :cpp:expr:`lv_event_t * e` containing the
current event code and other event-related information. The current event code can
be retrieved with:
.. code-block:: c
lv_event_code_t code = lv_event_get_code(e);
The Widget that triggered the event can be retrieved with:
.. code-block:: c
lv_obj_t * obj = lv_event_get_target(e);
To learn all features of the events go to the :ref:`events` section.
.. _basics_parts:
Parts
-----
Widgets are built from one or more *parts*. For example, a button
has only one part called :cpp:enumerator:`LV_PART_MAIN`. However, a
:ref:`lv_slider` has :cpp:enumerator:`LV_PART_MAIN`, :cpp:enumerator:`LV_PART_INDICATOR`
and :cpp:enumerator:`LV_PART_KNOB`.
By using parts you can apply different styles to sub-elements of a widget. (See below.)
Read the Widget's documentation to learn which parts it uses.
.. _basics_states:
States
------
Widgets can be in a combination of the following states:
- :cpp:enumerator:`LV_STATE_DEFAULT`: Normal, released state
- :cpp:enumerator:`LV_STATE_CHECKED`: Toggled or checked state
- :cpp:enumerator:`LV_STATE_FOCUSED`: Focused via keypad or encoder or clicked via touchpad/mouse
- :cpp:enumerator:`LV_STATE_FOCUS_KEY`: Focused via keypad or encoder but not via touchpad/mouse
- :cpp:enumerator:`LV_STATE_EDITED`: Edit by an encoder
- :cpp:enumerator:`LV_STATE_HOVERED`: Hovered by mouse
- :cpp:enumerator:`LV_STATE_PRESSED`: Being pressed
- :cpp:enumerator:`LV_STATE_SCROLLED`: Being scrolled
- :cpp:enumerator:`LV_STATE_DISABLED`: Disabled
For example, if you press a Widget it will automatically go to the
:cpp:enumerator:`LV_STATE_FOCUSED` and :cpp:enumerator:`LV_STATE_PRESSED` states and when you
release it the :cpp:enumerator:`LV_STATE_PRESSED` state will be removed while the
:cpp:enumerator:`LV_STATE_FOCUSED` state remains active.
To check if a Widget is in a given state use
:cpp:expr:`lv_obj_has_state(widget, LV_STATE_...)`. It will return ``true`` if the
Widget is currently in that state.
To manually add or remove states use:
.. code-block:: c
lv_obj_add_state(widget, LV_STATE_...);
lv_obj_remove_state(widget, LV_STATE_...);
.. _basics_styles:
Styles
------
A style instance contains properties such as background color, border
width, font, etc. that describe the appearance of Widgets.
Styles are carried in :cpp:struct:`lv_style_t` objects. Only their pointer is saved
in the Widgets so they need to be defined as static or global variables. Before
using a style it needs to be initialized with :cpp:expr:`lv_style_init(&style1)`.
After that, properties can be added to configure the style. For example:
.. code-block:: c
static lv_style_t style1;
lv_style_init(&style1);
lv_style_set_bg_color(&style1, lv_color_hex(0xa03080))
lv_style_set_border_width(&style1, 2))
See :ref:`style_properties_overview` for more details.
See :ref:`style_properties` to see the full list.
Styles are assigned using the OR-ed combination of a Widget's part and
state. For example to use this style on the slider's indicator when the
slider is pressed:
.. code-block:: c
lv_obj_add_style(slider1, &style1, LV_PART_INDICATOR | LV_STATE_PRESSED);
If the *part* is :cpp:enumerator:`LV_PART_MAIN` it can be omitted:
.. code-block:: c
lv_obj_add_style(btn1, &style1, LV_STATE_PRESSED); /* Equal to LV_PART_MAIN | LV_STATE_PRESSED */
Similarly, :cpp:enumerator:`LV_STATE_DEFAULT` can be omitted:
.. code-block:: c
lv_obj_add_style(slider1, &style1, LV_PART_INDICATOR); /* Equal to LV_PART_INDICATOR | LV_STATE_DEFAULT */
For :cpp:enumerator:`LV_STATE_DEFAULT` | :cpp:enumerator:`LV_PART_MAIN` simply pass ``0``:
.. code-block:: c
lv_obj_add_style(btn1, &style1, 0); /* Equal to LV_PART_MAIN | LV_STATE_DEFAULT */
Styles can be cascaded (similarly to CSS). This means you can add more
styles to a part of a Widget. For example ``style_btn`` can set a
default button appearance, and ``style_btn_red`` can overwrite the
background color to make the button red:
.. code-block:: c
lv_obj_add_style(btn1, &style_btn, 0);
lv_obj_add_style(btn1, &style1_btn_red, 0);
If a property is not set for the current state, the style with
:cpp:enumerator:`LV_STATE_DEFAULT` will be used. A default value is used if the
property is not defined in the default state.
Some properties (particularly the text-related ones) can be inherited. This
means if a property is not set in a Widget it will be searched for in
its parents. For example, you can set the font once in the screen's
style and all text on that screen will inherit it by default.
Local style properties also can be added to Widgets. This creates a
style which resides inside the Widget and is used only by that Widget:
.. code-block:: c
lv_obj_set_style_bg_color(slider1, lv_color_hex(0x2080bb), LV_PART_INDICATOR | LV_STATE_PRESSED);
To learn all the features of styles see :ref:`styles`.
.. _basics_themes:
Themes
------
Themes are the default styles for Widgets. Styles from a theme are
applied automatically when Widgets are created.
The theme for your application is a compile time configuration set in
``lv_conf.h``.
.. _basics_micropython:
MicroPython
-----------
LVGL can even be used with :ref:`micropython`.
.. code-block:: python
# Initialize
import display_driver
import lvgl as lv
# Create a button with a label
scr = lv.obj()
btn = lv.button(scr)
btn.align(lv.ALIGN.CENTER, 0, 0)
label = lv.label(btn)
label.set_text('Hello World!')
lv.screen_load(scr)
.. _going_deeper:
Going Deeper
*************
There are several good ways ways to gain deeper knowledge of LVGL. Here is one
recommended order of documents to read and things to play with while you are
advancing your knowledge:
1. If not already read, start with :ref:`introduction` page of
the documentation. (5 minutes)
2. Check out the `Online Demos`_ to see LVGL in action. (3 minutes)
3. If not already done, read the :ref:`lvgl_basics` (above). (15 minutes)
4. Set up an LVGL :ref:`simulator`. (10 minutes)
5. Have a look at some :ref:`examples` and their code.
6. Add LVGL to your project. See :ref:`add_lvgl_to_your_project` or check out
the `ready-to-use Projects`_.
7. Read the :ref:`main_components` pages to get a better understanding of the library. (2-3 hours)
8. Skim the documentation of :ref:`widgets` to see what is available.
9. If you have questions go to the `Forum`_.
10. Read the :ref:`contributing` guide to see how you can help to improve LVGL. (15 minutes)
.. _online demos: https://lvgl.io/demos
.. _ready-to-use projects: https://github.com/lvgl?q=lv_port_&type=&language=
.. _forum: https://forum.lvgl.io/
.. _basics_examples:
Basic Examples
**************
Below are several basic examples. They include the application code that produces
the Widget Tree needed to make LVGL render the examples shown. Each example assumes
a LVGL has undergone normal initialization, meaning that a ``lv_display_t`` object
was created and therefore has an :ref:`active_screen`.
.. include:: ../examples/get_started/index.rst

View File

@@ -1,290 +1,11 @@
.. _introduction:
.. _intro-page:
============
Introduction
============
LVGL (Light and Versatile Graphics Library) is a free and open-source graphics library providing everything you need to create an embedded GUI with easy-to-use graphical elements, beautiful visual effects and a low memory footprint.
.. toctree::
:maxdepth: 1
Key features
------------
- Powerful building blocks such as buttons, charts, lists, sliders, images, etc.
- Advanced graphics with animations, anti-aliasing, opacity, smooth scrolling
- Various input devices such as touchpad, mouse, keyboard, encoder, etc.
- Multi-language support with UTF-8 encoding
- Multi-display support, i.e. use multiple TFT, monochrome displays simultaneously
- Fully customizable graphic elements with CSS-like styles
- Hardware independent: use with any microcontroller or display
- Scalable: able to operate with little memory (64 kB Flash, 16 kB RAM)
- OS, external memory and GPU are supported but not required
- Single frame buffer operation even with advanced graphic effects
- Written in C for maximal compatibility (C++ compatible)
- Simulator to start embedded GUI design on a PC without embedded hardware
- Binding to MicroPython
- Tutorials, examples, themes for rapid GUI design
- Documentation is available online and as PDF
- Free and open-source under MIT license
.. _requirements:
Requirements
------------
Basically, every modern controller which is able to drive a display is suitable to run LVGL. The minimal requirements are:
* 16, 32 or 64 bit microcontroller or processor
* > 16 MHz clock speed is recommended
* Flash/ROM: > 64 kB for the very essential components (> 180 kB is recommended)
* RAM:
* Static RAM usage: ~2 kB depending on the used features and object types
* stack: > 2kB (> 8 kB is recommended)
* Dynamic data (heap): > 2 KB (> 48 kB is recommended if using several objects).
Set by :c:macro:`LV_MEM_SIZE` in ``lv_conf.h``.
* Display buffer: > *"Horizontal resolution"* pixels (> 10 *"Horizontal resolution"* is recommended)
* One frame buffer in the MCU or in an external display controller
* C99 or newer compiler
* Basic C (or C++) knowledge:
* `pointers <https://www.tutorialspoint.com/cprogramming/c_pointers.htm>`_.
* `structs <https://www.tutorialspoint.com/cprogramming/c_structures.htm>`_.
* `callbacks <https://www.geeksforgeeks.org/callbacks-in-c/>`_.
:Note: *memory usage may vary depending on architecture, compiler and build options.*
License
-------
The LVGL project (including all repositories) is licensed under `MIT license <https://github.com/lvgl/lvgl/blob/master/LICENCE.txt>`_.
This means you can use it even in commercial projects.
It's not mandatory, but we highly appreciate it if you write a few words about your project in the `My projects <https://forum.lvgl.io/c/my-projects/10>`_ category of the forum or a private message to `lvgl.io <https://lvgl.io/#contact>`_.
Although you can get LVGL for free there is a massive amount of work behind it. It's created by a group of volunteers who made it available for you in their free time.
To make the LVGL project sustainable, please consider :ref:`contributing` to the project.
You can choose from many different ways of contributing See :ref:`contributing` such as simply writing a tweet about you using LVGL, fixing bugs, translating the documentation, or even becoming a maintainer.
Repository layout
-----------------
All repositories of the LVGL project are hosted on `GitHub <https://github.com/lvgl>`_
You will find these repositories there:
* `lvgl <https://github.com/lvgl/lvgl>`_: The library itself with many `examples <https://github.com/lvgl/lvgl/blob/master/examples/>`_ and `demos <https://github.com/lvgl/lvgl/blob/master/demos/>`_.
* `lv_drivers <https://github.com/lvgl/lv_drivers>`_: Display and input device drivers
* `blog <https://github.com/lvgl/blog>`_: Source of the `blog's site <https://blog.lvgl.io>`_
* `sim <https://github.com/lvgl/sim>`_: Source of the `online simulator's site <https://sim.lvgl.io>`_
* `lv_port_* <https://github.com/lvgl?q=lv_port&type=&language=>`_: LVGL ports to development boards or environments
* `lv_binding_* <https://github.com/lvgl?q=lv_binding&type=&language=l>`_: Bindings to other languages
Release policy
--------------
The core repositories follow the rules of `Semantic versioning <https://semver.org/>`_:
* Major version: incompatible API changes. E.g. v5.0.0, v6.0.0
* Minor version: new but backward-compatible functionalities. E.g. v6.1.0, v6.2.0
* Patch version: backward-compatible bug fixes. E.g. v6.1.1, v6.1.2
Tags like `vX.Y.Z` are created for every release.
Release cycle
^^^^^^^^^^^^^
* Bug fixes: Released on demand even weekly
* Minor releases: Every 3-4 months
* Major releases: Approximately yearly
Branches
^^^^^^^^
The core repositories have at least the following branches:
* `master`: latest version, patches are merged directly here.
* `release/vX.Y`: stable versions of the minor releases
* `fix/some-description`: temporary branches for bug fixes
* `feat/some-description`: temporary branches for features
Changelog
^^^^^^^^^
The changes are recorded in :ref:`changelog`.
Version support
^^^^^^^^^^^^^^^
Before v8 the last minor release of each major series was supported for 1 year.
Starting from v8, every minor release is supported for 1 year.
+---------+--------------+--------------+--------+
| Version | Release date | Support end | Active |
+=========+==============+==============+========+
|v5.3 | 1 Feb, 2019 | 1 Feb, 2020 | No |
+---------+--------------+--------------+--------+
|v6.1 | 26 Nov, 2019 | 26 Nov, 2020 | No |
+---------+--------------+--------------+--------+
|v7.11 | 16 Mar, 2021 | 16 Mar, 2022 | No |
+---------+--------------+--------------+--------+
|v8.0 | 1 Jun, 2021 | 1 Jun, 2022 | No |
+---------+--------------+--------------+--------+
|v8.1 | 10 Nov, 2021 | 10 Nov, 2022 | No |
+---------+--------------+--------------+--------+
|v8.2 | 31 Jan, 2022 | 31 Jan, 2023 | No |
+---------+--------------+--------------+--------+
|v8.3 | 6 July, 2022 | 1 Jan, 2025 | Yes |
+---------+--------------+--------------+--------+
|v8.4 | Mar 19, 2024 | Mar 20, 2025 | Yes |
+---------+--------------+--------------+--------+
|v9.0 | Jan 22, 2024 | Jan 22, 2025 | Yes |
+---------+--------------+--------------+--------+
|v9.1 | Mar 20, 2024 | Mar 20, 2025 | Yes |
+---------+--------------+--------------+--------+
|v9.2 | Aug 26, 2024 | Aug 26, 2025 | Yes |
+---------+--------------+--------------+--------+
FAQ
---
Where can I ask questions?
^^^^^^^^^^^^^^^^^^^^^^^^^^
You can ask questions in the forum: `https://forum.lvgl.io/ <https://forum.lvgl.io/>`_.
We use `GitHub issues <https://github.com/lvgl/lvgl/issues>`_ for development related discussion.
You should use them only if your question or issue is tightly related to the development of the library.
Before posting a question, please ready this FAQ section as you might find answer to your issue here too.
Is my MCU/hardware supported?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Every MCU which is capable of driving a display via parallel port, SPI, RGB interface or anything else and fulfills the :ref:`requirements` is supported by LVGL.
This includes:
* "Common" MCUs like STM32F, STM32H, NXP Kinetis, LPC, iMX, dsPIC33, PIC32, SWM341 etc.
* Bluetooth, GSM, Wi-Fi modules like Nordic NRF, Espressif ESP32 and Raspberry Pi Pico W
* Linux with frame buffer device such as /dev/fb0. This includes Single-board computers like the Raspberry Pi
* Anything else with a strong enough MCU and a peripheral to drive a display
Is my display supported?
^^^^^^^^^^^^^^^^^^^^^^^^
LVGL needs just one simple driver function to copy an array of pixels into a given area of the display.
If you can do this with your display then you can use it with LVGL.
Some examples of the supported display types:
* TFTs with 16 or 24 bit color depth
* Monitors with an HDMI port
* Small monochrome displays
* Gray-scale displays
* even LED matrices
* or any other display where you can control the color/state of the pixels
See the :ref:`display_interface` section to learn more.
LVGL doesn't start, randomly crashes or nothing is drawn on the display. What can be the problem?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Try increasing :c:macro:`LV_MEM_SIZE`.
* Be sure your display works without LVGL. E.g. paint it to red on start up.
* Enable :ref:`logging`
* Enable asserts in ``lv_conf.h`` (`LV_USE_ASSERT_...`)
* If you use an RTOS
* increase the stack size of the task which calls :cpp:func:`lv_timer_handler`
* Be sure you used a mutex as described here: :ref:`os_interrupt`
My display driver is not called. What have I missed?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Be sure you are calling :cpp:expr:`lv_tick_inc(x)` in an interrupt and :cpp:func:`lv_timer_handler` in your main ``while(1)``.
Learn more in the :ref:`tick` and :ref:`timer_handler` sections.
Why is the display driver called only once? Only the upper part of the display is refreshed.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Be sure you are calling :cpp:expr:`lv_display_flush_ready(drv)` at the end of your "*display flush callback*".
Why do I see only garbage on the screen?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Probably there a bug in your display driver. Try the following code without using LVGL. You should see a square with red-blue gradient.
.. code-block:: c
#define BUF_W 20
#define BUF_H 10
lv_color_t buf[BUF_W * BUF_H];
lv_color_t * buf_p = buf;
uint16_t x, y;
for(y = 0; y < BUF_H; y++) {
lv_color_t c = lv_color_mix(LV_COLOR_BLUE, LV_COLOR_RED, (y * 255) / BUF_H);
for(x = 0; x < BUF_W; x++){
(*buf_p) = c;
buf_p++;
}
}
lv_area_t a;
a.x1 = 10;
a.y1 = 40;
a.x2 = a.x1 + BUF_W - 1;
a.y2 = a.y1 + BUF_H - 1;
my_flush_cb(NULL, &a, buf);
Why do I see nonsense colors on the screen?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Probably LVGL's color format is not compatible with your display's color format. Check :c:macro:`LV_COLOR_DEPTH` in *lv_conf.h*.
How to speed up my UI?
^^^^^^^^^^^^^^^^^^^^^^
- Turn on compiler optimization and enable cache if your MCU has it
- Increase the size of the display buffer
- Use two display buffers and flush the buffer with DMA (or similar peripheral) in the background
- Increase the clock speed of the SPI or parallel port if you use them to drive the display
- If your display has an SPI port consider changing to a model with a parallel interface because it has much higher throughput
- Keep the display buffer in internal RAM (not in external SRAM) because LVGL uses it a lot and it should have a fast access time
How to reduce flash/ROM usage?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can disable all the unused features (such as animations, file system, GPU etc.) and object types in *lv_conf.h*.
If you are using GCC/CLANG you can add `-fdata-sections -ffunction-sections` compiler flags and `--gc-sections` linker flag to remove unused functions and variables from the final binary. If possible, add the `-flto` compiler flag to enable link-time-optimisation together with `-Os` for GCC or `-Oz` for CLANG.
How to reduce the RAM usage
^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Lower the size of the *Display buffer*
* Reduce :c:macro:`LV_MEM_SIZE` in *lv_conf.h*. This memory is used when you create objects like buttons, labels, etc.
* To work with lower :c:macro:`LV_MEM_SIZE` you can create objects only when required and delete them when they are not needed anymore
How to work with an operating system?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To work with an operating system where tasks can interrupt each other (preemptively) you should protect LVGL related function calls with a mutex.
See the :ref:`os_interrupt` section to learn more.
introduction
basics
add-lvgl-to-your-project/index

304
docs/intro/introduction.rst Normal file
View File

@@ -0,0 +1,304 @@
.. _introduction:
============
Introduction
============
LVGL (Light and Versatile Graphics Library) is a free and open-source graphics
library providing everything you need to create an embedded GUI with easy-to-use
graphical elements, beautiful visual effects, and a low memory footprint.
Key features
------------
- Powerful building blocks such as :ref:`buttons, charts, lists, sliders, images <widgets>`, etc.
- Advanced graphics with animations, anti-aliasing, opacity, smooth scrolling
- Various input devices such as touchpad, mouse, keyboard, encoder, etc.
- Multi-language support with UTF-8 encoding
- Multi-display support, even with mixed color formats
- Fully customizable graphic elements with CSS-like styles
- Hardware independent: use with any microcontroller or display
- Scalable: able to operate with little memory (64 kB Flash, 16 kB RAM)
- :ref:`OS <threading>`, external memory and :ref:`GPU <draw>` are supported but not required
- Single frame buffer operation even with advanced graphic effects
- Written in C for maximal compatibility (C++ compatible)
- :ref:`Simulator <simulator>` to start embedded GUI design on a PC without embedded hardware
- User code developed under similator can be shared with firmware to make UI development more efficient.
- Binding to :ref:`MicroPython`
- Tutorials, examples, themes for rapid GUI design
- Documentation is available online
- Free and open-source under MIT license
.. _requirements:
Requirements
------------
Basically, every modern controller which is able to drive a display is suitable to run LVGL. The minimal requirements are:
* 16, 32 or 64 bit microcontroller or processor
* > 16 MHz clock speed is recommended
* Flash/ROM: > 64 kB for the very essential components (> 180 kB is recommended)
* RAM:
* Static RAM usage: ~2 kB depending on the used features and Widget types
* stack: > 2kB (> 8 kB recommended)
* Dynamic data (heap): > 2 KB (> 48 kB is recommended if using many GUI Widgets).
Set by :c:macro:`LV_MEM_SIZE` in ``lv_conf.h``.
* Display buffer: > *"Horizontal resolution"* pixels (> 10 X *"Horizontal resolution"* is recommended)
* One frame buffer in the MCU or in an external display controller
* C99 or newer compiler
* Basic C (or C++) knowledge:
* `pointers <https://www.tutorialspoint.com/cprogramming/c_pointers.htm>`__.
* `structs <https://www.tutorialspoint.com/cprogramming/c_structures.htm>`__.
* `callbacks <https://www.geeksforgeeks.org/callbacks-in-c/>`__.
.. note::
*Memory usage may vary depending on architecture, compiler and build options.*
License
-------
The LVGL project (including all repositories) is licensed under the `MIT license <https://github.com/lvgl/lvgl/blob/master/LICENCE.txt>`__.
This means you can use it even in commercial projects.
It is not mandatory, but we highly appreciate it if you write a few words about your project in the `My projects <https://forum.lvgl.io/c/my-projects/10>`__ category of the forum or a private message to `lvgl.io <https://lvgl.io/#contact>`__.
Although you can get LVGL for free there is a massive amount of work behind it. It's created by a group of volunteers who made it available for you in their free time.
To make the LVGL project sustainable, please consider :ref:`contributing` to the project.
You can choose from many different ways of contributing See :ref:`contributing` such as simply writing a tweet about you using LVGL, fixing bugs, translating the documentation, or even becoming a maintainer.
Repository layout
-----------------
All repositories of the LVGL project are hosted on `GitHub <https://github.com/lvgl>`_.
You will find these repositories there:
* `lvgl <https://github.com/lvgl/lvgl>`__: The library itself with many `examples <https://github.com/lvgl/lvgl/blob/master/examples/>`_ and `demos <https://github.com/lvgl/lvgl/blob/master/demos/>`__.
* `lv_drivers <https://github.com/lvgl/lv_drivers>`__: Display and input device drivers
* `blog <https://github.com/lvgl/blog>`__: Source of the `blog's site <https://blog.lvgl.io>`__
* `sim <https://github.com/lvgl/sim>`__: Source of the `online simulator's site <https://sim.lvgl.io>`__
* `lv_port_* <https://github.com/lvgl?q=lv_port&type=&language=>`__: LVGL ports to development boards or environments
* `lv_binding_* <https://github.com/lvgl?q=lv_binding&type=&language=l>`__: Bindings to other languages
Release policy
--------------
The core repositories follow the rules of `Semantic Versioning <https://semver.org/>`__:
* Major version: incompatible API changes. E.g. v5.0.0, v6.0.0
* Minor version: new but backward-compatible functionalities. E.g. v6.1.0, v6.2.0
* Patch version: backward-compatible bug fixes. E.g. v6.1.1, v6.1.2
Tags like `vX.Y.Z` are created for every release.
Release cycle
^^^^^^^^^^^^^
* Bug fixes: released on demand even weekly
* Minor releases: every 3-4 months
* Major releases: approximately yearly
Branches
^^^^^^^^
The core repositories have at least the following branches:
* `master`: latest version, patches are merged directly here
* `release/vX.Y`: stable versions of the minor releases
* `fix/some-description`: temporary branches for bug fixes
* `feat/some-description`: temporary branches for features
Change log
^^^^^^^^^^
The changes are recorded in :ref:`changelog`.
Version support
^^^^^^^^^^^^^^^
Before v8 the last minor release of each major series was supported for 1 year.
Starting from v8, every minor release is supported for 1 year.
+---------+--------------+--------------+--------+
| Version | Release date | Support end | Active |
+=========+==============+==============+========+
|v5.3 | 1 Feb, 2019 | 1 Feb, 2020 | No |
+---------+--------------+--------------+--------+
|v6.1 | 26 Nov, 2019 | 26 Nov, 2020 | No |
+---------+--------------+--------------+--------+
|v7.11 | 16 Mar, 2021 | 16 Mar, 2022 | No |
+---------+--------------+--------------+--------+
|v8.0 | 1 Jun, 2021 | 1 Jun, 2022 | No |
+---------+--------------+--------------+--------+
|v8.1 | 10 Nov, 2021 | 10 Nov, 2022 | No |
+---------+--------------+--------------+--------+
|v8.2 | 31 Jan, 2022 | 31 Jan, 2023 | No |
+---------+--------------+--------------+--------+
|v8.3 | 6 July, 2022 | 1 Jan, 2025 | Yes |
+---------+--------------+--------------+--------+
|v8.4 | Mar 19, 2024 | Mar 20, 2025 | Yes |
+---------+--------------+--------------+--------+
|v9.0 | Jan 22, 2024 | Jan 22, 2025 | Yes |
+---------+--------------+--------------+--------+
|v9.1 | Mar 20, 2024 | Mar 20, 2025 | Yes |
+---------+--------------+--------------+--------+
|v9.2 | Aug 26, 2024 | Aug 26, 2025 | Yes |
+---------+--------------+--------------+--------+
FAQ
---
Where can I ask questions?
^^^^^^^^^^^^^^^^^^^^^^^^^^
You can ask questions in the forum: https://forum.lvgl.io/.
We use `GitHub issues <https://github.com/lvgl/lvgl/issues>`_ for development related discussion.
You should use them only if your question or issue is tightly related to the development of the library.
Before posting a question, please read this FAQ section since you might find the answer to your issue here as well.
Is my MCU/hardware supported?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Every MCU which is capable of driving a display via parallel port, SPI, RGB interface or anything else and fulfills the :ref:`requirements` is supported by LVGL.
This includes:
* "Common" MCUs like STM32F, STM32H, NXP Kinetis, LPC, iMX, dsPIC33, PIC32, SWM341 etc.
* Bluetooth, GSM, Wi-Fi modules like Nordic NRF, Espressif ESP32 and Raspberry Pi Pico W
* Linux with frame buffer device such as /dev/fb0. This includes Single-board computers like the Raspberry Pi
* Anything else with a strong enough MCU and a peripheral to drive a display
Is my display supported?
^^^^^^^^^^^^^^^^^^^^^^^^
LVGL needs just one simple driver function to copy an array of pixels into a given area of the display.
If you can do this with your display then you can use it with LVGL.
Some examples of the supported display types:
* TFTs with 16 or 24 bit color depth
* Monitors with an HDMI port
* Small monochrome displays
* Gray-scale displays
* even LED matrices
* or any other display where you can control the color/state of the pixels
See the :ref:`display` section to learn more.
LVGL doesn't start, randomly crashes or nothing is drawn on the display. What can be the problem?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Try increasing :c:macro:`LV_MEM_SIZE`.
* Be sure your display works without LVGL. E.g. paint it to red on start up.
* Enable :ref:`logging`.
* Enable assertions in ``lv_conf.h`` (``LV_USE_ASSERT_...``).
* If you use an RTOS:
* Increase the stack size of the task that calls :cpp:func:`lv_timer_handler`.
* Be sure you are using one of the methods for thread management as described in :ref:`threading`.
My display driver is not called. What have I missed?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Be sure you are calling :cpp:expr:`lv_tick_inc(x)` as prescribed in
:ref:`tick_interface` and are calling :cpp:func:`lv_timer_handler` as prescribed in
:ref:`timer_handler`.
Learn more in the :ref:`tick_interface` and :ref:`timer_handler` sections.
Why is the display driver called only once? Only the upper part of the display is refreshed.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Be sure you are calling :cpp:expr:`lv_display_flush_ready(drv)` at the end of your
"*display flush callback*" as per :ref:`flush_callback` section.
Why do I see only garbage on the screen?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There is probably a bug in your display driver. Try the following code without using LVGL. You should see a square with red-blue gradient.
.. code-block:: c
#define BUF_W 20
#define BUF_H 10
lv_color_t buf[BUF_W * BUF_H];
lv_color_t * buf_p = buf;
uint16_t x, y;
for(y = 0; y < BUF_H; y++) {
lv_color_t c = lv_color_mix(LV_COLOR_BLUE, LV_COLOR_RED, (y * 255) / BUF_H);
for(x = 0; x < BUF_W; x++){
(*buf_p) = c;
buf_p++;
}
}
lv_area_t a;
a.x1 = 10;
a.y1 = 40;
a.x2 = a.x1 + BUF_W - 1;
a.y2 = a.y1 + BUF_H - 1;
my_flush_cb(NULL, &a, buf);
Why do I see nonsense colors on the screen?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The configured LVGL color format is probably not compatible with your display's color format. Check :c:macro:`LV_COLOR_DEPTH` in *lv_conf.h*.
How do I speed up my UI?
^^^^^^^^^^^^^^^^^^^^^^^^
- Turn on compiler optimization and enable instruction- and data-caching if your MCU has them.
- Increase the size of the display buffer.
- Use two display buffers and flush the buffer with DMA (or similar peripheral) in the background.
- Increase the clock speed of the SPI or parallel port if you use them to drive the display.
- If your display has an SPI port consider changing to a model with a parallel interface because it has much higher throughput.
- Keep the display buffer in internal RAM (not in external SRAM) because LVGL uses it a lot and it should have fast access time.
How do I reduce flash/ROM usage?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can disable unused features (such as animations, file system, GPU etc.) and widget types in *lv_conf.h*.
If you are using GCC/CLANG you can add `-fdata-sections -ffunction-sections` compiler flags and `--gc-sections` linker flag to remove unused functions and variables from the final binary. If possible, add the `-flto` compiler flag to enable link-time-optimisation together with `-Os` for GCC or `-Oz` for CLANG.
How do I reduce RAM usage?
^^^^^^^^^^^^^^^^^^^^^^^^^^
* Lower the size of the *Display buffer*.
* Reduce :c:macro:`LV_MEM_SIZE` in *lv_conf.h*. This memory is used when you create Widgets like buttons, labels, etc.
* To work with lower :c:macro:`LV_MEM_SIZE` you can create Widgets only when required and delete them when they are not needed anymore.
How do I use LVGL with an operating system?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To work with an operating system where tasks can interrupt each other (preemptively),
you must ensure that no LVGL function call be called while another LVGL call is in
progress. There are several ways to do this. See the :ref:`threading` section to
learn more.