Chaines de caractères en mémoire Flash et non en RAM

Bonjour à tous,

Je développe un éditeur MIDI pour Synthétiseur Roland JU-06
Ci-dessous, un extrait fonctionnel de mon code:

//==============================================================================================================================
#include <LiquidCrystal.h>
LiquidCrystal lcd(2,3,4,5,6,7);
//==============================================================================================================================
void setup() 
{
  Serial.begin(31250);
  lcd.begin(16,2);
}
//==============================================================================================================================
void loop() 
{
  byte parametre = Lecture_Serie();
  if(parametre != 0) affichage(parametre);
}
//==============================================================================================================================
void affichage(byte param)
{
    switch (param)
    {
    case 1: Ecran_2("DCO LFO"); break;    
    case 2: Ecran_2("DCO PWM"); break;    
    case 3: Ecran_2("DCO SUB"); break;
    case 4: Ecran_2("DCO NOICE"); break;
    case 5: Ecran_2("HPF FREQ"); break;        
    case 6: Ecran_2("VCF FREQ"); break;
    case 7: Ecran_2("VCF RES"); break;      
    case 8: Ecran_2("VCF ENV"); break;
   
    /*------------------
    ---- etc ---------
    ------------------*/
    
    case 49: Ecran_2("ENV Sustain"); break;
    case 50: Ecran_2("ENV Release"); break;
    } // fin de switch case 

}
//==============================================================================================================================
void Ecran_2(String string1)
{      
      lcd.clear(); //Effacer écran
      lcd.setCursor(0,0); lcd.print(string1); //Affichage du nom du paramètre
} //Fin de fonction
//==============================================================================================================================
byte Lecture_Serie()
{
  byte temp = 0;
  if(Serial.available() > 0) temp = Serial.read();   
  return temp;
}
//==============================================================================================================================

Cela consiste à lire un octet sur le port série et à afficher sur un écran LCD la valeur du paramètre correspondant.
Mon code fonctionne mais toutes les chaines de caractères de la fonction Affichage() sont stockées en mémoire RAM et cela prend trop de place.
Pourriez vous m'aider à écrire la même chose mais avec les chaines stockées dans la mémoire Flash ?
Merci par avance.
Philippe.

tu encadres toutes tes données par : F(). Exemple :

case 1: Ecran_2("DCO LFO"); break;

devient :

case 1: Ecran_2(F("DCO LFO")); break;

Cordialement.

Pierre

et utilisez des strings (tableau de caractères) pas des Strings (la classe - avec une majuscule)

la classe mange plein de mémoire

Merci beaucoup pour vos réponses rapides et précises.

Il me semble que ce n'est pas tout à fait aussi simple

Partons d'une fonction qui prend en paramètre une chaîne de caractères

void afficher(char *texte) {
  Serial.println(texte);
}

Précisément, la variable "texte" est un pointeur, c'est-à-dire un entier non signé sur 16 bits, qui contient l'adresse en RAM du premier caractère de la chaîne.

Cette fonction peut être appelée ainsi

char ligne[] = "Bonjour";
afficher(ligne);

ou même

afficher("Bonjour")

ce qui revient au même car dans ce cas le compilateur crée une variable globale et passe son adresse en paramètre d'appel de la fonction.
Et c'est bien là le problème : la variable globale occupe de manière fixe de l'espace en RAM.

Quand on utilise F("Bonjour"), le résultat est un pointeur différent : sa valeur représente une adresse en Flash.
Plus exactement, c'est un const __FlashStringHelper*

Etafficher(F("Bonjour"));générera une erreur de compilation.

Il faut d'abord appeler une fonction de copie de la flash vers la RAM, dans un buffer de stockage temporaire, avant de faire traiter la chaîne de caractères par une fonction classique.

Illustration :

void afficherRAM(char *texte) {
  Serial.println(texte);
}

void afficherFlash(const __FlashStringHelper *pointeur) {
  char texte[80]; //prévoir assez large, ou bien utiliser strncpy_P
  strcpy_P(texte, (const char *)pointeur); //transfert en RAM
  afficherRAM(texte);
}

void setup() {
  Serial.begin(9600);
  afficherRAM("Bonjour");
  afficherFlash(F("Bonjour"));
}

void loop(){
}

NB : la méthode ::print est définie dans plusieurs formats d'appel, dont
::print(const char )
::print(const __FlashStringHelper
)

Ce qui fait qu'on a l'impression qu'il suffit d'ajouter le F() pour traiter depuis la flash, mais ce n'est pas vrai pour toutes les méthodes.

bricoleau:
... Ce qui fait qu'on a l'impression qu'il suffit d'ajouter le F() pour traiter depuis la flash, mais ce n'est pas vrai pour toutes les méthodes.

Tu as raison, et je l'ai essayé à mes dépends.

Pour autant, dans le cas présent, ça fonctionne, je l'ai testé avant de répondre.

Cordialement.

Pierre

Ca doit être lié à la classe String

Perso je n'utilise jamais cette bestiole

on n'est pas obligé d'avoir un gros buffer.. cf la fonction print

size_t Print::print(const __FlashStringHelper *ifsh)
{
  const char PROGMEM *p = (const char PROGMEM *)ifsh;
  size_t n = 0;
  while (1) {
    unsigned char c = pgm_read_byte(p++);
    if (c == 0) break;
    n += write(c);
  }
  return n;
}

en fait suffit de sous classer et de surcharger les méthodes d'affichage (write même) dans votre objet qui sait "imprimer"

J'ai testé dans mon code la méthode:

Ecran_2(F("DCO LFO")); break;

Cela fonctionne bien et libère effectivement beaucoup de place en RAM.