Description / Background
- LVGL version: v9.4 (downloaded from lvgl/lvgl on GitHub)
- Environment: STM32 + display driver, using CMSIS-RTOS2 (FreeRTOS V2 wrapper)
- Previous setup: FreeRTOS V1 — everything worked fine, screens rendered correctly.
- Change: Migrated to FreeRTOS V2 — lv_tick appears to increment normally, but rendering never completes and the system hangs.
Observed Behavior & Debug Clues
- Stuck inside lv_draw_dispatch_wait_for_request() (inside lv_thread_sync_wait()), indicating the draw task never signals completion.
- Stack trace in FreeRTOS V1 (working path) shows draw_buf_flush() → refr_invalid_areas() → etc. — there, the buffer is updated correctly.
- In FreeRTOS V2, the same call stack executes, but the buffer remains unchanged, and xSyncSignal remains 0 — causing ulTaskNotifyTake(pdTRUE, portMAX_DELAY) to block indefinitely.
Comparison: FreeRTOS V1 (Working) vs. V2 (Hangs)
| Step / Function | FreeRTOS V1 (Working) | FreeRTOS V2 (Hangs) |
|---|---|---|
lvglRun() → lv_timer_handler() |
Executes regularly | Executes, but stall downstream |
refr_invalid_areas() → draw_buf_flush() |
Buffer (buf_t) filled with new pixel data |
Buffer remains zero / stale |
Render completion signal (lv_disp_flush_ready) |
Signal fires → tasks resume | Signal not fired → xSyncSignal = 0 |
lv_thread_sync_wait() (inside dispatcher) |
xSyncSignal becomes 1 — proceeds |
xSyncSignal stays 0 — hang in ulTaskNotifyTake() |
| Display update | Visible on screen | Frozen/blank screen |
Relevant Code Snippet — `lv_thread_sync_wait()
lv_result_t lv_thread_sync_wait(lv_thread_sync_t * pxCond)
{
lv_result_t lvRes = LV_RESULT_OK;
prvCheckCondInit(pxCond);
LV_LOG_WARN("WAIT ENTER: cond=%p, before_crit signal=%d, xTaskToNotify=%p",
pxCond, (int)pxCond->xSyncSignal, (void*)pxCond->xTaskToNotify);
#if LV_USE_FREERTOS_TASK_NOTIFY
TaskHandle_t xCurrentTaskHandle = xTaskGetCurrentTaskHandle();
_enter_critical();
BaseType_t xSyncSygnal = pxCond->xSyncSignal;
pxCond->xSyncSignal = pdFALSE;
if(xSyncSygnal == pdFALSE) {
pxCond->xTaskToNotify = xCurrentTaskHandle;
}
_exit_critical();
LV_LOG_WARN("WAIT AFTER CRIT: cond=%p, xSyncSignal=%d, xTaskToNotify=%p",
pxCond, (int)xSyncSygnal, (void*)pxCond->xTaskToNotify);
if(xSyncSygnal == pdFALSE) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
LV_LOG_WARN("WAIT RESUMED: cond=%p, task=%p (notify returned)",
pxCond, (void*)xCurrentTaskHandle);
}
#endif
return lvRes;
}
- In V1
xSyncSignalbecomes1after flush finish, and the task proceeds. - In V2
xSyncSignalalways remains0, so the wait never returns.
What I’ve Tried & Observed
- Confirmed that lv_tick works in both V1 and V2.
- Attempted .thread_attr.stack_size = 4096, but allocation failed.
- No rendering occurs — buffer remains unchanged.
Request to LVGL / RTOS Experts
- Is this a known issue when using CMSIS-RTOS2/FreeRTOS V2 integration?
- Does LVGL v9.4 have documented minimum stack or heap requirements for the draw thread?
- Could there be improvements in LVGL’s error handling when render thread creation fails?
- Would anyone share a CMSIS-RTOS2-compatible
lv_thread_syncimplementation that gracefully handles thread creation failures or uses static allocation?