Utilisation de la PSRAM

Cet article explique comment l’utiliser la RAM externe supplémentaire de la carte uPesy ESP32 Wrover Devkit sur le Framework Arduino et sur le Framework ESP-IDF.

Qu’est-ce que c’est ?

La quantité de RAM présente sur les microcontrôleurs est très faible comparée à celle d’ordinateurs. L’ESP32 est un microcontrôleur qui possède beaucoup de RAM car il en a besoin pour pouvoir utiliser correctement le Wifi et le Bluetooth. La RAM présente sur les ordinateurs est une RAM externe. Il est aussi possible d’avoir une RAM externe sur l’ESP32 beaucoup plus grosse que celle interne (8x)

La PSRAM est une RAM supplémentaire externe qui est présente dans certains modules ESP32:
  • ESP32-WROVER-B

  • ESP32-WROVER-I

L’ESP32 communique avec la PSRAM par SPI. C’est pourquoi elle est aussi appelée SPI RAM La PSRAM est branchée sur le même bus SPI que la FLASH.

../_images/psram.jpg

Aperçu interne du module ESP-WROVER-B

Note

Il y a une ambiguïté sur la taille réelle de la PSRAM. La taille réelle de la PSRAM (sur la puce de silicium) n’est pas de 4 Mo mais de 8 Mo. Par contre 4 Mo seulement peuvent être accessibles facilement par le logiciel. Pour utiliser les 8 Mo de la PSRAM , consulter Use 8 MB of PSRAM

Intéret

L’intéret d’une RAM externe est de pouvoir soulager la RAM interne pour le stockage de données importantes. On peut :

  • Créer d’énormes tableaux

  • Utiliser des frames buffers pour des écrans LCD

  • Enregistrer des photos (sur les cartes ESP32 Cam)

  • Télécharger des gros fichiers Json

  • Lire et manipuler les données de gros fichiers

  • Stocker des pages WEB entières

Comment l’utiliser ?

Avertissement

Sur l’IDE Arduino, pour pouvoir utiliser la PSRAM, il faut sélectionner une carte compatible, comme par exemple la carte ESP32 Wrover Module.

Par défaut, la PSRAM n’apparaît pas dans la table des mémoires de l’ESP32, c’est donc normal que la taille de la RAM indiquée sur Arduino IDE ou Plateformio soit toujours de 320 Ko. Il y a donc par défaut, une séparation claire entre la RAM interne de l’ESP32 (320 Ko) et la RAM externe (4 Mo).

Pour stocker une variable dans la PSRAM, il faut faire une allocation dynamique avec des fonctions spécifiques pour la PSRAM de l’ESP32.

Note

Il n’est pas nécessaire d’avoir des connaissances avancées en C/C++ pour utiliser la PSRAM. La connaissances des pointeurs, des fonctions d’allocations dynamiques comme malloc() ou calloc() est un plus, mais les exemples proposés se suffisent à eux-mêmes pour utiliser la PSRAM dans des projets d’électronique. Pour plus d’informations sur l’allocation dynamique, regardez L’allocation dynamique sur OpenClassroom

Framework Arduino

Les fonctions principales à utiliser sont les suivantes psramInit(), ESP.getFreePsram() , ps_malloc() ou ps_calloc() et free().

Exemples

Cette section propose des exemples d’utilisation de la PSRAM. Ils permettent de comprendre comment utiliser les foncions citées ci-dessus. Il n’est pas nécessaire de tout comprendre pour pouvoir utiliser la PSRAM, il suffit de modifier les valeurs dans les exemples pour les utiliser dans vos projets.

  • Cet exemple montre comment créer un tableau d’entiers de 1000 éléments stocké dans la PSRAM:

int n_elements = 1000;

void setup(){
        Serial.begin(9600);
        //Initialisation
        if(psramInit()){
        Serial.println("\nLa PSRAM est correctement initialisée");
        }else{
        Serial.println("La PSRAM ne fonctionne pas");
        }

        //Création d'un tableau de n_elements
        int tailleInitiale = ESP.getFreePsram();
        Serial.println((String)"Mémoire disponible PSRAM (octets): " + tailleInitiale); // Affiche le nombre d'octets encore disponible dans la PSRAM

        int *tableau = (int *) ps_malloc(n_elements * sizeof(int)); //Créer un tableau d'entiers de n_elements
        tableau[0] = 42;
        tableau[999] = 42; //On accéde aux éléments comme pour un tableau classique

        int taillePSRAM = ESP.getFreePsram();
        Serial.println((String)"Mémoire disponible PSRAM (octets): " + taillePSRAM); // L'espace libre de la mémoire a diminuée
        int tailleTableau = tailleInitiale - taillePSRAM;
        Serial.println((String)"Taille du tableau dans la mémoire en octets : " + tailleTableau);

        //Suppression du tableau
        free(tableau); //La memoire alloué est libérée.
        Serial.println((String)"Mémoire disponible PSRAM (octets): " +ESP.getFreePsram());
}

void loop() {
}

Sortie du terminal :

La PSRAM est corectement initialisée
Mémoire disponible PSRAM (octets): 4194252
Mémoire disponible PSRAM (octets): 4190236
Taille du tableau dans la mémoire en octets : 4016
Mémoire disponible PSRAM (octets): 4194252

Note

La taille en octets du tableau est de 4016 pour un tableau de 1000 entiers (int). Le type int est codé sur 4 octets. Il y a donc 4 * 1000 octets occupés par le tableau, les 16 octets restants contiennent des informations sur le bloc mémoire (taille, flags).

  • Pour des tableaux d’autres types de variables :

char *tableau1 = (char *) ps_malloc(n_elements * sizeof(char)); //Créer un tableau vide de n_elements caractères
float *tableau = (float *) ps_malloc(n_elements * sizeof(float)); //Créer un tableau de nombres avec virgules
  • On peut aussi créer des tableaux qui sont remplis de zéros avec ps_calloc() :

int n_elements = 20;
Serial.println((String)"Mémoire disponible PSRAM (octets): " +ESP.getFreePsram());
Serial.println("Tableau d'entiers initialisés à 0");
int *tableau = (int *) ps_calloc(n_elements, sizeof(int));
Serial.print("[tableau] : ");
for(int i=0; i!= n_elements;i++){
        Serial.print((String)tableau[0] + " ");
}
Serial.println((String)"\nMémoire disponible PSRAM (octets): " +ESP.getFreePsram());

Sortie du terminal :

Mémoire disponible PSRAM (octets): 4194252
Tableau d'entiers initialisés à 0
[tableau] : 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Mémoire disponible PSRAM (octets): 4194156
  • Voici 2 manières pour créer des tableaux à 2 dimensions :

int n_lignes = 10;
int n_colonnes = 20;


void setup(){
          Serial.begin(9600);
          //Initialisation
          if(psramInit()){
            Serial.println("\nLa PSRAM est corectement initialisée");
          }else{
            Serial.println("La PSRAM ne fonctionne pas");
          }

          //Création d'un tableau de n_elements
          int tailleInitiale = ESP.getFreePsram();
          Serial.println((String)"Mémoire disponible PSRAM (octets): " + tailleInitiale); // Affiche le nombre d'octets encore disponible dans la PSRAM

          //Création d'un tableau à deux dimensions avec un tableau à une dimension
          int *tableau2D = (int *) ps_malloc(n_lignes * n_colonnes * sizeof(int)); // Création d'un tableau de n_lignes x n_colonnes d'entiers codés sur 4 octets
          //Pour accéder à la case situé à la ligne 5 à la colonne 10
          int ligne_i = 5;
          int colonne_j = 10;
          tableau2D[colonne_j * n_colonnes +ligne_i] = 43;

          //Création d'un tableau rempli de zero à deux dimensions avec un tableau de pointeurs
          int **tableau2Dbis = (int **) ps_calloc(n_lignes, sizeof(int *));
          for (int i =0; i!= n_colonnes;i++){
                tableau2Dbis[i] = (int *) ps_calloc(n_colonnes , sizeof(int)); //Créer un tableau de pointeurs
          }
          //Pour accéder à la case situé à la ligne 5 à la colonne 10
          tableau2Dbis[ligne_i][colonne_j] = 42;


          int taillePSRAM = ESP.getFreePsram();
          Serial.println((String)"Mémoire disponible PSRAM (octets): " + taillePSRAM); // L'espace libre de la mémoire a diminuée
          int tailleTableau2D = tailleInitiale - taillePSRAM;
          Serial.println((String)"Taille du tableau dans la mémoire en octets : " + tailleTableau2D);

          //Suppression du tableau
          free(tableau2D); //La memoire alloué est libérée.
          free(tableau2Dbis);
          Serial.println((String)"Mémoire disponible PSRAM (octets): " +ESP.getFreePsram());
}

void loop() {
}

Liste détaillée des fonctions

psramInit()
Renvoie

true si la PSRAM s’est bien initialisée, false sinon

Type renvoyé

booléen

Cette fonction s’assure que la PSRAM est utilisable en faisant différents tests, puis l’initialise.

ESP.getFreePsram()
Renvoie

La mémoire disponible en octets de la PSRAM.

Type renvoyé

int

Cette fonction permet de connaître la mémoire disponible dans la PSRAM. La taille maximale est de 4 194 252 octets.

ps_malloc(size_t tailleBloc)
Paramètres
  • tailleBloc – nombre total d’octets que l’on veut réserver dans la RAM.

Renvoie

Un pointeur du type donné en argument qui contient l’adresse du bloc mémoire.

Type renvoyé

void *

Cette fonction permet d’allouer un bloc de mémoire dans la PSRAM.

ps_calloc(size_t nElements, int tailleElement)
Paramètres
  • nElements – Nombre d’éléments que l’on veut réserver dans la RAM.

  • tailleElement – Taille en octets d’un seul élément.

Renvoie

Un pointeur du type donné en argument qui contient l’adresse du bloc mémoire.

Type renvoyé

void *

Cette fonction permet d’allouer un bloc de mémoire de nElements, chacun de taille tailleElement dans la PSRAM et initialise tous ces octets à la valeur de 0.

ps_realloc(void * adresseBloc, nouvelleTailleBloc)
Paramètres
  • adresseBloc – Adresse mémoire du bloc de mémoire à réallouer.

  • nouvelleTailleBloc – Nouvelle taille en octets du bloc mémoire que l’on veut réserver dans la RAM.

Renvoie

Un pointeur du type donné en argument sur la zone mémoire réallouée.

Type renvoyé

void *

Cette fonction permet de réallouer un bloc de mémoire préalablement alloué avec ps_malloc() ou ps_calloc() . Si l’espace mémoire libre qui suit le bloc à réallouer est suffisamment grand pour la nouvelle taille du bloc, le bloc de mémoire d’origine est simplement agrandi. Par contre si l’espace libre n’est pas suffisant, un nouveau bloc de mémoire sera alloué, le contenu de la zone d’origine recopié dans la nouvelle zone et le bloc mémoire d’origine sera libéré automatiquement.

free(void *pointer)
Paramètres
  • pointer (*int, *char, *double, *long...) – L’adresse du bloc de mémoire à désallouer

Cette fonction libère un bloc de mémoire alloué dynamiquement avec ps_malloc() ou ps_calloc().

Framework ESP-IDF

Les fonctions utilisées sur le framework Arduino sont basées sur celles du Framework ESP-IDF. Ainsi les fonctions décrites ci-dessous sont utilisées implicitement sur le Framework Arduino-ESP32.

Les fonctions à utiliser sont les suivantes esp_spiram_init(), heap_caps_get_free_size() , heap_caps_malloc() ou heap_caps_calloc() et free()

Voir aussi

Pour des informations plus poussées, consulter la documentation officielle sur la PSRAM

Librairies Arduino / ESP32

Certaines librairies Arduino (compatibles avec l’ESP32) prennent en charge la PSRAM