Sauvegarder / Récupérer des réels en EEPROM de l'ATmega328

Bonjour les copains Arduinautes,
Rencontrant un problème de collision de pile, pour libérer de la mémoire dynamique je désire sauvegarder un tableau de “float” en mémoire EEPROM du microcontrôleur.
Mais avec <EEPROM.h> je ne sais qu’y écrire et lire des Octets.
Avez-vous une idée pour réaliser une telle opération s’il vous plait ?
j’ai pensé initialement envoyer les quatre octets de float, mais encore faut-il savoir les coder et surtout les replacer dans une variable de type “float”.
Bref … je patauge !
Merci d’avance pour l’attention que vous accorderez à mon post.
Amicalement : Nulentout

bonsoir http://playground.arduino.cc/Code/EEPROMex

La solution est sans doute ici :http://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html Au passage tu pourra remarquer la fonction Wiring/Arduino n'est qu'une encapsulation dans une classe d'une seule des nombreuses fonctions Atmel .

Voir put() et get() de la librairie
https://www.arduino.cc/en/Reference/EEPROM

Autrement un float c’est quatre octets. Il suffit d’écrire une fonction qui reçoit un pointeur sur la variable, qui va chercher les quatre octets et les écrit en EEPROM.

Pour manipuler les octets qui composent le float il faut passer par un pointeur intermédiaire.

void ecritEEPROM(int adresse, float *varFloat){
unsigned char *ptvar;
char index;

ptvar = (unsigned char*)varFloat;
   for(index=0;index<4;index++)
      EEPROM.write(adresse+index, *(ptvar+index));
   }
}

:) EEPROMex.h

Salut

N’en prenez pas ombrage, je vais essayer d’être un poil plus didactique.
J’espère ne pas me planter, les bouts de code sont donnés sans test.

Nulentout : si tu sais manipuler des octets, alors tu sais manipuler tout type de donnée.

Chaque type de donnée (ou structure, ou tableau) occupe un certain nombre d’octets de stockage (en RAM, dans l’Eeprom, en flash, …).

L’opérateur sizeof est bien utile pour connaître ce nombre.
Il peut être utilisé aussi bien sur un type que sur une variable ou un tableau.
Par exemple : sizeof(float) te retourne 4

Admettons que tu écrives une fonction qui traite un tableau d’octets.

Par exemple :

void ecrireEeprom(int adresse, byte tableau[], int taille)
{
  for (int i = 0; i < taille; i++)  EEPROM.write(adresse++, tableau[i]);
}

Tu peux ensuite utiliser cette fonction pour traiter n’importe quelle donnée
Par exemple

 float f;
...
  ecrireEeprom(0, (byte *)&f, sizeof(f));

(byte *)&f revient à passer l’adresse mémoire de f comme paramètre d’appel, en précisant au compilateur que cette adresse référence un octet et non un float.

Idem pour un tableau de float

 float f[10];
...
  ecrireEeprom(0, (byte *)&f[0], sizeof(f));

Voilà pour le principe.

Tu peux utiliser exactement la même logique générique de traitement d’un tableau d’octets, pour tout autre besoin.

Par exemples :

  • afficher en hexadécimal le contenu exact de la RAM associée à une variable
  • lire des valeurs constantes présentes en flash uniquement, car déclarées avec PROGMEM

D’ailleurs à ce sujet, si ton tableau de float a des valeurs constantes définies à la compil, il serait plus adéquat d’utiliser la flash au lieu de l’eeprom.

Bonjour les Amis, En premier, merci d’avoir répondu à ma question. Pour ce qui concerne la manipulation d’Octets, aucun problème. Par exemple en EPROM j’ai logé un petit dessin qui est affiché sur un écran LCD graphique. Pour ce qui est des pointeurs, j’avais creusé la question et effectué tout un tas de manipulations. Je m’étais même réalisé un petit livret au format A3 sur les pointeurs … le moment est venu de l’imprimer, car je n’ai pas de mémoire. Il faut que je m’y remette sérieusement. Mais mes manipulations portaient sur des Octets. Donc pour le réel j’ai testé un tas de choses, avec des pointeurs, et je n’y suis pas parvenu. Je sais qu’un réel n’est constitué que de 4 octets. On les transmet dans l’ordre et c’est fini … sauf que je n’y suis pas arrivé, maitrisant encore très mal la syntaxe pour les pointeurs. Donc … il vaut mieux un qui sait que dix qui cherchent. MERCI pour vos réponses, je vais tester tout ça. La balle est dans mon camp, maintenant c’est à moi de faire un effort. Je vais commencer par réviser sérieusement les pointeurs pour mieux comprendre vos propositions. QUOI qu’il en soit et quel que soit l’aide que je vais tirer de vos réponses : MERCI. P.S : Il n’y a jamais à s’excuser de faire un effort de pédagogie. C’est toujours au bénéfice des lecteurs.

Bonjour les copains, Autre question … j’exagère ! bricoleau a écrit : D'ailleurs à ce sujet, si ton tableau de float a des valeurs constantes définies à la compil, il serait plus adéquat d'utiliser la flash au lieu de l'eeprom. Je ne saisis pas pourquoi ce serait plus logique, vu que l’EEPROM est disponible et n’est généralement pas utilisée. Autant « la gaver » ?

Rencontrant un problème de collision de pile, pour libérer de la mémoire dynamique je désire sauvegarder un tableau de "float" en mémoire EEPROM

Pourquoi ne pas s'attaquer au vrai problème (l'utilisation de mémoire dynamique sur un petit µc) ?

Si tu passes par l'eeprom, cela va t'obliger à téléverser un premier programme d'initialisation, qui ne fait qu'écrire dans l'eeprom les valeurs constantes, avant de téléverser ton programme principal qui va utiliser l'eeprom en lecture uniquement.

C'est lourdingue. Classiquement, l'eeprom ne devrait être utilisée que pour y stocker des paramètres, c'est-à-dire des valeurs ayant une bonne stabilité, mais quand même susceptibles d'être modifiées de temps en temps.

Pour des valeurs constantes, le plus simple est de les laisser en flash.

Par exemple simplifié, admettons que le besoin soit d'avoir le tableau ci-dessous :

{0.0, 0.5, 1.5, 3.0}

Un programmeur débutant va le coder en variable globale :

float tableau[4] = {0.0, 0.5, 1.5, 3.0};

Un programmeur plus au point va le déclarer proprement en constante

const float tableau[] = {0.0, 0.5, 1.5, 3.0};

C'est la manière standard de programmer.

Ce tableau a besoin de 4x4 = 16 octets pour être stocké. Ces 16 octets (correctement valorisés) sont quelque part dans le fichier .hex généré par le compilateur et téléversé dans la mémoire flash de l'arduino.

Au lancement du programme, dans la phase d'initialisation des variables globales, ces 16 octets sont recopiés depuis la flash vers la RAM, dans un espace qui leur est alloué. Ce qui diminue d'autant la RAM disponible.

Pour économiser ces 16 octets de RAM, l'astuce consiste à déclarer ce tableau en PROGMEM.

L'inconvénient est alors que le programme n'a plus un accès direct à ces valeurs (il n'accède directement qu'au contenu de la RAM). A chaque fois que le programme a besoin de lire tout ou partie du tableau, il doit appeler une fonction spécifique, qui recopie le contenu de la flash dans une variable allouée temporairement en RAM. Cette mécanique d'accès aux données est aussi un peu plus lente qu'une lecture directe en RAM.

Mais une fois la valeur utilisée, la variable temporaire est libérée et la RAM est disponible pour autre chose.

A noter que la problématique est exactement la même avec les chaînes de caractères, qui sont très souvent à l'origine d'une saturation de RAM.

Un simple

Serial.print("Bonjour");

est équivalent à :

const char message[] = "bonjour";//7+1=8 octets occupés
Serial.print(message);

Toutes les chaînes de caractères contenues dans le code source, sont remplacées par un pointeur vers une variable globale constante.

Et là on voit bien que c'est exactement la même problématique que ton tableau de float

Voilà pour ta question, j'espère être compréhensible.

C'est sur qu'il vaudrait mieux commencer par appréhender de manière globale ton problème de RAM saturée, avant d'essayer telle ou telle solution, qui passera peut-être à côté du problème principal.

a+

Merci pour cette dernière réponse qui ouvre des pistes. Pour ma part, écrire un picoprogramme pour loger des entités en EEPROM reste élémentaire. Donc ce n’est en rien une « lourdeur ». Comme les informations que vous m’avez tous donné dans ce post m’ont permis d’arriver à résoudre mon problème, pour le moment je préfère continuer à développer mon programme. Mais il reste évident qu’il y a matière à réflexion. Aussi je note dans mes documents ce lien, pour revenir creuser tout ça quand j’aurais le temps. (Donc … jamais !) Le problème que j’ai rencontré est « vicieux ». J’ai un programme complet, dans lequel j’ajoute module par module. Chaque module est développé à part, ce qui évite d’avoir à compiler de gros sources. Quand le module est au point, j’ajoute au programme complet les variables nouvelles et le code du module. Je recompile le total et ça fonctionne … sauf cette fois. J’avais un aléas d’affichage sur un LCD OLED. Mon code ne comportait aucune erreur puisque « parfait » en module élémentaire. Le compilateur annonce à peine 39% consommé dans la mémoire dynamique. Comme le bug m’a fais soupçonner une collision de PILE avec HEAP j’ai testé la méroire RAM disponible avec le « classique » if (brkval == 0) {return (int) &BIDON -(int) &heap_start;} else {return (int) &BIDON -(int) __brkval;} Alors que le compilateur annonce plus de 1000 octets de disponible, le test avec les pointeurs n’en laisse plus qu’une petite centaine. Je me demande si le compilateur ne se fourvoie pas un peu, tout au moins je ne sais pas comment expliquer cette différence. Par contre, avoir passé mes 18 réels en EEPROM corrige effectivement le problème. Reste que loger en EEPROM les chaines de caractères pour les affichages est effectivement un moyen très utile de gagner de la place en RAM dynamique. RESUME : Vos réponses m’ont dépanné et je vous en remercie tous chaleureusement. Il reste encore pas mal de détails à élucider et d’expériences à tenter.

nulentout: Alors que le compilateur annonce plus de 1000 octets de disponible, le test avec les pointeurs n’en laisse plus qu’une petite centaine. Je me demande si le compilateur ne se fourvoie pas un peu

Le compilateur ne comptabilise que les variables globales. Il ne peut pas prendre en compte les variables locales et les variables allouées dynamiquement.

Arf,

C'est vrai qu'on a toujours tendance à privilégier les solutions que l'on connaît/maîtrise, au détriment d'un peu de curiosité vers d'autres solutions qui peuvent s'avérer meilleures.

Je parie que lorsque tu te seras plongé une fois dans Progmem, tu ne te poseras plus la question d'utiliser l'eeprom.

Le code ci-dessous devrait fonctionner :

const float tableau[] PROGMEM = {0.0, 0.5, 1.5, 3.0};

float lireTableau(int index)
{
  return pgm_read_float_near(&tableau[index]);
}

N'est-ce pas plus simple que d'utiliser l'eeprom?

Pourquoi tu persiste à lui montrer des exemples avec PROGMEM alors que nulentout n'arrête pas de répéter "mémoire dynamique" ?

Je me répète aussi, son véritable problème est qu'apparemment il alloue de la mémoire dynamiquement et s'étonne de rencontrer des problèmes. Mais puisqu'il ne veut pas résoudre ce problème et préfère réparer son code "avec du scotch", sans jamais montrer son code d'ailleurs, je laisse tomber...

Ne te fâche pas, regarde :

guix: Pourquoi tu persiste à lui montrer des exemples avec PROGMEM alors que nulentout n'arrête pas de répéter "mémoire dynamique" ?

Parce qu'à mon sens le sujet principal est bien de libérer la RAM des constantes qui l'encombrent, et j'essaye d'ouvrir les yeux à notre ami sur le fait que PROGMEM est un moyen plus simple d'y parvenir que l'eeprom.

guix: Je me répète aussi, son véritable problème est qu'apparemment il alloue de la mémoire dynamiquement et s'étonne de rencontrer des problèmes.

Je pense que nulentout n'en n'est pas à gérer des allocations dynamiques massives avec des malloc ou des new, et que dans son vocabulaire mémoire dynamique désigne juste la RAM. Ceci étant dit, nous lui prodiguons le même conseil :

bricoleau: ...

Voilà pour ta question, j'espère être compréhensible.

C'est sur qu'il vaudrait mieux commencer par appréhender de manière globale ton problème de RAM saturée, avant d'essayer telle ou telle solution, qui passera peut-être à côté du problème principal.

guix: Mais puisqu'il ne veut pas résoudre ce problème et préfère réparer son code "avec du scotch", sans jamais montrer son code d'ailleurs, je laisse tomber...

Pour ma part je préfère aider les personnes à comprendre les principes pour analyser leurs programmes par elles-mêmes, plutôt que celles qui nous balancent leur code en demandant de l'aide pour le mettre au point.

Je ne me fâche pas ;)

C'est juste qu'il y a sûrement d'autres moyens d'optimiser son code sans avoir recours à une autre mémoire que la SRAM (qui est bien plus rapide ).

Et ne pas oublier que l'EEPROM s'use à force d'écrire dedans, elle est garantie d'endurer au moins 100.000 cycles d'écriture sur une même cellule (et à propos la Flash c'est encore pire: 10.000 cycles ). Si nulentout écrit dedans toutes les secondes, en quelques jours son EEPROM ne fonctionnera plus correctement....

Voilà pourquoi il faut voir le code pour en savoir plus et donner les conseils en conséquence...

Désolé j'ai dû rater quelques lignes de tes romans :stuck_out_tongue:

C'est vrai que y a des fois, j'ai les doigts qui s'emballent sur le clavier... A la fin je suis obligé de les relire, pour voir si je suis d'accord avec eux :)

Discution très utile qui ne manquera pas d'intéresser les débutants, et pas si débutant que cela, qui liront ces lignes. C'est aussi cela le forum : ne pas répondre strictement à la question d'un unique individu et au contraire penser communauté et profiter d'une réponse pour faire passer des idées.

Petit complément, il me semble que la librairie standard EEPROM gère bien les floats et autre structures à l'aide des fonctions put() et get().

Pas besoin d'utiliser EEPROMEx...

Bonsoir à tous, je me permets de déterrer le sujet. En effet je cherche un moyen d’enregistrer un logo créé pour mon lcd 20*4 dans l’EEPROM (Logo qui ne s’affichera qu’à la mise sous tension du système). Donc au total j’ai 80 tableaux de type byte tableau[8] comme on le trouve dans l’exemple Custom Char. C’est une première pour moi de travailler avec l’EEPROM. Donc je vais tester les exemples présentés dans les commentaires. Si vous avez une façon de faire plus simple/mieux, je suis tout ouïe.