New to lvgl need help with random crashes

  • Read the
    • [FAQ]
    • [Quick overview]

BTW: Those two links result in “404 not found” error.

Description

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

Custom board with

  1. stm32f429b1t6
  2. LCD TFT050-84ATP
  3. external SDRAM is42s32000j-6tl (2Meg x 32 x 4 banks)

Code generated with

  1. Cubemx-6.1.2
  2. HAL FW 1.25.2
  3. FreeRTOS
  4. LVGL-7.11.0
  5. gcc-9.3.0
  6. newlib-3.1.0

Memory management defined like this:

#define LV_MEM_CUSTOM      0
#  define LV_MEM_CUSTOM_INCLUDE <stdlib.h>   /*Header for the dynamic memory function*/
#  define LV_MEM_CUSTOM_ALLOC   malloc       /*Wrapper to malloc*/
#  define LV_MEM_CUSTOM_FREE    free         /*Wrapper to free*/

What do you want to achieve?

I get random crashes, refresh errors and hardfault errors.

What have you tried so far?

I’ve added this snippet to help track down the problem:

diff --git a/src/lv_core/lv_refr.c b/src/lv_core/lv_refr.c
index 8199f4ea..af931428 100644
--- a/src/lv_core/lv_refr.c
+++ b/src/lv_core/lv_refr.c
@@ -223,6 +223,7 @@ void _lv_disp_refr_task(lv_task_t * task)
 #else
                 copy_buf = _lv_mem_buf_get(disp_refr->driver.hor_res * sizeof(lv_color_t));
 #endif
+                LV_LOG_WARN("_lv_mem_buf_get()->%p", copy_buf);
 
                 uint8_t * buf_act = (uint8_t *)vdb->buf_act;
                 uint8_t * buf_ina = (uint8_t *)vdb->buf_act == vdb->buf1 ? vdb->buf2 : vdb->buf1;
@@ -254,6 +255,7 @@ void _lv_disp_refr_task(lv_task_t * task)
                     }
                 }
 
+                LV_LOG_WARN("release %p", copy_buf);
                 if(copy_buf) _lv_mem_buf_release(copy_buf);
             }
         } /*End of true double buffer handling*/
diff --git a/src/lv_misc/lv_mem.c b/src/lv_misc/lv_mem.c
index 805bbfe5..1a4e09a2 100644
--- a/src/lv_misc/lv_mem.c
+++ b/src/lv_misc/lv_mem.c
@@ -152,6 +152,7 @@ void _lv_mem_deinit(void)
 void * lv_mem_alloc(size_t size)
 {
     if(size == 0) {
+        LV_LOG_WARN("_lv_mem_alloc()->%p", &zero_mem);
         return &zero_mem;
     }
 
@@ -209,6 +210,7 @@ void * lv_mem_alloc(size_t size)
 #endif
     }
 
+    LV_LOG_WARN("_lv_mem_alloc()->%p", &alloc);
     return alloc;
 }
 
@@ -530,6 +532,8 @@ void _lv_mem_buf_release(void * p)
 {
     uint8_t i;
 
+    LV_LOG_WARN("lv_mem_buf_release: %p", p);
+
     /*Try small static buffers first*/
     for(i = 0; i < sizeof(mem_buf_small) / sizeof(mem_buf_small[0]); i++) {
         if(mem_buf_small[i].p == p) {
@@ -545,7 +549,7 @@ void _lv_mem_buf_release(void * p)
         }
     }
 
-    LV_LOG_ERROR("lv_mem_buf_release: p is not a known buffer")
+    LV_LOG_ERROR("lv_mem_buf_release: %p is not a known buffer", p);
 }
 
 /**
@@ -554,6 +558,9 @@ void _lv_mem_buf_release(void * p)
 void _lv_mem_buf_free_all(void)
 {
     uint8_t i;
+
+    LV_LOG_WARN("lv_mem_buf_free_all");
+
     for(i = 0; i < sizeof(mem_buf_small) / sizeof(mem_buf_small[0]); i++) {
         mem_buf_small[i].used = 0;
     }

With this, I see that there is an attempt to free a pointer twice

WARNING: File: lvgl//src/lv_misc/lv_mem.c#535: _lv_mem_buf_release: lv_mem_buf_release: C0002000
WARNING: File: lvgl//src/lv_misc/lv_mem.c#535: _lv_mem_buf_release: lv_mem_buf_release: C0002000
ERROR: File: lvgl//src/lv_misc/lv_mem.c#552: _lv_mem_buf_release: lv_mem_buf_release: C0002000 is not a known buffer

Here’s the gdb backtrace:

#2  0x080251be in _lv_log_add (level=level@entry=3 '\003', file=file@entry=0x80f4490 "lvgl//src/lv_misc/lv_mem.c", line=line@entry=552,
    func=func@entry=0x80f45ec <__func__.6790> "_lv_mem_buf_release", format=format@entry=0x80f44f4 "lv_mem_buf_release: %p is not a known buffer")
    at lvgl//src/lv_misc/lv_log.c:89
#3  0x0802420e in _lv_mem_buf_release (p=p@entry=0xc0002000) at lvgl//src/lv_misc/lv_mem.c:552
#4  0x080313d6 in lv_draw_map (map_area=map_area@entry=0x20006dd0 <ucHeap+19816>, clip_area=clip_area@entry=0x20006d50 <ucHeap+19688>, map_p=<optimized out>,
    draw_dsc=draw_dsc@entry=0x20006dd8 <ucHeap+19824>, chroma_key=chroma_key@entry=false, alpha_byte=alpha_byte@entry=true)
    at lvgl//src/lv_draw/lv_draw_img.c:472
#5  0x08031af4 in lv_img_draw_core (coords=coords@entry=0x20006dd0 <ucHeap+19816>, clip_area=clip_area@entry=0x20006dc8 <ucHeap+19808>, src=<optimized out>,
    draw_dsc=0x20006dd8 <ucHeap+19824>) at lvgl//src/lv_draw/lv_draw_img.c:277
#6  0x08031c34 in lv_draw_img (coords=coords@entry=0x20006dd0 <ucHeap+19816>, mask=mask@entry=0x20006dc8 <ucHeap+19808>, src=<optimized out>,
    dsc=dsc@entry=0x20006dd8 <ucHeap+19824>) at lvgl//src/lv_draw/lv_draw_img.c:85
#7  0x08021616 in lv_img_design (img=<optimized out>, clip_area=0x20006e90 <ucHeap+20008>, mode=<optimized out>) at lvgl//src/lv_widgets/lv_img.c:712
#8  0x0801b634 in lv_refr_obj (obj=obj@entry=0xc00012ac, mask_ori_p=mask_ori_p@entry=0x20006eb0 <ucHeap+20040>) at lvgl//src/lv_core/lv_refr.c:696
#9  0x0801b6be in lv_refr_obj (obj=obj@entry=0xc0001248, mask_ori_p=mask_ori_p@entry=0x20006ee8 <ucHeap+20096>) at lvgl//src/lv_core/lv_refr.c:736
#10 0x0801b6be in lv_refr_obj (obj=obj@entry=0xc0000e58, mask_ori_p=mask_ori_p@entry=0x20006fa0 <ucHeap+20280>) at lvgl//src/lv_core/lv_refr.c:736
#11 0x0801b6d2 in lv_refr_obj_and_children (top_p=top_p@entry=0xc0000e58, mask_p=mask_p@entry=0x20006fa0 <ucHeap+20280>) at lvgl//src/lv_core/lv_refr.c:638
#12 0x0801b93e in lv_refr_area_part (area_p=area_p@entry=0xc00008fa) at lvgl//src/lv_core/lv_refr.c:569
#13 0x0801ba70 in lv_refr_area (area_p=0xc00008fa) at lvgl//src/lv_core/lv_refr.c:424
#14 0x0801bc42 in lv_refr_areas () at lvgl//src/lv_core/lv_refr.c:402
#15 0x0801bcbe in _lv_disp_refr_task (task=0xc0000a1c) at lvgl//src/lv_core/lv_refr.c:199
#16 0x0801be68 in lv_refr_now (disp=0xc0000890) at lvgl//src/lv_core/lv_refr.c:88
#17 0x08012c18 in ilvgl_test_show () at app/lvgl-test.c:327
#18 0x08012f76 in lvgl_test_show_start () at app/lvgl-test.c:434
#19 0x0800fee0 in app_entry () at app/app-entry.c:59
#20 0x0800d1ac in vPortFree (pv=0x0) at cubemx/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c:305

The offending piece of code is:

static void lvgl_test_show(void)
{
    /* Setup some styles, initial screen and some buttons
      */
    lvgl_test_setup();

    lv_scr_load(it1010_main_screen);
    lv_refr_now(lv_disp_get_default()); /* Here we crash */
}

Any help to track this problem down?

(cc @kisvegabor)

It seems to me that there is only one instance where this can happen: if map2 and mask_buf have the same value here.

@dohnjoe It’s strange because we should have already seen at least

ERROR: File: lvgl//src/lv_misc/lv_mem.c#552: _lv_mem_buf_release: lv_mem_buf_release: C0002000 is not a known buffer

warning so far.

I’ve tried to reproduce it but couldn’t see the issue. I modified _lv_mem_buf_release like this:

void _lv_mem_buf_release(void * p)
{
    uint8_t i;

    /*Try small static buffers first*/
    for(i = 0; i < sizeof(mem_buf_small) / sizeof(mem_buf_small[0]); i++) {
        if(mem_buf_small[i].p == p) {
            /*ADDED HERE*/   
            if(mem_buf_small[i].used == 0) printf("error1\n");

            mem_buf_small[i].used = 0;
            return;
        }
    }

    for(i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
        if(LV_GC_ROOT(_lv_mem_buf[i]).p == p) {
            /*ADDED HERE*/    
            if(LV_GC_ROOT(_lv_mem_buf[i]).used == 0) printf("error2\n");
            
            LV_GC_ROOT(_lv_mem_buf[i]).used = 0;
            return;
        }
    }

    LV_LOG_ERROR("lv_mem_buf_release: p is not a known buffer")
}

Can you reproduce the issue in a simulator too?

@embeddedt

It can happen only if there is an error in _lv_mem_buf_get that returns the same buffer twice.

BTW, C0002000 seems very special. Isn’t it a frame buffer address or so?

Uh, looks like I’ve copied the wrong stack backtrace… I’ll need to reproduce it once more.

0xc0002000 is the external RAM that is assigned for lvgl:

#define LV_MEM_CUSTOM      0
#if LV_MEM_CUSTOM == 0
#define LV_MEM_SIZE     768000
#  define LV_MEM_ATTR
#  define LV_MEM_ADR          0xC0000000
#  define LV_MEM_AUTO_DEFRAG  1
#else       /*LV_MEM_CUSTOM*/

Don’t know whether it’s a good idea to use the external SDRAM as memory for lvgl.
External SDRAM is slower than the internal one.

At which address is your framebuffer located?

Maybe you can use internal RAM for lvgl to use, and check whether the crash occurs again.

It makes me wonder that your LV_MEM_SIZE is 768000.
That’s your display size (800 x 480 x 2)!?

Not that there is a misunderstanding between the LV_MEM_CUSTOM setting and your framebuffer.

Thanks to all for the help.

#define LV_MEM_CUSTOM      0
#if LV_MEM_CUSTOM == 0
#define LV_MEM_SIZE     768000
#  define LV_MEM_ATTR
#  define LV_MEM_ADR          0xC0000000
#  define LV_MEM_AUTO_DEFRAG  1
#else       /*LV_MEM_CUSTOM*/
...
#endif

...

#define SDRAM_BASE ((uint8_t *)0xc0000000)
#define PIXEL_SIZE 2
#define FRAMEBUFFER_LEN = ((LV_HOR_RES_MAX)
 * (LV_VER_RES_MAX) * (PIXEL_SIZE))
#define FRAMEBUFFER1    ((SDRAM_BASE) + (FRAMEBUFFER_LEN)*1)
#define FRAMEBUFFER2    ((SDRAM_BASE) + (FRAMEBUFFER_LEN)*2)

So the areas should not overlap, IMHO.

I have set the LVGL heap into external RAM to prevent it from colliding with the heap of FreeRTOS. I’m not about optimizing at this early stage.

One thing looks strange in this context to me: If I use:

    lv_disp_buf_init (&disp_buf,
                      FRAMEBUFFER1, FRAMEBUFFER2,
                      FRAMEBUFFER_LEN); /* 800*480*2 */

I get garbled images in display. I need to use

     lv_disp_buf_init (&disp_buf,
                      FRAMEBUFFER1, FRAMEBUFFER2,
                      LV_HOR_RES_MAX * LV_VER_RES_MAX); /* 800*480 */

to get a proper display. But isn’t this only half of the required memory for 16 bit color depth?

I’m not a 100 % sure, but as far as I remember:
If you setup two full frame buffers with the call of lv_disp_buf_init lvgl works with these two buffers in the double buffer frame buffer mode.
I think it means that your flush function just have to switch the build in LCD driver (direct driven display by STM32Fxxx) to use the other frame buffer. So you don’t copy the pixel data between lvgl’s working buffer and STM32Fxxx’s frame buffer.

The other way is to setup lvgl to use a smaller working buffer (your second example) and in flush fuction you copy the lvgl’s working buffer into the real frame buffer.

You don’t have to setup a full frame buffer sized working buffer for lvgl. A shorter one is also appropriate for lvgl (e.g. LV_HOR_RES_MAX * 50 * 2).
Lvgl doesn’t always need to update the entire display, but only smaller parts/portions. So in this case a really large working buffer would be a waste.
Especially if you are limitted in internal RAM size (which is normally the case).
For larger display updates lvgl would need some loops to do the update, but it can be done faster in internal RAM than in external one.
On the other hand, when using full frame double buffering, lvgl has to make a copy (which also will need some time) from the currently shown and active frame buffer to the working frame buffer (before new drawings can be processed).

In doubt, just take the working one :grin: :wink:

Yes, that’s my understanding, too:

static void Flush_LVGL (lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    set_fb_address (color_p);
    lv_disp_flush_ready(&disp_drv);
}

But with this, I’d need the full sized buffers?

To be honest, this (the double buffering) was also my first try last year.
But then I realized that this doesn’t seem to be the optimal way.

Just setup for one working buffer (in lv_dips_buf_init) and change the flush function accordingly and enjoy a working solution.
I didn’t try for two (smaller) working buffers. AFAIK this should give some better progress, as one working buffer can be used for transferring the display data to display, and the other one can be used by lvgl for building a new portion for the display in the meanwhile.
This will only give some inprovement when transferring the display data from working buffer to frame buffer is done via DMA.

Just start with the normal recommended way, get familiar with all the stuff and let room for improvements later.

In what way is this not optimal? Does it give bad performance?