ESP32 – How To Use PSRAM
The ESP32 has a lot more internal RAM than the ESP8266 had. But it can use even more by addressing up to 4MB of external SPI RAM memory. In this blog post we will show you how to use the PSRAM in your projects.
Especially when working with graphical applications you can never have enough working memory. For instance, our Minigrafx library allocates a frame buffer first in RAM. Then all drawing operations use only on this memory. After the application finishes drawing a frame it tells the library to write the complete content of the frame buffer to the display. For a display with 320×240 pixel and 16bit of color information per pixel this makes 150kb only for the frame buffer.
The ESP8266 has by far not enough RAM to hold this amount of information in the RAM. That’s why we used an old trick known from the first home computers to reduce the color information per pixel. Instead of 16bit “True Color” we use a palette of defined colors. Before the library writes the frame buffer to the display it looks up the real color value and replaces the palette index with that value. With a palette of 4 colors, we only need 2 bits per pixel (2^2) or roughly 19kb!
ESP-WROVER-B: 8MB of PSRAM?
Espressif, the manufacturer of the ESP32, sells a module called ESP-WROVER-B and it is often advertised with 8MB of PSRAM. PSRAM stands for pseudo static RAM. It might be true that the module comes with an external 8MB PSRAM chip but as a matter of fact you can (currently?) only use the lower 4MB in your applications. Don’t get me wrong, 4MB is still a lot of RAM for a microcontroller but the 8MB ads are a bit misleading.
If we believe Espressif’s description of external PSRAM then the 4MB limit is rather based on silicon limitations than limitations in the ESP-IDF framework. The page states “It can be insufficient for some purposes, so ESP32 has the ability to also use up to 4 MB of external SPI RAM memory.” This sounds more like a hard limit and not one that will be soon removed by further work on the SDK.
How Much RAM is available?
So how do you use this external RAM? Before we get into that let’s make sure that our ESP32 module has this external PSRAM and that it is addressable from our code. The ESP32/Arduino platform exposes a couple of methods to find out how much RAM you have in total and how much you can use.
#include <Arduino.h>
void setup() {
log_d("Total heap: %d", ESP.getHeapSize());
log_d("Free heap: %d", ESP.getFreeHeap());
log_d("Total PSRAM: %d", ESP.getPsramSize());
log_d("Free PSRAM: %d", ESP.getFreePsram());
}
void loop() {}
Note that I’m using the logging macro log_d(..)
which allows us to later disable the log output. If we run this code in the Arduino IDE with the following settings in the tool menu. Especially make sure that you have the Core Debug Level set to Verbose.
Running the code shows us the following lines in the Serial Monitor:
[D][esp32-hal-psram.c:47] psramInit(): PSRAM enabled
[D][PSRAMTestArduino.ino:4] setup(): Total heap: 393356
[D][PSRAMTestArduino.ino:5] setup(): Free heap: 367948
[D][PSRAMTestArduino.ino:6] setup(): Total PSRAM: 4194252
[D][PSRAMTestArduino.ino:7] setup(): Free PSRAM: 4194252
Great! The log output tells us that the PSRAM is on and that we have 4MB of PSRAM available. If we run the same code in the Platformio IDE we get this:
[D][main.cpp:4] setup(): Total heap: 390484
[D][main.cpp:5] setup(): Free heap: 365140
[D][main.cpp:6] setup(): Total PSRAM: 0
[D][main.cpp:7] setup(): Free PSRAM: 0
Hm, strange, isn’t it? Maybe I chose the wrong board. This is configured in the platformio.ini:
[env:esp-wrover-kit]
platform = espressif32
board = esp-wrover-kit
framework = arduino
monitor_speed = 115200
upload_speed = 921600
build_flags = -DCORE_DEBUG_LEVEL=5
Please note that the last line is required to see the log_d(..)
output. But why we don’t see the PSRAM? It turns out that we need to enable PSRAM configuration manually. We have to enable this by adding a build flag to the platformio.ini:
build_flags = -DCORE_DEBUG_LEVEL=5
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
BOARD_HAS_PSRAM
enables PSRAM support and fix-esp32-psram-cache-issue
is a workaround for a sequence of code which can crash the board when PSRAM is enabled. Read more about this here.
How To Use the PSRAM?
In the last paragraph, we have seen how we can make sure that PSRAM is available. Now we’re going to see how we can use it. According to the Espressif manual, there are four ways to use the PSRAM. In this blog post we are only going to look at one.
The internal RAM is already quite big. So you are most likely going to use the external memory to allocate a relatively big buffer. For this, we can use ps_malloc()
and free()
to release the memory. Let’s see where the memory is allocated with this little program:
#include <Arduino.h>
void logMemory() {
log_d("Used PSRAM: %d", ESP.getPsramSize() - ESP.getFreePsram());
}
void setup() {
logMemory();
byte* psdRamBuffer = (byte*)ps_malloc(500000);
logMemory();
free(psdRamBuffer);
logMemory();
}
void loop() {}
Running the code show this on the serial console:
[D][esp32-hal-psram.c:47] psramInit(): PSRAM enabled
[D][main.cpp:4] logMemory(): Used PSRAM: 0
[D][main.cpp:4] logMemory(): Used PSRAM: 500000
[D][main.cpp:4] logMemory(): Used PSRAM: 0
Congratulations, you have just successfully allocated memory in the external PSRAM!
More about PSRAM
If you like to know more about PSRAM then the esp32-hal-psram.c file from the ESP32/Arduino platform is an interesting source. There we find other functions we haven’t covered in this post:
bool psramFound();
void *ps_malloc(size_t size);
void *ps_calloc(size_t n, size_t size);
void *ps_realloc(void *ptr, size_t size);
For instance, you can use psramFound()
to check if pseudo ram is available, rather than checking for the size of available external memory.
Summary
In this blog post, we looked at how we can make sure that PSRAM is available in our application code. We then looked at how we can use it to allocate memory. We also learned that the ESP32 is limited to 4MB of PSRAM, even if some boards come with 8MB chips.