feat(demos): add demo for the OSAL (#6182)

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Fabian Blatz 2024-06-01 04:46:13 +02:00 committed by GitHub
parent 4ab68c5dc1
commit 70ed004ef3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 311 additions and 11 deletions

View File

@ -7,8 +7,10 @@ Operating system and interrupts
LVGL is **not thread-safe** by default.
However, in the following conditions it's valid to call LVGL related
functions: - In *events*. Learn more in :ref:`events`. -
In *lv_timer*. Learn more in :ref:`timer`.
functions:
- In *events*. Learn more in :ref:`events`.
- In *lv_timer*. Learn more in :ref:`timer`.
Tasks and threads
-----------------
@ -20,19 +22,26 @@ around every LVGL (``lv_...``) related function call and code. This way
you can use LVGL in a real multitasking environment. Just make use of a
mutex to avoid the concurrent calling of LVGL functions.
LVGL has a built-in mutex which can be used with:
- :cpp:func:`lv_lock()` and :cpp:func:`lv_lock_isr()`
- :cpp:func:`lv_unlock()`
These functions are called internally in :cpp:func:`lv_timer_handler`
and the users need to call them only from their own therads.
To enable ``lv_lock/lv_unlock`` ``LV_USE_OS`` needs to be set to other
than ``LV_OS_NONE``.
Here is some pseudocode to illustrate the concept:
.. code:: c
static mutex_t lvgl_mutex;
void lvgl_thread(void)
{
while(1) {
uint32_t time_till_next;
mutex_lock(&lvgl_mutex);
time_till_next = lv_timer_handler();
mutex_unlock(&lvgl_mutex);
time_till_next = lv_timer_handler(); /*lv_lock/lv_unlock is called internally*/
thread_sleep(time_till_next); /* sleep for a while */
}
}
@ -40,15 +49,15 @@ Here is some pseudocode to illustrate the concept:
void other_thread(void)
{
/* You must always hold the mutex while using LVGL APIs */
mutex_lock(&lvgl_mutex);
lv_lock();
lv_obj_t *img = lv_image_create(lv_screen_active());
mutex_unlock(&lvgl_mutex);
lv_unlock();
while(1) {
mutex_lock(&lvgl_mutex);
lv_lock();
/* change to the next image */
lv_image_set_src(img, next_image);
mutex_unlock(&lvgl_mutex);
lv_unlock();
thread_sleep(2000);
}
}

View File

@ -21,6 +21,7 @@ extern "C" {
#include "layouts/lv_example_layout.h"
#include "libs/lv_example_libs.h"
#include "others/lv_example_others.h"
#include "porting/osal/lv_example_osal.h"
#include "scroll/lv_example_scroll.h"
#include "styles/lv_example_style.h"
#include "widgets/lv_example_widgets.h"

View File

@ -0,0 +1,95 @@
/**
* @file lv_example_osal.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void counter_button_event_cb(lv_event_t * e);
static void increment_thread_entry(void * user_data);
/**********************
* STATIC VARIABLES
**********************/
static lv_thread_sync_t press_sync;
static lv_thread_t increment_thread;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_example_osal(void)
{
lv_obj_t * counter_button;
counter_button = lv_button_create(lv_screen_active());
lv_obj_align(counter_button, LV_ALIGN_CENTER, 0, -15);
lv_obj_add_event_cb(counter_button, counter_button_event_cb, LV_EVENT_CLICKED, NULL);
if(lv_thread_sync_init(&press_sync) != LV_RESULT_OK) {
LV_LOG_ERROR("Error initializing thread sync");
}
if(lv_thread_init(&increment_thread, LV_THREAD_PRIO_MID, increment_thread_entry, 2048, NULL) != LV_RESULT_OK) {
LV_LOG_ERROR("Error initializing thread");
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void counter_button_event_cb(lv_event_t * e)
{
LV_UNUSED(e);
if(lv_thread_sync_signal(&press_sync) != LV_RESULT_OK) {
LV_LOG_ERROR("Error signaling thread sync");
}
}
static void increment_thread_entry(void * user_data)
{
LV_UNUSED(user_data);
lv_obj_t * counter_label;
uint32_t press_count = 0;
lv_lock();
counter_label = lv_label_create(lv_scr_act());
lv_obj_align(counter_label, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text_fmt(counter_label, "Pressed %u times", press_count);
lv_unlock();
while(true) {
if(lv_thread_sync_wait(&press_sync) != LV_RESULT_OK) {
LV_LOG_ERROR("Error awaiting thread sync");
}
press_count += 1;
lv_lock();
lv_label_set_text_fmt(counter_label, "Pressed %u times", press_count);
lv_unlock();
}
}
#endif

View File

@ -0,0 +1,38 @@
/**
* @file lv_example_osal.h
*
*/
#ifndef LV_EXAMPLE_OSAL_H
#define LV_EXAMPLE_OSAL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_example_osal(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_EXAMPLE_OSAL_H*/

View File

@ -1028,6 +1028,7 @@
/*Vector graphic demo*/
#define LV_USE_DEMO_VECTOR_GRAPHIC 0
/*--END OF LV_CONF_H--*/
#endif /*LV_CONF_H*/

View File

@ -27,6 +27,7 @@ extern "C" {
#include "../misc/lv_log.h"
#include "../misc/lv_style.h"
#include "../misc/lv_timer.h"
#include "../osal/lv_os.h"
#include "../others/sysmon/lv_sysmon.h"
#include "../stdlib/builtin/lv_tlsf.h"
@ -210,6 +211,10 @@ typedef struct _lv_global_t {
struct _lv_nuttx_ctx_t * nuttx_ctx;
#endif
#if LV_USE_OS != LV_OS_NONE
lv_mutex_t lv_general_mutex;
#endif
void * user_data;
} lv_global_t;

View File

@ -3349,6 +3349,7 @@
#endif
/*----------------------------------
* End of parsing lv_conf_template.h
-----------------------------------*/

View File

@ -24,6 +24,8 @@
#include "draw/lv_draw.h"
#include "misc/lv_async.h"
#include "misc/lv_fs.h"
#include "osal/lv_os_private.h"
#if LV_USE_DRAW_VGLITE
#include "draw/nxp/vglite/lv_draw_vglite.h"
#endif
@ -160,6 +162,8 @@ void lv_init(void)
lv_profiler_builtin_init(&profiler_config);
#endif
lv_os_init();
_lv_timer_core_init();
_lv_fs_init();

View File

@ -78,6 +78,8 @@ LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void)
}
LV_PROFILER_BEGIN;
lv_lock();
uint32_t handler_start = lv_tick_get();
if(handler_start == 0) {
@ -139,7 +141,10 @@ LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void)
state_p->already_running = false; /*Release the mutex*/
LV_TRACE_TIMER("finished (%" LV_PRIu32 " ms until the next timer call)", time_until_next);
lv_unlock();
LV_PROFILER_END;
return time_until_next;
}

71
src/osal/lv_os.c Normal file
View File

@ -0,0 +1,71 @@
/**
* @file lv_os.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_os.h"
#include "lv_os_private.h"
#include "../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define lv_general_mutex LV_GLOBAL_DEFAULT()->lv_general_mutex
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_os_init(void)
{
#if LV_USE_OS != LV_OS_NONE
lv_mutex_init(&lv_general_mutex);
#endif /*LV_USE_OS != LV_OS_NONE*/
}
void lv_lock(void)
{
#if LV_USE_OS != LV_OS_NONE
lv_mutex_lock(&lv_general_mutex);
#endif /*LV_USE_OS != LV_OS_NONE*/
}
lv_result_t lv_lock_isr(void)
{
#if LV_USE_OS != LV_OS_NONE
return lv_mutex_lock_isr(&lv_general_mutex);
#else /*LV_USE_OS != LV_OS_NONE*/
return LV_RESULT_OK;
#endif /*LV_USE_OS != LV_OS_NONE*/
}
void lv_unlock(void)
{
#if LV_USE_OS != LV_OS_NONE
lv_mutex_unlock(&lv_general_mutex);
#endif /*LV_USE_OS != LV_OS_NONE*/
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@ -145,6 +145,29 @@ lv_result_t lv_thread_sync_signal(lv_thread_sync_t * sync);
*/
lv_result_t lv_thread_sync_delete(lv_thread_sync_t * sync);
/**
* Lock LVGL's general mutex.
* LVGL is not thread safe, so a mutex is used to avoid executing multiple LVGL functions at the same time
* from different threads. It shall be called when calling LVGL functions from threads
* different than lv_timer_handler's thread. It doesn't need to be called in LVGL events because
* they are called from lv_timer_handler().
* It is called internally in lv_timer_handler().
*/
void lv_lock(void);
/**
* Same as `lv_lock()` but can be called from an interrupt.
* @return LV_RESULT_OK: success; LV_RESULT_INVALID: failure
*/
lv_result_t lv_lock_isr(void);
/**
* The pair of `lv_lock()` and `lv_lock_isr()`.
* It unlocks LVGL general mutex.
* It is called internally in lv_timer_handler().
*/
void lv_unlock(void);
/**********************
* MACROS
**********************/

47
src/osal/lv_os_private.h Normal file
View File

@ -0,0 +1,47 @@
/**
* @file lv_os_private.h
*
*/
#ifndef LV_OS_PRIVATE_H
#define LV_OS_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* OS OPTIONS
*********************/
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the OS layer
*/
void lv_os_init(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OS_PRIVATE_H*/