Rpi Pico cannot allocate memory

Description

I’m trying to use LVGL with the Raspberry Pi Pico but with no luck. I’m using a ER-TFTM050A2-3-3661 display from EastRising, the display is 800x480 with a RA8875 controller driven via SPI. I wrote a custom driver using the Adafruit one as a template; my initialization process works fine, however when I try to run LVGL, it gets stuck on the malloc process of creating a display and the pico launches a SIGTRAP signal,when i debug it, see the attached screenshot. It seems like a memory related problem.

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

I’m using pico-sdk latest version 2.0.0 on PopOS, Pi pico is version 1, the one with RP2040 chip. I’m using the latest LVGL version 9.2.2.

What do you want to achieve?

What have you tried so far?

I’ve tried to reduce the display buffer size, change the avaible memory for lv_malloc.
I’m using a 32kB size of size avaible to malloc and a 19kB single buffer for the display.

Code to reproduce

#include <stdio.h>
#include "pico/stdlib.h"
#include "RA8875.h"
#include "GSL1680.h"
#include "lvgl.h"
#include "hardware/spi.h"
#include "pico/rand.h"
#define PIN_CS   26
#define PIN_RST  20
#define PIN_CTP_WAKE 21
#define PIN_CTP_INT 22
#define TICK_MS 5
#define HOR_RES 800
#define VER_RES 480


static GSL1680 touch = GSL1680(true);
static RA8875 tft = RA8875(PIN_CS,PIN_RST);
static lv_draw_buf_t *draw_buf1;


static lv_display_t * display1 = lv_display_create(HOR_RES, VER_RES);
static lv_indev_t * indev = lv_indev_create();

bool repeating_tick_inc(__unused struct repeating_timer *t) {
    lv_tick_inc(TICK_MS);
    return true;
}

static uint32_t get_elapsed_ms(){
    return to_ms_since_boot(get_absolute_time());
}

void raio_display_flush(lv_display_t * display, const lv_area_t * area, uint8_t * px_map){
    uint16_t * buf16 = (uint16_t *)px_map;
    uint32_t w = (area->x2 - area->x1 +1);
    uint32_t h = (area->y2 - area->y1 +1);
    tft.setActiveWindow(area->x1, area->x2, area->y1, area->y2);
    tft.writeCommand(RA8875_MRWC);
    gpio_put(PIN_CS,0);
    uint8_t cmd = RA8875_DATAWRITE;
    spi_write_blocking(spi0,&cmd,1);
    for(uint32_t i = 0; i < w*h; i++){
        tft.pushColor(*buf16);
        buf16++;
    }
    gpio_put(PIN_CS,1);
    lv_disp_flush_ready(display1);
}

void touchpad_read(lv_indev_t * indev, lv_indev_data_t * data){
    static uint16_t last_x = 0, last_y = 0;
    bool valid = false;
    uint16_t touchX = last_x, touchY = last_y;
    uint8_t touched = touch.data_read();
    touchX = touch.readFingerX(1);
    touchY = touch.readFingerY(1);
    if(touched){
        valid = true;
        last_x = touchX;
        last_y = touchY;
    }
    data->point.x = touchX;
    data->point.y = touchY;
    data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR;
}

static void btn_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * btn = static_cast<lv_obj_t *>(lv_event_get_target(e));
    if(code == LV_EVENT_CLICKED) {
        static uint8_t cnt = 0;
        cnt++;
        lv_obj_t * label = lv_obj_get_child(btn, 0);
        lv_label_set_text_fmt(label, "Button: %d", cnt);
    }
}

int main()
{
    struct repeating_timer timer;
    stdio_init_all();
    add_repeating_timer_ms(5,repeating_tick_inc, nullptr,&timer);


    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    gpio_put(PICO_DEFAULT_LED_PIN,1);

    tft.begin();
    tft.displayOn(true);
    touch.begin(PIN_CTP_WAKE,PIN_CTP_INT);
    tft.fillScreen(RA8875_BLACK);
    tft.graphicsMode();


    //LVGL INIT

    lv_display_set_default(display1);
    draw_buf1 = lv_draw_buf_create(RA8875_WIDTH / 2, RA8875_HEIGHT / 10 , LV_COLOR_FORMAT_RGB565, LV_STRIDE_AUTO);
    lv_display_set_draw_buffers(display1, draw_buf1, NULL);
    lv_tick_set_cb(get_elapsed_ms);
    lv_display_set_flush_cb(display1, raio_display_flush);
// Create and set up at least one display before you register any input devices.

    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);  // Touch pad is a pointer-like device.
    lv_indev_set_read_cb(indev, touchpad_read);   //  Set driver function.
    const char* s = "Hello World!";
    lv_obj_t *label1 = lv_label_create(lv_scr_act());
    lv_label_set_text(label1, s);
    lv_obj_align(label1, LV_ALIGN_CENTER, 0, 0);
    lv_obj_t * btn = lv_btn_create(lv_scr_act());
    lv_obj_set_pos(btn, 10, 10);
    lv_obj_set_size(btn, 120, 50);
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);

    lv_obj_t * label = lv_label_create(btn);
    lv_label_set_text(label, "Button");
    lv_obj_center(label);

    while (1) {
        sleep_ms(10);
        lv_task_handler();

    }

}

Screenshot and/or video

After some research and debugging I found out that the memory wasn’t initialized, in fact I didn’t call lv_init() :sweat_smile:, finally I’ve changed some code and now it works fine, I’ll continue to tweak the code a little bit meanwhile if anyone needs it I post my updated and working code below.

#include <stdio.h>
#include "pico/stdlib.h"
#include "RA8875.h"
#include "GSL1680.h"
#include "lvgl.h"
#include "hardware/spi.h"
#include "pico/rand.h"

#define PIN_CS   26
#define PIN_RST  20
#define PIN_CTP_WAKE 21
#define PIN_CTP_INT 22
#define TICK_MS 1
#define HOR_RES 800
#define VER_RES 480

#include <malloc.h>

static GSL1680* touch;
static RA8875* tft;
static lv_draw_buf_t *draw_buf1;
static uint8_t buf1[HOR_RES * VER_RES / 10 * 2];
static lv_display_t * display1;
static lv_indev_t * indev;

long map(long x, long in_min, long in_max, long out_min, long out_max) {

    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

}

uint32_t getTotalHeap(void) {
    extern char __StackLimit, __bss_end__;

    return &__StackLimit  - &__bss_end__;
}

uint32_t getFreeHeap(void) {
    struct mallinfo m = mallinfo();

    return getTotalHeap() - m.uordblks;
}

void my_log_cb(lv_log_level_t level, const char * buf)
{
    printf("%s",buf);
}

bool repeating_tick_inc(__unused struct repeating_timer *t) {
    lv_tick_inc(TICK_MS);
    return true;
}

static uint32_t get_elapsed_ms(){
    return to_ms_since_boot(get_absolute_time());
}

void raio_display_flush(lv_display_t * display, const lv_area_t * area, uint8_t * px_map){
    uint16_t * buf16 = (uint16_t *)px_map;
    uint32_t w = (area->x2 - area->x1 +1);
    uint32_t h = (area->y2 - area->y1 +1);
    tft->setActiveWindow(area->x1, area->x2, area->y1, area->y2);
    tft->drawPixels(buf16,w*h,area->x1,area->y1);
    lv_disp_flush_ready(display);
}

void touchpad_read(lv_indev_t * indev, lv_indev_data_t * data){
    static uint16_t last_x = 0, last_y = 0;
    bool valid = false;
    uint16_t touchX = last_x, touchY = last_y;
    uint8_t touched = touch->data_read();
    touchX = touch->readFingerX(1);
    touchY = touch->readFingerY(1);
    if(touched){
        valid = true;
        last_x = touchX;
        last_y = touchY;
    }
    data->point.x = touchX;
    data->point.y = map(touchY,4100,4600,0,480);
    data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR;
}

static void btn_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * btn = static_cast<lv_obj_t *>(lv_event_get_target(e));
    if(code == LV_EVENT_CLICKED) {
        static uint8_t cnt = 0;
        cnt++;

        /*Get the first child of the button which is the label and change its text*/
        lv_obj_t * label = lv_obj_get_child(btn, 0);
        lv_label_set_text_fmt(label, "Button: %d", cnt);
    }
}

int main()
{
    struct repeating_timer timer;
    stdio_init_all();
    add_repeating_timer_ms(TICK_MS,repeating_tick_inc, nullptr,&timer);
    lv_init();
    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    gpio_put(PICO_DEFAULT_LED_PIN,1);
    static RA8875 temp = RA8875(PIN_CS,PIN_RST);
    tft = &temp;
    static GSL1680 temp2 = GSL1680(true);
    touch = &temp2;

    tft->begin();
    tft->displayOn(true);
    touch->begin(PIN_CTP_WAKE,PIN_CTP_INT);
    tft->graphicsMode();

    //LVGL INIT
    indev = lv_indev_create();
    display1 = lv_display_create(HOR_RES, VER_RES);
    lv_display_set_default(display1);
    draw_buf1 = lv_draw_buf_create(RA8875_WIDTH / 2, RA8875_HEIGHT / 10 , LV_COLOR_FORMAT_RGB565, LV_STRIDE_AUTO);
    lv_display_set_draw_buffers(display1, draw_buf1, NULL);
    lv_display_set_buffers(display1, buf1, NULL, sizeof(buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);
    lv_tick_set_cb(get_elapsed_ms);
    lv_display_set_flush_cb(display1, raio_display_flush);
    lv_log_register_print_cb(my_log_cb);
    // Create and set up at least one display before you register any input devices.

    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);  // Touch pad is a pointer-like device.
    lv_indev_set_read_cb(indev, touchpad_read);   //  Set driver function.
    lv_indev_set_display(indev,display1);

    const char* s = "Hello World!";
    lv_obj_t *label1 = lv_label_create(lv_scr_act());
    lv_label_set_text(label1, s);
    lv_obj_align(label1, LV_ALIGN_CENTER, 0, 0);
    lv_obj_t * btn = lv_btn_create(lv_scr_act());
    lv_obj_set_pos(btn, 20, 20);
    lv_obj_set_size(btn, 200, 70);
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);

    lv_obj_t * label = lv_label_create(btn);
    lv_label_set_text(label, "Button");
    lv_obj_center(label);

    while (1) {
        lv_task_handler();
        sleep_ms(5);

    }

}