Page 1 of 1

Allocating variables to SRAM or PSRAM.

Posted: Sun Dec 15, 2024 5:27 am
by gkeep01
Hi All,

I hope someone can help me. We are looking to use the ESP32 in a product and this all started becuase we need to send TFT sprites to SRAM however there seems to be a size limit on variables sent to SRAM when you have PSRAM enabled on a wrover. it seems to be around the 4KB mark (45 x 45 pixel of 2 bytes per pixel esprite). I have tried everything to force sprites and even malloc and ps_malloc of variables without any luck. does anyone know of a workaround for forcing more than 4kb into SRAM on a wrover while still accessing PSRAM?

If you have a look at the code below you can see I try and attempt to send a 6 and a 4 KB variable to SRAM as well as PSRAM. When attempting to send 6kb to the SRAM buffer its returning a PSRAM address on the serial monitor. Also only 4KB is seen in the SRAM buffer and 16KB is seen in the PSRAM buffer.

Can someone please help with this.

I have tried using different boards in the board manager :
ESP32 dev module
ESP32 wrover module
ESP32 wrover kit (all versions)

Im using:
arduino IDE version 2.3.3

Code: Untitled.cpp Select all


#include "esp_system.h"
#include "esp_spiram.h" // Include this header for PSRAM functions

long initialFreeSRAM;
long initialFreePSRAM;
long usedSRAM = 0; // Track used SRAM
long usedPSRAM = 0; // Track used PSRAM

int *heap1Var; // Pointer to dynamically allocated memory in heap
int *heap2Var; // Pointer to dynamically allocated memory in heap
int *PSRAM1Var; // Pointer to dynamically allocated memory in PSRAM
int *PSRAM2Var; // Pointer to dynamically allocated memory in PSRAM

void checkPSRAM() {
initialFreePSRAM = ESP.getFreePsram(); // Free PSRAM available
Serial.print("Total PSRAM size: ");
Serial.println(esp_spiram_get_size());
Serial.print("Free PSRAM size: ");
Serial.println(initialFreePSRAM);
}

void checkSRAM() {
initialFreeSRAM = ESP.getFreeHeap();
Serial.print("Free SRAM heap memory: ");
Serial.println(initialFreeSRAM);
Serial.println("");
}

void calculateRAMused() {
// Recalculate based on direct tracking
usedSRAM = initialFreeSRAM - ESP.getFreeHeap();
usedPSRAM = initialFreePSRAM - ESP.getFreePsram();
Serial.println("");
Serial.print("**SRAM allocated = ");
Serial.println(usedSRAM);
Serial.print("**PSRAM allocated = ");
Serial.println(usedPSRAM);
}

void setup() {
Serial.begin(115200);
psramInit();
Serial.println("Before allocation of RAM");
checkPSRAM();
checkSRAM();

// Dynamically allocate 4KB of memory on the heap
heap1Var = (int *)malloc(4096); // Allocate 4KB (4096 bytes) on the heap
if (heap1Var != NULL) {
*heap1Var = 14; // Assign a value to the allocated memory

// Print the memory address of the allocated heap memory
Serial.print("Memory address of heap1Var (Heap): ");
Serial.println((intptr_t)heap1Var, HEX);

// Print the value stored in the heap
Serial.print("heap1Var value: ");
Serial.println(*heap1Var);
} else {
Serial.println("Failed to allocate 4KB of memory on the heap");
}

heap2Var = (int *)malloc(6144); // Allocate 6KB (6144 bytes) on the heap
if (heap2Var != NULL) {
*heap2Var = 16; // Assign a value to the allocated memory

// Print the memory address of the allocated heap memory
Serial.print("Memory address of heap2Var (Heap): ");
Serial.println((intptr_t)heap2Var, HEX);

// Print the value stored in the heap
Serial.print("heap2Var value: ");
Serial.println(*heap2Var);
} else {
Serial.println("Failed to allocate 6KB of memory on the heap");
}

// Dynamically allocate 4KB of memory on PSRAM
PSRAM1Var = (int *)ps_malloc(4096); // Allocate 4KB (4096 bytes) in PSRAM
if (PSRAM1Var != NULL) {
*PSRAM1Var = 24; // Assign a value to the allocated PSRAM memory

// Print the memory address of the allocated PSRAM memory
Serial.print("Memory address of PSRAM1Var (PSRAM): ");
Serial.println((intptr_t)PSRAM1Var, HEX);

// Print the value stored in PSRAM
Serial.print("PSRAM1Var value: ");
Serial.println(*PSRAM1Var);
} else {
Serial.println("Failed to allocate 4KB of memory in PSRAM");
}

PSRAM2Var = (int *)ps_malloc(6144); // Allocate 6KB (6144 bytes) in PSRAM
if (PSRAM2Var != NULL) {
*PSRAM2Var = 26; // Assign a value to the allocated PSRAM memory

// Print the memory address of the allocated PSRAM memory
Serial.print("Memory address of PSRAM2Var (PSRAM): ");
Serial.println((intptr_t)PSRAM2Var, HEX);

// Print the value stored in PSRAM
Serial.print("PSRAM2Var value: ");
Serial.println(*PSRAM2Var);
} else {
Serial.println("Failed to allocate 6KB of memory in PSRAM");
}

// Print free heap memory after allocation
Serial.println("");
Serial.println("After Allocating memory to PSRAM");
calculateRAMused();
checkPSRAM();
checkSRAM();

}

void loop() {
// Nothing to do here
}

Re: Allocating variables to SRAM or PSRAM.

Posted: Sun Dec 15, 2024 5:44 am
by Sprite
malloc() in esp-idf uses a very simple strategy to decide where to allocate memory: if the area is larger than some number (seemingly 4K in the arduino configuration) it goes to psram, otherwise to internal memory. If you want to specifically get RAM from some particular source, you can use heap_caps_malloc with the capabilities you need specified.

Re: Allocating variables to SRAM or PSRAM.

Posted: Mon Dec 16, 2024 12:24 am
by gkeep01
Hi ESP_Sprite,

Cool name by the way!

Thank you for your reply I appreciate your time.

I tried what you recommended using heap_caps_malloc to manually allocate memory and maybe im doing something wrong but it keeps acting like its going into SRAM but in fact going into PSRAM once investigated.

Do you know what might be going on here? or what i might be able to do to force this without disabling PSRAM all together.

Code: Select all

#include <TFT_eSPI.h>  // Include the TFT library

TFT_eSPI tft = TFT_eSPI();  // Create TFT object
TFT_eSprite spr1 = TFT_eSprite(&tft);  // Create sprite object

uint8_t *spr1Buffer;  // Pointer for sprite buffer
int spriteXSize = 64;  // Example sprite width
int spriteYSize = 64;  // Example sprite height

long initialFreeSRAM;
long initialFreePSRAM;
long usedSRAM = 0;  // Track used SRAM
long usedPSRAM = 0; // Track used PSRAM

void checkPSRAM() {
  initialFreePSRAM = ESP.getFreePsram();  // Free PSRAM available
  Serial.print("Total PSRAM size: ");
  Serial.println(esp_spiram_get_size());
  Serial.print("Free PSRAM size:  ");
  Serial.println(initialFreePSRAM);
}

void checkSRAM() {
  initialFreeSRAM = ESP.getFreeHeap();
  Serial.print("Free SRAM heap memory: ");
  Serial.println(initialFreeSRAM);
  Serial.println("");
}

void calculateRAMused() {
  // Recalculate based on direct tracking
  usedSRAM = initialFreeSRAM - ESP.getFreeHeap();
  usedPSRAM = initialFreePSRAM - ESP.getFreePsram();
  Serial.println("");
  Serial.print("**SRAM allocated = ");
  Serial.println(usedSRAM);
  Serial.print("**PSRAM allocated = ");
  Serial.println(usedPSRAM);
}


void setup() {
  Serial.begin(115200);
  Serial.println("Starting setup...");
  Serial.println("Before allocation of RAM");
  checkPSRAM();
  checkSRAM();

  tft.begin();  // Initialize the TFT display
  tft.setRotation(3);
  Serial.println("TFT initialized");
  tft.fillScreen(TFT_GREENYELLOW);
  // Allocate sprite buffer in SRAM
  spr1Buffer = (uint8_t*)heap_caps_malloc(spriteXSize * spriteYSize, MALLOC_CAP_INTERNAL);
  if (spr1Buffer == NULL) {
    Serial.println("Failed to allocate sprite buffer in SRAM");
    return;
  }
  Serial.println("Sprite buffer allocated in SRAM");

  // Create sprite with custom buffer (TFT_eSprite will handle the internal buffer)
  spr1.createSprite(spriteXSize, spriteYSize);  // Just create sprite, no custom buffer yet
  Serial.println("Sprite created");

  // Example operation: Fill the sprite with color
  spr1.fillSprite(TFT_RED);
  Serial.println("Sprite filled with red");

  // Push the sprite to the screen
  spr1.pushSprite(10, 10);  // Push the sprite to the screen at position (10, 10)
  Serial.println("Sprite pushed to screen at (10, 10)");
  
  Serial.println("After Allocating memory to PSRAM");
  calculateRAMused();
  checkPSRAM();
  checkSRAM();
  checkSpriteMemory("spr1",spr1);
}

void loop() {
  // Your main loop code
}

void checkSpriteMemory(const char* spriteName, TFT_eSprite& sprite) {
  uint8_t* spriteBuffer = (uint8_t*)sprite.getPointer();
  if (spriteBuffer != nullptr) {
    if (esp_ptr_external_ram(spriteBuffer)) {
      Serial.print(spriteName);
      Serial.println(" is stored in PSRAM...........................***********");
    } else if (esp_ptr_internal(spriteBuffer)) {
      Serial.print(spriteName);
      Serial.println(" is stored in internal SRAM..........................");
    } else {
      Serial.print(spriteName);
      Serial.println(" memory location could not be determined.");
    }
  } else {
    Serial.print(spriteName);
    Serial.println(" buffer is NULL (not allocated).");
  }
}


Serial OUTPUT:

Code: Select all

Before allocation of RAM
Total PSRAM size: 8388608
Free PSRAM size:  4191847
Free SRAM heap memory: 301604

TFT initialized
Sprite buffer allocated in SRAM
Sprite created
Sprite filled with red
Sprite pushed to screen at (10, 10)
After Allocating memory to PSRAM

**SRAM allocated = -872
**PSRAM allocated = 8212
Total PSRAM size: 8388608
Free PSRAM size:  4183635
Free SRAM heap memory: 302476

spr1 is stored in PSRAM...........................***********


Re: Allocating variables to SRAM or PSRAM.

Posted: Mon Dec 16, 2024 3:07 am
by lbernstone
It's very likely that when you call createSprite it is trashing whatever you had allocated, and then starting over (using the default allocator).
You will probably have to customize the Sprite.cpp functions to only call the ps_ allocation functions when the sprite fits in the range you want.

Re: Allocating variables to SRAM or PSRAM.

Posted: Mon Dec 16, 2024 5:42 am
by boarchuz
You allocate spr1Buffer in internal memory but it's never used. Look into C++'s "placement new".