From 65a52f2f2d0e2fa61f243bde7a0ccca8e2810a25 Mon Sep 17 00:00:00 2001 From: Victor Wheeler Date: Fri, 6 Dec 2024 03:43:44 -0700 Subject: [PATCH] feat(docs): update threading details in answer to #7313 (#7342) --- .../connecting_lvgl.rst | 47 +++++++++++++++---- .../add-lvgl-to-your-project/threading.rst | 17 +++++-- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/docs/intro/add-lvgl-to-your-project/connecting_lvgl.rst b/docs/intro/add-lvgl-to-your-project/connecting_lvgl.rst index 867775e61..5211c7f9e 100644 --- a/docs/intro/add-lvgl-to-your-project/connecting_lvgl.rst +++ b/docs/intro/add-lvgl-to-your-project/connecting_lvgl.rst @@ -56,10 +56,10 @@ input, firing events, animations, etc. 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: + calling :cpp:expr:`lv_tick_set_cb(my_get_milliseconds)`. + :cpp:expr:`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);``, where ``my_tick_get_cb`` is: @@ -75,12 +75,43 @@ There are two ways to provide this information to LVGL: 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. + called from an interrupt if writing to a ``uint32_t`` value is atomic on your + platform. See below and the :ref:`threading` section to learn more. -The ticks (milliseconds) should be independent from any other activities of the MCU. +Either way, the writing of the ``uint32_t`` Tick value must be :ref:`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 :cpp:expr:`lv_tick_set_cb(my_get_milliseconds)`: -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 + + /** + * @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 :c:func:`lv_timer_handler` varies from call to call and +thus the delay it introduces cannot be known. .. code-block:: c diff --git a/docs/intro/add-lvgl-to-your-project/threading.rst b/docs/intro/add-lvgl-to-your-project/threading.rst index 53edea76f..c08cdda22 100644 --- a/docs/intro/add-lvgl-to-your-project/threading.rst +++ b/docs/intro/add-lvgl-to-your-project/threading.rst @@ -16,12 +16,12 @@ Thread In "bare-metal" implementations (i.e. no OS), threads include: - the main thread executing a while(1) loop that runs the system, and - - interrupts. + - interrupt service routines (ISRs). When running under an OS, threads include: - each task (or process), - - interrupts, and + - ISRs, and - advanced OSes can have multiple "execution threads" within a processes. .. _atomic operation: @@ -93,8 +93,9 @@ before any other LVGL function is started. 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) + - :cpp:func:`lv_tick_inc` (if writing to a ``uint32_t`` is atomic on your + platform; see :ref:`tick_interface` for more information) and + - :cpp:func:`lv_display_flush_ready` (:ref:`flush_callback` for more information) The reason this is okay is that the LVGL data changed by them is :ref:`atomic `. @@ -105,6 +106,14 @@ before any other LVGL function is started. If you are using an OS, there are a few other options. See below. +Ensuring Time Updates are Atomic +-------------------------------- +For LVGL's time-related tasks to be reliable, the time updates via the Tick Interface +must be reliable and the Tick Value must appear :ref:`atomic ` to LVGL. See +:ref:`tick_interface` for details. + + + .. _tasks: Tasks