dimanche 23 juin 2019

Arduino progmem : Différence entre UNO et SAMD21

Puisque la RAM est si limitée sur un Arduino, il est important de l'utiliser à bon escient et surtout utiliser toute technique qui permet de tout simplement éviter de l'utiliser tout simplement. Un Arduino Uno utilisant un microcontroleur ATMEGA 328P n'a que 2 KByte de RAM mais 32 KBytes de mémoire Flash. Tout le programme compilé réside en Flash et bien qu'il soit possible d'avoir tellement de code en utilisant plein de librairie pour remplir ce 32KByte, la plupart du temps c'est pas mal vide là-dedans.

Il est possible d'ajouter des données dans cette espace Flash tant qu'elles sont utilisées qu'en lecture seule. Cependant, il faut ramener en RAM octet par octet pour en lire le contenue. La directive progmem indique au compilateur d'ajouter une variable en mémoire Flash et non en RAM.

Ex:

const PROGMEM char str_flash[] = "Je suis en memoire FLASH!";

 Ici, les 26 octets utilisés par cette chaine de caractères résident dans l'espace mémoire Flash et non en RAM, alors que:

const char str_flash[] = "Je suis en memoire FLASH!";

Réduit de 26 octets la RAM disponible et ce pour toute la durée d'exécution du programme.
Le compilateur inclus déjà une macro F() pour simplifier cette technique et les classe dérivant de Print tel de Serial permettent de passer en paramètre un pointeur vers un chaine de caractère en progmem.

Ex:

Serial.println(F("Je suis en memoire FLASH!"));

Il est a noter que le compilateur ne semble pas détecter que deux chaines identiques pourraient être stockées qu'une seule fois en mémoire, donc:

Serial.println(F("Je suis en memoire FLASH!"));
Serial.println(F("Je suis en memoire FLASH!"));

Utilise 52 octets en mémoire Flash puisque la chaine est stocké en deux endroits séparés.

Pour ce qui est d'accéder directement à une donnée, il est nécessaire d'utiliser les méthodes  tel que pgm_read_byte_near et pgm_read_word_near. Ces méthodes permettent de ramener en RAM un type de données sotcké en Flash. L'ensemble des méthodes disponibles est défini dans le fichier , qui est en passant nécessaire d'ajouter à votre programme. Puisse que ces méthodes nécessitent un pointeur vers la données, prenons par exemple:

const PROGMEM byte  untableudebyte[] = { 1, 2, 3, 4, 5 ,6 ,7, 8};

Afin de lire le 3e élément, il faut augmenter le pointeur de 2 et demander une lecture d'un seul octet à cette adresse:

byte byteEnRAM = pgm_read_byte_near(untableudebyte + 2);

la valeur de byteEnRAM sera maintennt de 3. Évidemment qu'il faut être à l'aise avec l'arithmétique de pointeur, ce qui est un sujet en soit. Mais si vous avez de long texte, de grosses tables de valeurs pré-calculées ou tout autre blob binaire devant être fourni tel qu'une image pour un écran, c'est vraiment pratique.

Ceci dit, toute cette mécanique n'est pas nécessaire sur les Arduino à base d'architecture ARM. Dès qu'une variable est déclarée const, elle est automatiquement stockée seulement qu'en Flash et l'adressage permet l'accès direct, donc pas de pgm_read_byte_near nécessaire. Si vous écrivez une librairie, il est possible de mettre le code selon différente architecture dans des sous-dossier, par contre si vous utilisez cette technique dans le .ino, il est nécessaire d'utiliser des directive du préprocesseur tel que #idef __AVR_ATmega328P__ pour identifier le genre d'architecture et inclure le code compatible.

Tout ce petit volet sur l'utilisation est utile pour la sérialisation des behavior tree. Ce sera le sujet du prochain post, avec un peu plus d'exemples concrets cette fois. 

Aucun commentaire:

Enregistrer un commentaire