StoreProhibited exception in lv_timer.c

Description

Executing basic LVGL code (v8.3.6 from SquareLine) generate a StoreProhibited exception in function
static bool lv_timer_exec(lv_timer_t * timer) (file lv_timer.c) at line :
if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer);

Guru Meditation Error: Core 1 panic’ed (StoreProhibited). Exception was unhandled.

Core 1 register dump:
PC : 0x4011fc08 PS : 0x00060430 A0 : 0x800df4c2 A1 : 0x3ffb1c60
A2 : 0x00000000 A3 : 0x00000010 A4 : 0x00000320 A5 : 0x00000000
A6 : 0x00000000 A7 : 0x00000000 A8 : 0x4acf4acf A9 : 0x00004acf
A10 : 0x00000000 A11 : 0x0000031f A12 : 0x3ffc4224 A13 : 0x00000013
A14 : 0x00000004 A15 : 0x3ffc32a8 SAR : 0x00000012 EXCCAUSE: 0x0000001d
EXCVADDR: 0x00000000 LBEG : 0x4008788e LEND : 0x40087899 LCOUNT : 0x00000000

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

I’m using ESP32 with RA8875 and Arduino IDE 2.2.1

What do you want to achieve?

LVGL not crashing with basic code :slight_smile:

What have you tried so far?

In lv_conf.h I tried #define LV_TICK_CUSTOM with values 0 or 1 (and removing or adding lv_tick_inc() function in my code). Same result.

In lv_conf.h I tried to change #define LV_MEM_SIZE (48U * 1024U) with value 32U * 1024U. Same result.

It’s like if the lv_timer_t pointer was not initialized. Perhaps I have to add something in the code ?

I already spend a few days reading LVGL documentation and trying to understand why it is crashing.
Thank you very much for your help !

Code to reproduce

#include <lvgl.h>
#include <ui.h>
#include <stdint.h>
#include <SPI.h>
#include <Wire.h>
#include "Adafruit_GFX.h"
#include "Adafruit_RA8875.h"

//EP32 PINS 
#define RA8875_INT 2
#define RA8875_CS 5
#define RA8875_RESET 4
#define RA8875_SCLK 18
#define RA8875_DO 19
#define RA8875_DI 23
#define GSL1680_WAKE 32
#define GSL1680_INT 33   

hw_timer_t * timerTick = NULL; // Timer for LVGL Tick

//Screen size
static const uint16_t screenWidth  = 800;
static const uint16_t screenHeight = 480;

Adafruit_RA8875 tft = Adafruit_RA8875(RA8875_CS, RA8875_RESET);

static lv_disp_draw_buf_t draw_buf;
static lv_color_t *buf1 = (lv_color_t*) heap_caps_malloc(screenWidth * screenHeight / 10, MALLOC_CAP_SPIRAM );

#if LV_USE_LOG != 0
/* Serial debugging */
void my_print(const char * buf)
{
    Serial.printf(buf);
    Serial.flush();
}
#endif

void my_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
   //Function I wrote for RA8875
    lv_disp_flush_ready(disp);         /* Indicate you are ready with the flushing*/
}

void my_touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
   //Function I wrote for GSLX680 Touch pad 
}

//Function for lv_tick_inc
static void lv_tick_handler()
{
  lv_tick_inc(5);
}

void setup() 
{
  Serial.begin(115200);
  SPI.begin(RA8875_SCLK, RA8875_DO, RA8875_DI, -1);
  Wire.begin();        // join i2c bus (address optional for master)

  while (!tft.begin(RA8875_800x480)) 
  {
    Serial.println("RA8875 Not Found!");
    delay(100);
  }  
  Serial.println("Found RA8875");

  tft.displayOn(true);
  tft.GPIOX(true);      // Enable TFT - display enable tied to GPIOX 
  tft.PWM1config(true, RA8875_PWM_CLK_DIV1024); // PWM output for backlight
  tft.PWM1out(255);
       
  tft.fillScreen(RA8875_WHITE);
  tft.graphicsMode();
  
  timerTick = timerBegin(0, 80, true);
  timerAttachInterrupt(timerTick, &lv_tick_handler, true);
  timerAlarmWrite(timerTick, 5000, true);  //5ms
  timerAlarmEnable(timerTick);

  lv_init();

  #if LV_USE_LOG != 0
    lv_log_register_print_cb( my_print ); /* register print function for debugging */
  #endif

  lv_disp_draw_buf_init(&draw_buf, buf1, NULL, screenWidth * screenHeight / 10);  //Initialize the display buffer.

  /*Initialize the display*/
  static lv_disp_drv_t disp_drv;        /*Descriptor of a display driver*/
  lv_disp_drv_init(&disp_drv);          /*Basic initialization*/
  disp_drv.hor_res = screenWidth;
  disp_drv.ver_res = screenHeight;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.draw_buf = &draw_buf;
  lv_disp_drv_register( &disp_drv );
 
  /*Initialize the (dummy) input device driver*/
  static lv_indev_drv_t indev_drv;
  lv_indev_drv_init( &indev_drv );
  indev_drv.type = LV_INDEV_TYPE_POINTER;
  indev_drv.read_cb = my_touchpad_read;
  lv_indev_drv_register( &indev_drv );

  ui_init();
}

void loop() 
{
  lv_timer_handler(); // let the GUI do its work
  delay(5); 
}

Hi,

Can you try changing

if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer);
to
if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer_cb);

The timer_cb expects an lv_timer_cb_t function, but it’s receiving a lv_timer_t instead.

Hi Carlos,

Thanks for your answer.

We can see in file lv_timer.h :
typedef void (*lv_timer_cb_t)(struct _lv_timer_t *);

I never used this kind of typedef before but maybe is it like a pointer to the next struct _lv_timer_t in a chain ?

typedef struct _lv_timer_t {
uint32_t period; /**< How often the timer should run*/
uint32_t last_run; /**< Last time the timer ran*/
lv_timer_cb_t timer_cb; /**< Timer function*/
void * user_data; /**< Custom user data*/
int32_t repeat_count; /**< 1: One time; -1 : infinity; n>0: residual times*/
uint32_t paused : 1;
} lv_timer_t;

And in file lv_timer.c we have :
static bool lv_timer_exec(lv_timer_t * timer)
{
...
if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer);
...
}

But if I change this line to
if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer_cb);
timer_cb will need to come from somewhere …

It seems that timer->timer_cb(timer) describe a chain of pointer pointing to itself ?

Perhaps you will be able to explain me this :slight_smile:
And why this creates a StroreProhibited exception …

Hi,

The sintax is a typedef for a function pointer named lv_timer_cb_t that receives a pointer to struct _lv_timer_t and returns void

typedef void (*lv_timer_cb_t)(struct _lv_timer_t *);

My analysis was wrong, based on this page storeprohibited-exception it makes my think we’re writing more memory than expected, it’s been a long time since I’ve used ESP32, let me try investigate further and I’ll get back to you.

Thanks for your help Carlos ! I keep trying things…

Hi,
I thought the problem came from timer->timer_cb(timer) because I added two printf before and after this line in file lv_timer.c :
printf("A");
if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer);
printf("B");
and in the serial monitor I was seeing “A” and directly after the StoreProhibited exception.

But I discovered that if I replace
static lv_color_t *disp_draw_buf = (lv_color_t*) heap_caps_malloc(screenWidth * screenHeight / 10, MALLOC_CAP_SPIRAM );
with
static lv_color_t disp_draw_buf[screenWidth * screenHeight / 10];
it doesn’t crash.

So the problem comes from the use of a draw buffer in PSRAM.
But of course a draw buffer in RAM is too small and I need to declare a buffer in the PSRAM of ESP32.

I’m wondering if it doesn’t come from the compiler (Arduino IDE) and today I will try with platformIO.

And I have a question, can we bypass the use of a draw buffer and make LVLG directly rendering to the display ?

It’s great the you have found the root of the issue, even if it’s not clear how to move forward from here. I recommend asking these tricky memory related questions in Espressif’s Forum of GitHub repo too.

Checking it out might be useful too: https://github.com/espressif/esp-bsp/blob/master/components/esp_lvgl_port/esp_lvgl_port.c

It now possible in LVGL because it would result in ugly flickering.

Hi!
Thank you for your answers.

I found the solution.
The problem was that with ESP32 (I’m using ESP32-WROVER) we can’t declare a global variable in PSRAM.

This is the guideline to implement LVGL with ESP32 with PSRAM :

1/ In Arduino IDE : Check that in menu tools > PSRAM is enabled

2/ declare a global variable lv_color_t* disp_draw_buf1;

3/ In setup()
disp_draw_buf1 = (lv_color_t *) ps_malloc (screenWidth * screenHeight / 10 * sizeof (lv_color_t));
lv_disp_draw_buf_init(&draw_buf, disp_draw_buf1, NULL, screenWidth * screenHeight / 10);

1 Like

Thanks for posting the solution @DavidR ! Enjoy LVGL.