ESP32 Crashing if lv_timer_handler is called frequently

Description

ESP32 Program written with ESP-IDF Framework is Crashing if lv_timer_handler is called @ 5milliseconds, but working fine when called @10milliseconds

What MCU/Processor/Board and compiler are you using?

ESP32 with Custom Board

What do you want to achieve?

I wanted to integrate the LVGL with my display board, and it is working also if I set the 10ms delay for calling lv_timer_handler function

What have you tried so far?

I am reading online what could be the issue, for learning purposes I wrote the ILI9341 drivers and XPT2046 by taking reference from the porting example, so I assume there could be some problem, as I did this for learning purposes.

Code to reproduce

void display_init( void )
{
  // for LVGL 8.3.11
  static lv_disp_draw_buf_t draw_buf; // contains internal graphics buffer called draw buffer
  static lv_disp_drv_t disp_drv;      // contains callback functions
  static lv_indev_drv_t indev_drv;    // input device drivers

  // initialize the lvgl library
  lv_init();

  // initialize the tft and touch library
  tft_init();

  lv_color_t *buf1 = heap_caps_malloc(DISP_BUFFER_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
  assert(buf1);
  lv_color_t *buf2 = heap_caps_malloc(DISP_BUFFER_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
  assert(buf2);
  // LVGL Draw Buffer

  // initialize the working buffer
  lv_disp_draw_buf_init(&draw_buf, buf1, buf2, DISP_BUFFER_SIZE);

  // Register Display Driver to LVGL
  lv_disp_drv_init(&disp_drv);
  disp_drv.hor_res = tft_get_width();
  disp_drv.ver_res = tft_get_height();
  // disp_drv.flush_cb = display_flush_slow_cb;
  disp_drv.flush_cb = display_flush_cb;
  // disp_drv.flush_cb = display_flush_swap_cb;
  disp_drv.drv_update_cb = NULL;        // todo
  disp_drv.draw_buf = &draw_buf;
  // user data todo
  // lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
  lv_disp_drv_register(&disp_drv);

  // Tick Interface for LVGL using esp_timer to generate 2ms periodic event
  const esp_timer_create_args_t lvgl_tick_timer_args =
  {
    .callback = &lvgl_tick,
    .name = "lvgl_tick"
  };
  esp_timer_handle_t lvgl_tick_timer;
  ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
  ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, LV_TICK_PERIOD_MS * 1000));  // here time is in micro seconds

  // configuring input devices
  lv_indev_drv_init(&indev_drv);            // Basic initialization
  indev_drv.type = LV_INDEV_TYPE_POINTER;   // touchpad and mouse
  indev_drv.read_cb = display_input_read;   // register callback
  // Register the driver in LVGL and save the created input device object
  // lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv);
  lv_indev_drv_register(&indev_drv);

  // callback function, task name, stack size, parameters, priority, task handle
  xTaskCreate(&display_mng, "display mng", 4096, NULL, 5, NULL);
}


// Private Function Definitions
/**
 * @brief Display Manager Function which calls the lvgl timer handler function
 * @param *pvParameter  task parameter
 */
static void display_mng(void *pvParameter)
{
  while(1)
  {
    lv_timer_handler();
    vTaskDelay(5 / portTICK_PERIOD_MS);
  }
}

/**
 * @brief LVGL Tick Funtion Hook
 *        LVGL need to call funcion lv_tick_inc periodically @ LV_TICK_PERIOD_MS
 *        to keep timing information.
 * @param arg 
 */
static void lvgl_tick(void *arg)
{
  (void) arg;

  lv_tick_inc(LV_TICK_PERIOD_MS);
}

The following is my flush function.

static void display_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
  size_t width = (area->x2 - area->x1 + 1);
  size_t height = (area->y2 - area->y1 + 1);
  size_t len = width * height * 2;

  ili9341_set_window(area->x1, area->y1, area->x2, area->y2)

  // while here the first command i.e. ILI9341_GRAM using polling method
  // while the send data method using spi_device_transmit function
  tft_send_cmd(ILI9341_GRAM, 0, 0);
  tft_send_data((uint8_t*)color_map, len);

  lv_disp_flush_ready(drv);
}

and following are ttft_send_cmd and tft_send_data functions.

void tft_send_cmd( uint8_t cmd, const uint8_t *data, size_t len )
{
  esp_err_t ret;
  spi_transaction_t t;

  TFT_CS_LOW();
  TFT_DC_LOW();
  // Send Command
  memset( &t, 0x00, sizeof(t) );    // zero out the transaction
  t.length = 8;                     // Commands are 8-bits
  t.tx_buffer = &cmd;               // The data is command itself
  ret = spi_device_polling_transmit(spi_tft_handle, &t);  // transmit
  assert(ret == ESP_OK);            // should have no issues
  TFT_DC_HIGH();
  // if there is some data with command
  if( len )
  {
    memset( &t, 0x00, sizeof(t) );    // zero out the transaction
    t.length = len*8;                 // length is in bytes while transaction length is in bits
    t.tx_buffer = data;               // The data is command itself
    ret = spi_device_polling_transmit(spi_tft_handle, &t);  // transmit
    TFT_CS_HIGH();
    assert(ret == ESP_OK);            // should have no issues
  }
  TFT_CS_HIGH();
}
void tft_send_data( const uint8_t *data, size_t len )
{
  esp_err_t ret;
  spi_transaction_t t;
  if( len == 0 )
    return;                         // no need to send anything

  TFT_CS_LOW();
  TFT_DC_HIGH();
  memset( &t, 0x00, sizeof(t) );    // zero out the transaction
  t.length = len*8;                 // length is in bytes while transaction length is in bits
  t.tx_buffer = data;               // Data
  // Not used any more
  // t.user = (void*)1;                // transaction id, keep it 1 for data mode
  // ret = spi_device_polling_transmit(spi_tft_handle, &t);  // transmit
  ret = spi_device_transmit(spi_tft_handle, &t);  // transmit
  TFT_CS_HIGH();
  assert(ret == ESP_OK);            // should have no issues
}

The following are logs when the system is working fine, but as can be seen, there are still some errors reported by the LVGL library, this is when I call lv_timer_handler every 10ms.

I (0) cpu_start: App cpu up.
I (300) cpu_start: Pro cpu start user code
I (300) cpu_start: cpu freq: 160000000 Hz
I (300) cpu_start: Application information:
I (305) cpu_start: Project name:     app-template
I (310) cpu_start: App version:      a6c7024-dirty
I (315) cpu_start: Compile time:     Jan 30 2024 06:53:43
I (322) cpu_start: ELF file SHA256:  8137eff5eba74545...
I (328) cpu_start: ESP-IDF:          v5.1.2
I (332) cpu_start: Min chip rev:     v0.0
I (337) cpu_start: Max chip rev:     v3.99 
I (342) cpu_start: Chip rev:         v1.0
I (347) heap_init: Initializing. RAM available for dynamic allocation:
I (354) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (360) heap_init: At 3FFBB118 len 00024EE8 (147 KiB): DRAM
I (366) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (373) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (379) heap_init: At 4008DE74 len 0001218C (72 KiB): IRAM
I (387) spi_flash: detected chip: generic
I (390) spi_flash: flash io: dio
I (394) app_start: Starting scheduler on CPU0
I (399) app_start: Starting scheduler on CPU1
I (399) main_task: Started on CPU0
I (409) main_task: Calling app_main()
I (409) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
I (419) gpio: GPIO[5]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
[Error] (0.004, +4)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.006, +2)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.012, +6)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.022, +10)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.030, +8)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.040, +10)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.048, +8)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.058, +10)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.084, +26)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.086, +2)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.096, +10)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.102, +6)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.108, +6)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
LVGL Demo Running
LVGL Demo Running

But if I increase the frequency to call lv_timer_handler, then the following are the logs, and here system is crashing and facing a lot of troubles.

I (0) cpu_start: App cpu up.
I (300) cpu_start: Pro cpu start user code
I (300) cpu_start: cpu freq: 160000000 Hz
I (300) cpu_start: Application information:
I (305) cpu_start: Project name:     app-template
I (310) cpu_start: App version:      a6c7024-dirty
I (315) cpu_start: Compile time:     Jan 30 2024 06:53:43
I (322) cpu_start: ELF file SHA256:  8ac5b7051afa6060...
I (328) cpu_start: ESP-IDF:          v5.1.2
I (332) cpu_start: Min chip rev:     v0.0
I (337) cpu_start: Max chip rev:     v3.99 
I (342) cpu_start: Chip rev:         v1.0
I (347) heap_init: Initializing. RAM available for dynamic allocation:
I (354) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (360) heap_init: At 3FFBB118 len 00024EE8 (147 KiB): DRAM
I (366) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (373) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (379) heap_init: At 4008DE74 len 0001218C (72 KiB): IRAM
I (387) spi_flash: detected chip: generic
I (390) spi_flash: flash io: dio
I (394) app_start: Starting scheduler on CPU0
I (399) app_start: Starting scheduler on CPU1
I (399) main_task: Started on CPU0
I (409) main_task: Calling app_main()
I (409) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
I (419) gpio: GPIO[5]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
[Error] (0.004, +4)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.006, +2)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.012, +6)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.022, +10)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.030, +8)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.040, +10)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.048, +8)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.058, +10)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.070, +12)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.078, +8)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.104, +26)     _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.108, +4)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.342, +234)    _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
[Error] (0.346, +4)      _lv_inv_area: detected modifying dirty areas in render         (in lv_refr.c line #213)
Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x400ddf72  PS      : 0x00060c30  A0      : 0x800de061  A1      : 0x3ffc1b30
0x400ddf72: get_prop_core at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_obj_style.c:618

A2      : 0x3ffb4380  A3      : 0x00000000  A4      : 0x00000000  A5      : 0x00000000
A6      : 0x00000008  A7      : 0x00060023  A8      : 0x800ddf45  A9      : 0x3ffc1b10
A10     : 0x00000003  A11     : 0x00000000  A12     : 0x3ffc1d30  A13     : 0x00000000
A14     : 0x00000000  A15     : 0x00000009  SAR     : 0x0000001d  EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000004  LBEG    : 0x4000c2e0  LEND    : 0x4000c2f6  LCOUNT  : 0xffffffff
0x4000c2e0: memcpy in ROM

0x4000c2f6: memcpy in ROM



Backtrace: 0x400ddf6f:0x3ffc1b30 0x400de05e:0x3ffc1b80 0x400d94f5:0x3ffc1bb0 0x400d9faf:0x3ffc1c60 0x4011052d:0x3ffc1c90 0x400e7a0d:0x3ffc1cb0 0x4011052d:0x3ffc1cf0 0x400d7109:0x3ffc1d10 0x400d71f2:0x3ffc1d30 0x400df79a:0x3ffc1d70 0x400df845:0x3ffc1db0 0x400df7fb:0x3ffc1e00 0x400df845:0x3ffc1e40 0x400df7fb:0x3ffc1e90 0x400df845:0x3ffc1ed0 0x400dfad4:0x3ffc1f20 0x400dfcda:0x3ffc1fBacktrace: 0x400ddf6f:0x3ffc1b30 0x400de05e:0x3ffc1b80 0x400d94f5:0x3ffc1bb0 0x400d9faf:0x3ffc1c60 0x4011052d:0x3ffc1c90 0x400e7a0d:0x3ffc1cb0 0x4011052d:0x3ffc1cf0 0x400d7109:0x3ffc1d10 0x400d71f2:0x3ffc1d30 0x400df79a:0x3ffc1d70 0x400df845:0x3ffc1db0 0x400df7fb:0x3ffc1e00 0x400df845:0x3ffc1e40 0x400df7fb:0x3ffc1e90 0x400df845:0x3ffc1ed0 0x400dfad4:0x3ffc1f20 0x400dfcda:0x3ffc1f40 0x400dfe53:0x3ffc1fb0 0x400dff60:0x3ffc1fe0 0x400e012a:0x3ffc2000 0x400e54ed:0x3ffc2030 0x400e55ab:0x3ffc2050 0x400
d6783:0x3ffc2070 0x40088fb1:0x3ffc2090
0x400ddf6f: get_prop_core at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_obj_style.c:617

0x400de05e: lv_obj_get_style_prop at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_obj_style.c:229
0x400d94f5: lv_obj_get_style_border_post at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_obj_style_gen.h:267
 (inlined by) lv_obj_draw at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_obj.c:529

0x400d9faf: lv_obj_event at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_obj.c:874

0x4011052d: lv_obj_event_base at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_event.c:98

0x400e7a0d: lv_label_event at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/widgets/lv_label.c:741

0x4011052d: lv_obj_event_base at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_event.c:98

0x400d7109: event_send_core at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_event.c:458

0x400d71f2: lv_event_send at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_event.c:75

0x400df79a: lv_obj_redraw at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:148 (discriminator 3)

0x400df845: refr_obj at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:974

0x400df7fb: lv_obj_redraw at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:182 (discriminator 3)

0x400df845: refr_obj at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:974

0x400df7fb: lv_obj_redraw at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:182 (discriminator 3)

0x400df845: refr_obj at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:974

0x400dfad4: refr_obj_and_children at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:856

0x400dfcda: refr_area_part at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:791

0x400dfe53: refr_area at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:680 (discriminator 2)

0x400dff60: refr_invalid_areas at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:618

0x400e012a: _lv_disp_refr_timer at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/core/lv_refr.c:325

0x400e54ed: lv_timer_exec at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/misc/lv_timer.c:313 (discriminator 2)

0x400e55ab: lv_timer_handler at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/managed_components/lvgl__lvgl/src/misc/lv_timer.c:109

0x400d6783: display_mng at D:/Projects/Embedded/ESP32/ESP-IDF/LVGL_TemperatureHumidity/main/display_mng.c:108 (discriminator 1)

0x40088fb1: vPortTaskWrapper at C:/Espressif/frameworks/esp-idf-v5.1.2/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:162

In case someone wants to build the project, I uploaded it on Drive.

Screenshot and/or video

Can anyone suggest what could be the problem?

Show how use lv_tick

Hi, thanks for your response.
This is shown in the above code snippet, anyways I will paste here for more clarity.
In display_init() function I am using the ESP Timer to call the function every 2millisecond.

  // Tick Interface for LVGL using esp_timer to generate 2ms periodic event
  const esp_timer_create_args_t lvgl_tick_timer_args =
  {
    .callback = &lvgl_tick,
    .name = "lvgl_tick"
  };
  esp_timer_handle_t lvgl_tick_timer;
  ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
  ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, LV_TICK_PERIOD_MS * 1000));  // here time is in micro second

and then

static void lvgl_tick(void *arg)
{
  (void) arg;

  lv_tick_inc(LV_TICK_PERIOD_MS);
}

Let me know, if u need some additional information. Many Thanks

Seems ok, but handle lv in separate task is … read about thread safety in docu
My tip 4096 stack is low and too lvconf values for disp and indev refresh is important.

I updated the code like this.
Increased the stack size.

  // callback function, task name, stack size, parameters, priority, task handle
  xTaskCreate(&display_mng, "display mng", 4096*4, NULL, 5, NULL);

And then use semaphore for calling lv_timer_handler function.

static void display_mng(void *pvParameter)
{
  while(1)
  {
    vTaskDelay(pdMS_TO_TICKS(5));
    if( pdTRUE == xSemaphoreTake(lvgl_semaphore, portMAX_DELAY) )
    {
      lv_timer_handler();
      xSemaphoreGive(lvgl_semaphore);
    }
  }
}

It is working, but like this, it is taking some time to draw everything on screen.

LVGL_NotWorking

If LVGL Logging is disabled, a 5ms delay between calling lv_timer_handler is fine.
If LVGL Logging is enabled, to log all important events, the GIF I attached above the system is behaving like that, if I increase the delay to 10ms, it works fine.
Can someone explain to me why this is?
What I am doing wrong?