feat(docs): reorganize docs (#7136)
This commit is contained in:
28
docs/intro/add-lvgl-to-your-project/building_lvgl.rst
Normal file
28
docs/intro/add-lvgl-to-your-project/building_lvgl.rst
Normal 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
|
||||
|
||||
93
docs/intro/add-lvgl-to-your-project/configuration.rst
Normal file
93
docs/intro/add-lvgl-to-your-project/configuration.rst
Normal 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.
|
||||
|
||||
131
docs/intro/add-lvgl-to-your-project/connecting_lvgl.rst
Normal file
131
docs/intro/add-lvgl-to-your-project/connecting_lvgl.rst
Normal 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`
|
||||
25
docs/intro/add-lvgl-to-your-project/getting_lvgl.rst
Normal file
25
docs/intro/add-lvgl-to-your-project/getting_lvgl.rst
Normal 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.
|
||||
|
||||
18
docs/intro/add-lvgl-to-your-project/index.rst
Normal file
18
docs/intro/add-lvgl-to-your-project/index.rst
Normal 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
|
||||
|
||||
11
docs/intro/add-lvgl-to-your-project/other_platforms.rst
Normal file
11
docs/intro/add-lvgl-to-your-project/other_platforms.rst
Normal 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.
|
||||
|
||||
|
||||
277
docs/intro/add-lvgl-to-your-project/threading.rst
Normal file
277
docs/intro/add-lvgl-to-your-project/threading.rst
Normal 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.
|
||||
|
||||
|
||||
|
||||
63
docs/intro/add-lvgl-to-your-project/timer_handler.rst
Normal file
63
docs/intro/add-lvgl-to-your-project/timer_handler.rst
Normal 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
|
||||
***
|
||||
Reference in New Issue
Block a user