LVGL v9.4 lv_draw_dispatch_wait_for_request() hangs after migrating from FreeRTOS V1 to V2

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

  1. Stuck inside lv_draw_dispatch_wait_for_request() (inside lv_thread_sync_wait()), indicating the draw task never signals completion.
  2. Stack trace in FreeRTOS V1 (working path) shows draw_buf_flush() → refr_invalid_areas() → etc. — there, the buffer is updated correctly.
  3. 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 xSyncSignal becomes 1 after flush finish, and the task proceeds.
  • In V2 xSyncSignal always remains 0, so the wait never returns.

What I’ve Tried & Observed

  1. Confirmed that lv_tick works in both V1 and V2.
  2. Attempted .thread_attr.stack_size = 4096, but allocation failed.
  3. No rendering occurs — buffer remains unchanged.

Request to LVGL / RTOS Experts

  1. Is this a known issue when using CMSIS-RTOS2/FreeRTOS V2 integration?
  2. Does LVGL v9.4 have documented minimum stack or heap requirements for the draw thread?
  3. Could there be improvements in LVGL’s error handling when render thread creation fails?
  4. Would anyone share a CMSIS-RTOS2-compatible lv_thread_sync implementation that gracefully handles thread creation failures or uses static allocation?