Description
Is there any good practice example of how to write a thread gateway for the lvgl lib ?
https://docs.lvgl.io/9.3/details/integration/adding-lvgl-to-your-project/threading.html#gateway-thread
I am already running “lv_task_handler()” in a separate task, but i just need to use a mutex almost every time I access functions from the lvgl lib to work.
It works well using mutex obviously, but I would like to explore this gateway thread design pattern.
Task to run only lv_task_handler() each 5 ms, in my code:
static void littleVgl_task_handler( void* pvParameters )
{
TickType_t* freq = (TickType_t*) pvParameters;
TickType_t xFrequency = pdMS_TO_TICKS( *freq ); // The macro pdMS_TO_TICKS() can be used to convert milliseconds into ticks.
while(1) // execulta a cada 5 ms.
{
my_app_lvgl_semaphore_take();
lv_task_handler();
my_app_lvgl_semaphore_give();
// Wait for the next cycle.
vTaskDelay( xFrequency ); // Se quiser usar vTaskSuspend e VtaskResume da mherda.
}
}
lv_tick_inc( 1 ) i am executing in a timer interrupt each 1 ms.
What MCU/Processor/Board and compiler are you using?
ESP32-S3
What do you want to achieve?
I better and organized way to write a ihm code.
What have you tried so far?
I used mutex.
Thank’s.
I think it would look something like this:
// --- The LVGL Gateway Task ---
void lv_gateway_task(void *pvParameter)
{
(void)pvParameter; // Cast to void to suppress unused parameter warning
// Ensure the command queue is valid. If not, something went wrong during system init.
if (xGuiCommandQueue == NULL)
{
printf("ERROR: GUI command queue is not initialized. Deleting LVGL gateway task.\n");
vTaskDelete(NULL); // Self-delete if critical resource is missing
}
gui_command_t received_command; // Buffer to hold commands received from the queue
// Main loop of the LVGL Gateway Task
while (1)
{
// 1. Process all pending GUI commands from the queue
// We use a timeout of 0 (pdMS_TO_TICKS(0)) to make it non-blocking.
// This ensures lv_task_handler() is called regularly even if the queue is empty.
while (xQueueReceive(xGuiCommandQueue, &received_command, (TickType_t)0) == pdTRUE)
{
switch (received_command.type)
{
case CMD_CREATE_BUTTON:
{
lv_obj_t *btn = lv_btn_create(lv_scr_act()); // Create button on the active screen
lv_obj_set_pos(btn, received_command.data.create_button_data.x, received_command.data.create_button_data.y);
lv_obj_set_size(btn, 100, 50); // Fixed size for example
lv_obj_t *label = lv_label_create(btn); // Create a label inside the button
lv_label_set_text(label, received_command.data.create_button_data.text);
lv_obj_center(label); // Center the label within the button
printf("LVGL_TASK: Button '%s' created at (%d,%d)\n", received_command.data.create_button_data.text, received_command.data.create_button_data.x, received_command.data.create_button_data.y);
// Optional: Attach an event callback. This callback will execute within this task's context.
// lv_obj_add_event_cb(btn, my_button_event_cb, LV_EVENT_CLICKED, NULL);
break;
}
case CMD_UPDATE_LABEL:
{
// Always check if the target object is still valid before operating on it.
if (received_command.data.update_label_data.target_label != NULL &&
lv_obj_get_parent(received_command.data.update_label_data.target_label) != NULL)
{
lv_label_set_text(received_command.data.update_label_data.target_label, received_command.data.update_label_data.new_text);
printf("LVGL_TASK: Label updated to '%s'\n", received_command.data.update_label_data.new_text);
}
else
{
printf("LVGL_TASK: Attempted to update NULL/invalid label.\n");
}
break;
}
case CMD_SET_SCREEN:
{
if (received_command.data.set_screen_data.screen_obj != NULL)
{
lv_disp_load_scr(received_command.data.set_screen_data.screen_obj);
printf("LVGL_TASK: Switched to new screen.\n");
}
else
{
printf("LVGL_TASK: Attempted to load NULL screen.\n");
}
break;
}
default:
printf("LVGL_TASK: Unknown GUI command received.\n");
break;
}
}
// 2. Run the main LVGL task handler
// This function handles display refreshing, input device polling,
// animation advancements, and timer executions. It must be called regularly.
lv_task_handler();
// 3. Yield CPU time
// A short delay to allow other FreeRTOS tasks to run.
// The ideal delay depends on desired UI responsiveness and system load.
vTaskDelay(pdMS_TO_TICKS(5)); // Delay for 5 milliseconds
}
}