Slow flush calls (at max time interval) even when changing content faster

Some interesting observations…

Important note: still using the lv_timer_handler() calls from the Espressif port, which means there is a sleep after each lv_timer_handler() call based on the return value, but capped to 500ms. I kept this because it lets me learn how LVGL behaves:)

The setup: I’m going to update an object on the screen every 100ms
When I say flush happens, I mean I see the SPI traffic go out.

test 1: run my update code in an lv_timer()
outcome: flush happens 1.5ms after every update, in sync (100us jitter)

test 2: run my update code in a FreeRTOS task but also run an empty lv_timer() every 100ms
outcome: flush happens every 100ms but at an offset (62ms after the change)

test 3: run my update code in a FreeRTOS task but also run an empty lv_timer() every 200ms
outcome: flush happens every other display update, every 200ms, again at a fixed offset (62ms after the change)

test 4: run my update code in a FreeRTOS task and call lv_refr_now() after each update. No lv_timer() created
outcome: flush happens 1.5ms after every update, in sync (100us jitter) just like test 1
(lv_timer_handler() still returns 0xFFFFFFFF so flush must be happening from lv_refr_now() directly)

Conclusion: flushing happens when an actual timer (created with lv_timer_create()) runs (by means of lv_timer_handler()). If no timer runs, flushing depends on the rate at which you call lv_timer_handler().

So my possible solution(s) are:

  • update from an lv_timer
  • call lv_timer_handler at a faster rate (not using its return value)
  • use lv_refr_now()

Thanks @Marian_M and @kdschlosser for your insights, I hope this can help somebody else too and I hope @kdschlosser wants to post his PM below (or allows me to post it) as it contains helpful information for everybody!

Addendum: scope image of timing (old scope w/o USB so actual picture of display, sorry).
The yellow signal at the bottom is a GPIO I’m toggling.
First high->low = start of lv_label_set_text() call
low->high = end of that call + start of lv_refr_now(): took 420us
high->low = end of refr_now(): took 1400us
top signal is SPI bus

Platform = ESP32S3 at 240MHz and SPI bus at 40MHz, refreshing a text label of 32x32 px.
IDF 5.0.1, compiled -Os