memory-managementheap-memoryfreertosdmaesp-idf

T-Display-S3-long FreeRTOS Memory allocation fails while there is heap space available


I am trying to program for the T-Display-S3-long, I have downloaded the AXS15231B.cpp library and tested the basic program where it displays 3 statically allocated images. My goal is to have an RTOS task that will handle the UI rendering accordingly. The program I am testing right now is very simple it just starts the task and draws four rectangles on the screen (or at least this is what it should happen). The configuration is set to use DMA to draw on the screen so when it tries to draw the first rectangle the AXS15231B library tries to allocate memory in order to fit the rectangle pixels inside and push them through the DMA -> QSPI to the display. Although this allocation fails and since I have enabled memory and heap corruption detection the whole program restarts. I have increased the stack size of the drawing task as well as the stack size of the main_app function, also I have added some logs to see what is the available heap size before allocation and it seems enough for the allocation. Balow I have posted the code along with the logs:

app_main.cpp

static uint16_t col_back = 0x07ba;
static uint16_t col1 = 0x07ba;
static uint16_t col2 = 0xfc60;
static uint16_t col3 = 0xd800;
static uint16_t col4 = 0x16e0;

void test_lcd(void *arg) {
    xSemaphoreTake(sync_lcd_task, portMAX_DELAY);

    pinMode(TFT_BL, OUTPUT);
    digitalWrite(TFT_BL, HIGH);
    ESP_LOGI(TFT_TAG, "Display Initialised");
    axs15231_init();
    lcd_setRotation(1);
    while(1) {
        digitalWrite(TFT_BL, HIGH);
        lcd_fill(10 , 20, 150, 160, col1);
        lcd_fill(170, 20, 310, 160, col2);
        lcd_fill(330, 20, 470, 160, col3);
        lcd_fill(490, 20, 630, 160, col4);
        vTaskDelay(10000);
    }
    
}

void app_main(void)
{
    ESP_LOGI("main", "Free Heap size %d", xPortGetFreeHeapSize());
    //Allow other core to finish initialization
    vTaskDelay(pdMS_TO_TICKS(100));

    //Create semaphores to synchronize
    sync_wifi_prov_task = xSemaphoreCreateBinary();
    sync_stats_task = xSemaphoreCreateBinary();
    sync_lcd_task = xSemaphoreCreateBinary();

    xTaskCreatePinnedToCore(provision_wifi, WIFI_PROV_TASK_NAME, 4096, NULL, WIFI_PROV_PRIO, NULL, 0);
    ESP_LOGI("main", "Free Heap size %d", xPortGetFreeHeapSize());
    xTaskCreatePinnedToCore(stats_task, STATS_TASK_NAME, 4096, NULL, STATS_TASK_PRIO, NULL, tskNO_AFFINITY);
    ESP_LOGI("main", "Free Heap size %d", xPortGetFreeHeapSize());
    xTaskCreatePinnedToCore(test_lcd, "lcd_test", 220000, NULL, 4, NULL, 1);
    ESP_LOGI("main", "Free Heap size %d", xPortGetFreeHeapSize());

    xSemaphoreGive(sync_lcd_task);
    xSemaphoreGive(sync_wifi_prov_task);
    xSemaphoreGive(sync_stats_task);
}

I have modified the function that fails on the AXS15231B library to add two logs before allocation:

static const char *TFT_TAG = "display";
void lcd_fill(uint16_t xsta,
              uint16_t ysta,
              uint16_t xend,
              uint16_t yend,
              uint16_t color)
{

    uint16_t w = xend - xsta;
    uint16_t h = yend - ysta;
    uint32_t size = w * h * 2;
    ESP_LOGI(TFT_TAG, "Free Heap size %d", xPortGetFreeHeapSize());
    ESP_LOGI(TFT_TAG, "Trying to allocate %lu bytes", size);
    uint16_t *color_p = (uint16_t *)heap_caps_malloc(size, MALLOC_CAP_INTERNAL);
    if (color_p == NULL) {
        ESP_LOGE(TFT_TAG, "Failed to allocate memory (%lu bytes)", size);
        return;
    }
    int i = 0;
    for(i = 0; i < w * h ; i+=1)
    {
        color_p[i] = color;
    }

    lcd_PushColors(xsta, ysta, w, h, color_p);
    free(color_p);
}

The logs I get when it runs:

Task allocation on app_main:
I (454) main: Free Heap size 296752
I (558) main: Free Heap size 291928
I (558) main: Free Heap size 287440
I (565) main: Free Heap size 67048
...
Memory allocation on AXS15231B:
...
I (1377) display: Free Heap size 44024
I (1378) display: Trying to allocate 39200 bytes

Mem alloc fail. size 0x00009920 caps 0x00000800


Backtrace: 0x40375c72:0x3fce7c40 0x4037e47d:0x3fce7c60 0x403765dd:0x3fce7c80 0x40376632:0x3fce7ce0 0x40376d15:0x3fce7d00 0x40376e3d:0x3fce7d50 0x4200c238:0x3fce7d70 0x4200ba76:0x3fce7da0 0x403811de:0x3fce7dc0
0x40375c72: panic_abort at C:/Users/~/esp/esp-idf/components/esp_system/panic.c:452

0x4037e47d: esp_system_abort at C:/Users/~/esp/esp-idf/components/esp_system/port/esp_system_chip.c:84

0x403765dd: heap_caps_alloc_failed at C:/Users/~/esp/esp-idf/components/heap/heap_caps.c:96

0x40376632: heap_caps_malloc at C:/Users/~/esp/esp-idf/components/heap/heap_caps.c:201

0x40376d15: trace_malloc at C:/Users/~/esp/esp-idf/components/heap/include/heap_trace.inc:95

0x40376e3d: __wrap_heap_caps_malloc at C:/Users/~/esp/esp-idf/components/heap/include/heap_trace.inc:184

0x4200c238: lcd_fill(unsigned short, unsigned short, unsigned short, unsigned short, unsigned short) at ###/main/AXS15231B.cpp:284

0x4200ba76: test_lcd(void*) at ###/main/app_main.cpp:48 (discriminator 1)

0x403811de: vPortTaskWrapper at C:/Users/~/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:162

As mentioned the available space is 44024 and it tries to allocate 39200. Why this allocation fails since there is enough memory and how to overcome this issue?

Further increasing the task stack size also does not help, I guess that this task should be statically allocated at compile time with enough memory for a frame buffer but I don't see why the dynamic allocation is not working.


Solution

    1. Try to use ps_malloc instead of heap_caps_malloc. OR
    2. #define BOARD_HAS_PSRAM 1 or add build_flags = -DBOARD_HAS_PSRAM to the platformio.ini file if you are using platformIO.

    Here is my setup for platformIO project: platformio.ini file