J'ai des difficultés à comprendre la gestion de la mémoire.
La documentation Arduino dit, il est possible de garder des constantes comme des chaînes ou tout ce que je ne veux pas changer pendant l'exécution dans la mémoire du programme. Je pense qu'il est intégré quelque part dans le segment de code, ce qui doit être assez possible dans une architecture von-Neumann. Je veux en profiter pour rendre mon menu d'interface utilisateur sur un écran LCD possible.
Mais je suis déconcerté par ces instructions pour simplement lire et imprimer les données de la mémoire du programme:
strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
Serial.println( buffer );
Pourquoi diable dois-je copier le fichu contenu sur la RAM avant d'y accéder? Et si c'est vrai, qu'advient-il alors de tout le code? Est-il également chargé dans la RAM avant exécution? Comment est alors géré le code (32 ko) avec seulement 2 ko de RAM? Où sont ces petits gobelins portant des disquettes?
Et encore plus intéressant: qu'arrive-t-il aux constantes littérales comme dans cette expression:
a = 5*(10+7)
les 5, 10 et 7 sont-ils vraiment copiés dans la RAM avant de les charger dans les registres? Je ne peux pas croire ça.
la source
string_table
tableau. Ce tableau peut être de 20 Ko et ne tient jamais en mémoire (même temporairement). Vous ne pouvez cependant charger qu'un seul index en utilisant la méthode ci-dessus.Réponses:
L'AVR est une famille d' architecture Harvard modifiée , donc le code est stocké en flash uniquement, tandis que les données existent principalement dans la RAM lors de leur manipulation.
Dans cet esprit, répondons à vos questions.
Vous n'avez pas besoin de le faire en soi, mais par défaut, le code suppose que les données sont dans la RAM sauf si le code est modifié pour le rechercher spécifiquement en flash (comme avec
strcpy_P()
).Nan. Architecture de Harvard. Voir la page Wikipedia pour tous les détails.
Le préambule généré par le compilateur copie les données qui doivent être modifiables / modifiées dans SRAM avant d'exécuter le programme réel.
Je ne sais pas. Mais si vous les voyez, je ne peux rien faire pour vous aider.
Non. Le compilateur évalue l'expression au moment de la compilation. Tout ce qui se passe dépend des lignes de code qui l'entourent.
la source
const uint8_t test1[5]= { 0x54, 0x65, 0x73, 0x74, 0x31 }; const uint8_t bla[9]= { 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x62 }; const uint8_t Menu[4]= { 0x3d, 0x65, 0x6e, 0x75};
comment amener ces données à flasher et plus tard dans la fonction SPI.transfer (), qui prend un uint8_t par appel.Voici comment
Print::print
imprime à partir de la mémoire du programme dans la bibliothèque Arduino:__FlashStringHelper*
est une classe vide qui permet aux fonctions surchargées comme print de différencier un pointeur pour programmer la mémoire d'une à la mémoire normale, car les deux sont vues commeconst char*
par le compilateur (voir /programming/16597437/arduino-f- qu'est-ce-qu'il-fait-réellement )Vous pouvez donc surcharger la
print
fonction de votre écran LCD pour qu'elle prenne un__FlashStringHelper*
argument, l'appelleLCD::print
, puis utiliselcd.print(F("this is a string in progmem"));' to call it.
F () `est une macro qui garantit que la chaîne est dans la mémoire du programme.Pour prédéfinir la chaîne (pour être compatible avec l'impression Arduino intégrée), j'ai utilisé:
Je pense qu'une alternative serait quelque chose comme
ce qui éviterait le
__FlashStringHelper
casting.la source
Toutes les constantes sont initialement dans la mémoire du programme. Où seraient-ils ailleurs lorsque le courant est coupé?
C'est en fait l'architecture de Harvard .
Non. En fait, il existe une instruction matérielle (LPM - Load Program Memory) qui déplace les données directement de la mémoire du programme dans un registre.
J'ai un exemple de cette technique en sortie Arduino Uno sur un moniteur VGA . Dans ce code, une police bitmap est stockée dans la mémoire du programme. Il est lu à partir de cela à la volée et copié dans la sortie comme ceci:
Un démontage de ces lignes montre (en partie):
Vous pouvez voir qu'un octet de mémoire de programme a été copié dans R30, puis immédiatement stocké dans le registre USART UDR0. Aucune RAM impliquée.
Il existe cependant une complexité. Pour les chaînes normales, le compilateur s'attend à trouver des données dans la RAM et non PROGMEM. Ce sont des espaces d'adressage différents, et donc 0x200 dans la RAM est quelque chose de différent de 0x200 dans PROGMEM. Ainsi, le compilateur se donne la peine de copier des constantes (comme des chaînes) dans la RAM au démarrage du programme, il n'a donc pas à se soucier de connaître la différence plus tard.
Bonne question. Vous ne vous en sortirez pas avec plus de 2 Ko de chaînes constantes, car il n'y aura pas de place pour les copier toutes.
C'est pourquoi les gens qui écrivent des choses comme des menus et d'autres trucs verbeux, prennent des mesures supplémentaires pour donner aux chaînes l'attribut PROGMEM, ce qui les désactive d'être copiées dans la RAM.
Si vous ajoutez l'attribut PROGMEM, vous devez prendre des mesures pour informer le compilateur que ces chaînes se trouvent dans un espace d'adressage différent. Faire une copie complète (temporaire) est un moyen. Ou imprimez simplement directement à partir de PROGMEM, un octet à la fois. Un exemple de cela est:
Si vous transmettez à cette fonction un pointeur sur une chaîne dans PROGMEM, elle effectue la "lecture spéciale" (pgm_read_byte) pour extraire les données de PROGMEM plutôt que de RAM et les imprime. Notez que cela prend un cycle d'horloge supplémentaire, par octet.
Non, car ce n'est pas obligatoire. Cela se compilerait en une instruction "charger le littéral dans le registre". Cette instruction est déjà dans PROGMEM, donc le littéral est maintenant traité. Pas besoin de le copier dans la RAM puis de le relire.
J'ai une longue description de ces choses sur la page Mettre des données constantes dans la mémoire du programme (PROGMEM) . Cela a un exemple de code pour configurer des chaînes et des tableaux de chaînes, assez facilement.
Il mentionne également la macro F () qui est un moyen simple d'imprimer simplement à partir de PROGMEM:
Un peu de complexité du préprocesseur permet de compiler dans une fonction d'assistance qui extrait les octets de la chaîne de PROGMEM un octet à la fois. Aucune utilisation intermédiaire de RAM n'est requise.
Il est assez facile d'utiliser cette technique pour des choses autres que série (par exemple votre écran LCD) en dérivant l'impression LCD de la classe d'impression.
Par exemple, dans l'une des bibliothèques LCD que j'ai écrites, j'ai fait exactement cela:
Le point clé ici est de dériver de Print et de remplacer la fonction "écriture". Maintenant, votre fonction remplacée fait tout ce dont elle a besoin pour sortir un caractère. Puisqu'il est dérivé de Print, vous pouvez maintenant utiliser la macro F (). par exemple.
la source