From 586a25ed6fc3ae75e35a25c72ccd9c43bd969f45 Mon Sep 17 00:00:00 2001 From: Akash <145587932+Itsmeakash248@users.noreply.github.com> Date: Thu, 26 Feb 2026 23:17:30 +0530 Subject: [PATCH] feat(freertos): allow overriding idle percentage calculation (#9647) --- Kconfig | 9 ++++ docs/src/integration/rtos/freertos.rst | 64 ++++++++++++++++++++++++++ env_support/cmsis-pack/lv_conf_cmsis.h | 3 ++ lv_conf_template.h | 3 ++ src/lv_conf_internal.h | 9 ++++ src/osal/lv_freertos.c | 2 + tests/src/lv_test_perf_conf.h | 4 +- 7 files changed, 93 insertions(+), 1 deletion(-) diff --git a/Kconfig b/Kconfig index 3ec2fe5370..0a57f828a2 100644 --- a/Kconfig +++ b/Kconfig @@ -162,6 +162,15 @@ menu "LVGL configuration" Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM than unblocking a task using an intermediary object such as a binary semaphore. RTOS task notifications can only be used when there is only one task that can be the recipient of the event. + config LV_OS_IDLE_PERCENT_CUSTOM + bool "Custom idle percentage calculation" + default n + depends on LV_OS_FREERTOS + help + Enable this to provide a custom implementation of lv_os_get_idle_percent. + This is useful for multi-core systems where the default + FreeRTOS implementation might not sufficiently track idle time across all cores. + endmenu menu "Rendering Configuration" diff --git a/docs/src/integration/rtos/freertos.rst b/docs/src/integration/rtos/freertos.rst index 9daa497af4..583b5b59d4 100644 --- a/docs/src/integration/rtos/freertos.rst +++ b/docs/src/integration/rtos/freertos.rst @@ -31,3 +31,67 @@ the idle function. To enable this, the following needs to be added in #define traceTASK_SWITCHED_IN() lv_freertos_task_switch_in(pxCurrentTCB->pcTaskName); #define traceTASK_SWITCHED_OUT() lv_freertos_task_switch_out(); + +Alternatively, on multi-core environments (like the ESP32 SMP), this default +implementation can be insufficient or inaccurate. In this case, you can set +``LV_OS_IDLE_PERCENT_CUSTOM`` to ``1`` in ``lv_conf.h`` to disable the default +implementation. Then, you can provide a custom implementation of +:cpp:expr:`lv_os_get_idle_percent` tailored to your environment +(e.g., using ``uxTaskGetSystemState()``) without modifying LVGL source files. + +Example: Custom implementation for ESP32 SMP +******************************************** + +For multi-core environments like the ESP32 (which uses dual-core SMP), the default +implementation might not track all idle tasks. Below is an example of a custom +implementation using ``uxTaskGetSystemState()``. This requires +``configGENERATE_RUN_TIME_STATS`` and ``configUSE_TRACE_FACILITY`` to be enabled +in your ``FreeRTOSConfig.h`` (or via menuconfig for ESP-IDF). + +.. code-block:: c + + #include "FreeRTOS.h" + #include "task.h" + #include + + uint32_t lv_os_get_idle_percent(void) { + static uint32_t last_idle_time = 0; + static uint32_t last_total_time = 0; + + uint32_t ulTotalRunTime; + uint32_t ulIdleTime = 0; + + TaskStatus_t *pxTaskStatusArray; + UBaseType_t uxArraySize, x; + + uxArraySize = uxTaskGetNumberOfTasks(); + pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t)); + + if (pxTaskStatusArray == NULL) { + return 0; + } + + uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime); + for (x = 0; x < uxArraySize; x++) { + /* ESP32 has IDLE0 and IDLE1 tasks */ + if (strcmp(pxTaskStatusArray[x].pcTaskName, "IDLE0") == 0 || + strcmp(pxTaskStatusArray[x].pcTaskName, "IDLE1") == 0) { + ulIdleTime += pxTaskStatusArray[x].ulRunTimeCounter; + } + } + vPortFree(pxTaskStatusArray); + + uint32_t idle_diff = ulIdleTime - last_idle_time; + uint32_t total_diff = ulTotalRunTime - last_total_time; + + last_idle_time = ulIdleTime; + last_total_time = ulTotalRunTime; + + if (total_diff == 0) return 0; + + /* Combined idle percentage across both cores (0-200 on dual-core) */ + uint32_t idle_pct = (idle_diff * 100) / total_diff; + + /* For Sysmon, we usually want average load across all cores (0-100) */ + return idle_pct / 2; /* Adjust divisor based on your CPU core count */ + } diff --git a/env_support/cmsis-pack/lv_conf_cmsis.h b/env_support/cmsis-pack/lv_conf_cmsis.h index 6ac64835cd..9eb9798b86 100644 --- a/env_support/cmsis-pack/lv_conf_cmsis.h +++ b/env_support/cmsis-pack/lv_conf_cmsis.h @@ -127,6 +127,9 @@ * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. */ #define LV_USE_FREERTOS_TASK_NOTIFY 1 + + /* Enable this to provide a custom implementation of lv_os_get_idle_percent. */ + #define LV_OS_IDLE_PERCENT_CUSTOM 0 #endif /*======================== diff --git a/lv_conf_template.h b/lv_conf_template.h index 9fbced096e..35d34974f5 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -119,6 +119,9 @@ * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. */ #define LV_USE_FREERTOS_TASK_NOTIFY 1 + + /* Enable this to provide a custom implementation of lv_os_get_idle_percent. */ + #define LV_OS_IDLE_PERCENT_CUSTOM 0 #endif /*======================== diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 0c89315063..e2695bccc1 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -312,6 +312,15 @@ #define LV_USE_FREERTOS_TASK_NOTIFY 1 #endif #endif + + /* Enable this to provide a custom implementation of lv_os_get_idle_percent. */ + #ifndef LV_OS_IDLE_PERCENT_CUSTOM + #ifdef CONFIG_LV_OS_IDLE_PERCENT_CUSTOM + #define LV_OS_IDLE_PERCENT_CUSTOM CONFIG_LV_OS_IDLE_PERCENT_CUSTOM + #else + #define LV_OS_IDLE_PERCENT_CUSTOM 0 + #endif + #endif #endif /*======================== diff --git a/src/osal/lv_freertos.c b/src/osal/lv_freertos.c index a8545c1007..9a1f51cdaa 100644 --- a/src/osal/lv_freertos.c +++ b/src/osal/lv_freertos.c @@ -403,6 +403,7 @@ void lv_freertos_task_switch_out(void) else globals->freertos_non_idle_time_sum += elaps; } +#if LV_OS_IDLE_PERCENT_CUSTOM == 0 uint32_t lv_os_get_idle_percent(void) { if(globals->freertos_non_idle_time_sum + globals->freertos_idle_time_sum == 0) { @@ -418,6 +419,7 @@ uint32_t lv_os_get_idle_percent(void) return pct; } +#endif void lv_sleep_ms(uint32_t ms) { diff --git a/tests/src/lv_test_perf_conf.h b/tests/src/lv_test_perf_conf.h index 6c4ad37a7d..1660322935 100644 --- a/tests/src/lv_test_perf_conf.h +++ b/tests/src/lv_test_perf_conf.h @@ -121,8 +121,10 @@ * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. */ #define LV_USE_FREERTOS_TASK_NOTIFY 1 - #endif + /* Enable this to provide a custom implementation of lv_os_get_idle_percent. */ + #define LV_OS_IDLE_PERCENT_CUSTOM 0 + #endif /*======================== * RENDERING CONFIGURATION *========================*/