diff --git a/docs/details/main-components/fs.rst b/docs/details/main-components/fs.rst index 2713eb0be..281d2061e 100644 --- a/docs/details/main-components/fs.rst +++ b/docs/details/main-components/fs.rst @@ -26,7 +26,7 @@ Adding a driver Registering a driver -------------------- -To add a driver, a :cpp:type:`lv_fs_drv_t` needs to be initialized like below. +To add a driver, a :cpp:type:`lv_fs_drv_t` needs to be initialized like the example below. The :cpp:type:`lv_fs_drv_t` needs to be static, global or dynamically allocated and not a local variable. @@ -75,7 +75,7 @@ The prototype of ``open_cb`` looks like this: The return value is a pointer to a *file object* that describes the opened file or ``NULL`` if there were any issues (e.g. the file wasn't found). The returned file object will be passed to other file system -related callbacks. (see below) +related callbacks. (See below.) Other callbacks --------------- @@ -88,8 +88,8 @@ like this: lv_fs_res_t (*write_cb)(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); For ``file_p``, LVGL passes the return value of ``open_cb``, ``buf`` is -the data to write, ``btw`` is the Bytes To Write, ``bw`` is the actually -written bytes. +the data to write, ``btw`` is the Bytes To Write, ``bw`` is the actual +bytes written during the call. For a template of these callbacks see `lv_fs_template.c `__. @@ -135,7 +135,7 @@ practice to insert a ``'/'`` in front of each directory name. break; } - /* fn is empty, if not more files to read */ + /* fn is empty, if no more files to read */ if(strlen(fn) == 0) { break; } @@ -151,7 +151,7 @@ Use drives for images :ref:`Image ` Widgets can be opened from files as well (besides variables stored in the compiled program). -To use files in image widgets the following callbacks are required: +To use files in Image Widgets the following callbacks are required: - open - close diff --git a/docs/details/main-components/timer.rst b/docs/details/main-components/timer.rst index 1b15837ec..c5fca96b9 100644 --- a/docs/details/main-components/timer.rst +++ b/docs/details/main-components/timer.rst @@ -4,25 +4,43 @@ Timer (lv_timer) ================ -LVGL has a built-in timer system. You can register a function to have it -be called periodically. The timers are handled and called in +LVGL has a built-in Timer system. You can register a function to have it +be called periodically. The Timers are handled and called in :cpp:func:`lv_timer_handler`, which needs to be called every few milliseconds. See :ref:`timer_handler` for more information. -Timers are non-preemptive, which means a timer cannot interrupt another -timer. Therefore, you can call any LVGL related function in a timer. +By default, LVGL itself uses Timers to: -Create a timer -************** +- refresh each display --- during the creation of each :ref:`Display`, a Timer is + created for that Display. That Timer refreshes the display based on the configured + value of :c:macro:`LV_DEF_REFR_PERIOD`, and also sends all display-related events, + like :cpp:enumerator:`LV_EVENT_REFR_START`, :cpp:enumerator:`LV_EVENT_REFR_READY`, + etc. +- read input devices --- during the creation of each :ref:`indev`, a Timer is + created for that Input Device based on the configured value of + :c:macro:`LV_DEF_REFR_PERIOD`. That Timer causes that input device to be read and + also sends all input-device-related events, like :cpp:enumerator:`LV_EVENT_CLICKED`, + :cpp:enumerator:`LV_EVENT_PRESSED`, etc. +- update system-monitor values --- if :c:macro:`LV_USE_SYSMON` is set to ``1`` in + ``lv_conf.h``, one or more timers are created to periodically compute and + monitor system performance statistics and LVGL's memory usage. -To create a new timer, use -:cpp:expr:`lv_timer_create(timer_cb, period_ms, user_data)`. It will create an -:cpp:type:`lv_timer_t` ``*`` variable, which can be used later to modify the -parameters of the timer. :cpp:func:`lv_timer_create_basic` can also be used. -This allows you to create a new timer without specifying any parameters. +Timers are non-preemptive, which means a Timer cannot interrupt another +Timer. Therefore, you can call any LVGL related function in a Timer. -A timer callback should have a ``void (*lv_timer_cb_t)(lv_timer_t *)`` -prototype. + + +Creating a Timer +**************** + +To create a new Timer, use +:cpp:expr:`lv_timer_create(timer_cb, period_ms, user_data)`. It returns an +:cpp:type:`lv_timer_t` ``*`` which can be used later to modify the +parameters of the Timer, pause it, or delete it when it is no longer needed. +:cpp:func:`lv_timer_create_basic` can also be used to create a new Timer without +specifying any parameters. + +A Timer callback should have this prototype: ``void (*lv_timer_cb_t)(lv_timer_t *)``. For example: @@ -37,7 +55,7 @@ For example: /* Do something with LVGL */ if(something_happened) { something_happened = false; - lv_button_create(lv_screen_active(), NULL); + lv_button_create(lv_screen_active()); } } @@ -46,74 +64,127 @@ For example: static uint32_t user_data = 10; lv_timer_t * timer = lv_timer_create(my_timer, 500, &user_data); + + Ready and Reset *************** -:cpp:expr:`lv_timer_ready(timer)` makes a timer run on the next call of +:cpp:expr:`lv_timer_ready(timer)` makes a Timer run on the next call of :cpp:func:`lv_timer_handler`. -:cpp:expr:`lv_timer_reset(timer)` resets the period of a timer. It will be -called again after the defined period of milliseconds has elapsed. +:cpp:expr:`lv_timer_reset(timer)` resets the period of a Timer. It will be +called again after its currently-set period (in milliseconds) has elapsed. -Set parameters -************** -You can modify some timer parameters later: -- :cpp:expr:`lv_timer_set_cb(timer, new_cb)` -- :cpp:expr:`lv_timer_set_period(timer, new_period)` - -Repeat count -************ - -You can make a timer repeat only a given number of times with -:cpp:expr:`lv_timer_set_repeat_count(timer, count)`. The timer will -automatically be deleted after it's called the defined number of times. -Set the count to ``-1`` to repeat indefinitely. - -Enable and Disable +Setting Parameters ****************** -You can enable or disable a timer with :cpp:expr:`lv_timer_enable(en)`. +You can modify these Timer parameters at any time during its life: + +- :cpp:expr:`lv_timer_set_cb(timer, new_cb)` +- :cpp:expr:`lv_timer_set_period(timer, new_period_ms)` +- :cpp:expr:`lv_timer_set_user_data(timer, user_data)` + + + +Repeat Count +************ + +When a Timer is created, its repeat-count is set to ``-1`` to cause it to repeat +indefinitely. You can make a Timer repeat only a given number of times with +:cpp:expr:`lv_timer_set_repeat_count(timer, count)`. By default, once the Timer has +run ``count`` times, it will be automatically deleted. + +You can use :cpp:expr:`lv_timer_set_auto_delete(timer, false)` if you want the timer +to instead be paused after it has run ``count`` times. This can be handy if you +reuse that timer repeatedly and want to avoid the CPU and :cpp:func:`lv_malloc` +overhead of repeatedly creating and deleting a timer. If you use this option, you +will need to set its repeat count (to either ``-1`` or a positive repeat count, since +it will have decremented to ``0``) and :ref:`resume ` it to +make it active again. + + + +.. _timer_pause_and_resume: Pause and Resume **************** -:cpp:expr:`lv_timer_pause(timer)` pauses the specified timer. +:cpp:expr:`lv_timer_pause(timer)` pauses the specified Timer. -:cpp:expr:`lv_timer_resume(timer)` resumes the specified timer. +:cpp:expr:`lv_timer_resume(timer)` resumes the specified Timer. -Measure idle time -***************** + + +Measuring Idle Time +******************* You can get the idle percentage time of :cpp:func:`lv_timer_handler` with -:cpp:func:`lv_timer_get_idle`. Note that, it doesn't measure the idle time of -the overall system, only :cpp:func:`lv_timer_handler`. It can be misleading if -you use an operating system and call :cpp:func:`lv_timer_handler` in a timer, as -it won't actually measure the time the OS spends in an idle thread. +:cpp:func:`lv_timer_get_idle`. Note that it does not measure the idle time of +the overall system, only of :cpp:func:`lv_timer_handler`. This can be misleading if +you are using an operating system and DMA and/or GPU are used during rendering, as it +does not actually measure the time the OS spends in an idle thread. -Timer handler resume callback +If you are using an OS and wish to get the time the CPU is spending in an idle +thread, one way of doing so is configuring :c:macro:`LV_USE_SYSMON` and +:c:macro:`LV_USE_PERF_MONITOR` to ``1`` in ``lv_conf.h`` (if they are not already), +and setting the macro :c:macro:`LV_SYSMON_GET_IDLE` to the name of a function that +fetches the percent of CPU time spent in the OS's idle thread. An example of such +a function is :cpp:func:`lv_os_get_idle_percent` in ``lv_freertos.c``. While the +configuration is set this way, some system performance statistics (including CPU +load) will appear on the display in a partially-transparent label whose location is +set by the :c:macro:`LV_USE_PERF_MONITOR_POS` macro. + + + +Enable and Disable +****************** + +You can temporarily disable Timer handling with :cpp:expr:`lv_timer_enable(false)`. +Be advised: this also pauses handling of Timers that refresh Display(s) and read +from input devices, so don't forget to re-enable it with +:cpp:expr:`lv_timer_enable(true)` as soon as the need for the pause is over. + + + +Timer Handler Resume Callback ***************************** -When the `lv_timer_handler` is stopped, if you want to pay attention to the wake-up -timing of the `lv_timer_handler`, you can set a resume callback using -:cpp:expr:`lv_timer_handler_set_resume_cb(cb, user_data)`. -The callback should have a ``void (*lv_timer_handler_resume_cb_t)(void*)`` prototype. +When the Timer system has been disabled (causing :cpp:func:`lv_timer_handler` to +return early before it has processed any timers), if you want to take some action +when the Timer system is re-enabled again, set a resume callback using +:cpp:expr:`lv_timer_handler_set_resume_cb(cb, user_data)`. The callback should have +this prototype: ``void (*lv_timer_handler_resume_cb_t)(void*)``. + + Asynchronous calls ****************** -In some cases, you can't perform an action immediately. For example, you -can't delete a Widget because something else is still using it, or you -don't want to block the execution now. For these cases, -:cpp:expr:`lv_async_call(my_function, data_p)` can be used to call -``my_function`` on the next invocation of :cpp:func:`lv_timer_handler`. -``data_p`` will be passed to the function when it's called. Note that -only the data pointer is saved, so you need to ensure that the variable -will be "alive" while the function is called. It can be *static*, global -or dynamically allocated data. If you want to cancel an asynchronous -call, call :cpp:expr:`lv_async_call_cancel(my_function, data_p)`, which will -clear all asynchronous calls matching ``my_function`` and ``data_p``. +There are several cases in which you may not want to perform an action immediately. +Some examples are: + +- you cannot delete a Widget because something else is still using it, +- you don't want to block execution now, or +- you detect the need to delete a Widget in a thread other than the thread making + LVGL calls (e.g. in a case where you are using a :ref:`Gateway Thread ` to make all LVGL calls in a multi-threaded environment). + +For these cases, +:cpp:expr:`lv_async_call(my_function, data_p)` can be used to call ``my_function`` on +the next invocation of :cpp:func:`lv_timer_handler`. As a side effect, this also +ensures it is called in a thread in which it is safe to make LVGL calls. +``data_p`` will be passed to the function when it's called. Note that only the data's +pointer is saved, so whatever it is pointing to needs to remain valid until the +function is called, so it can point to ``static``, global or dynamically allocated +data. If you want to cancel an asynchronous call, call +:cpp:expr:`lv_async_call_cancel(my_function, data_p)`, which will remove all +asynchronous calls matching ``my_function`` and ``data_p``. + +Note that if :cpp:expr:`lv_async_call(my_function, data_p)` is called from a thread +other than the one that normally makes LVGL calls, you are still obligated to protect +the LVGL data structure using a MUTEX. For example: @@ -132,14 +203,18 @@ For example: /* Do something with the Widget on the current screen */ /* Delete screen on next call of `lv_timer_handler`, not right now. */ + lv_lock(); lv_async_call(my_screen_clean_up, lv_screen_active()); + lv_unlock(); /* The screen is still valid so you can do other things with it */ If you just want to delete a Widget and don't need to clean anything up -in ``my_screen_cleanup`` you could just use :cpp:func:`lv_obj_delete_async` which +in ``my_screen_cleanup`` you could just use :cpp:expr:`lv_obj_delete_async(widget)` which will delete the Widget on the next call to :cpp:func:`lv_timer_handler`. + + .. _timer_api: API