title | categories | subCategories | ||
---|---|---|---|---|
PROGMEM |
|
|
Speichere Daten im Flash-/Programm-Speicher statt im SRAM. Eine Beschreibung der unterschiedlichen Arten von Speicher des Arduinos findest du hier.
PROGMEM
ist ein Variablenmodifikator, welcher nur mit den Datentypen in pgmspace.h
verwendet werden sollte. Es ist eine Anweisung an den Compiler, um die Daten im Flash-/Programm-Speicher statt im SRAM zu speichern.
PROGMEM
gehört zur pgmspace.h-Softwarebibliothek. Diese ist in allen modernen IDE-Versionen
standardmäßig enthalten. Solltest du eine IDE-Version niedriger als 1.0 (von 2011) besitzen, musst du die Bibliothek erst händisch einbinden. Dies funktioniert wie folgt:
#include <avr/pgmspace.h>
const dataType variableName[] PROGMEM = {data0, data1, data3…};`
dataType
- Beliebiger Variablentyp
variableName
- Der Variablenname
PROGMEM
ist ein variabler Modifikator, weshalb die Arduino-IDE alle folgenden synonymen Versionen der Syntax akzeptiert. Durch Experimente wurde allerdings festgestellt,
dass in einigen Versionen der Arduino-IDE (Durch die unterschiedlichen GCC-Versionen) an einigen Stellen funktioniert und an anderen nicht. Die Beispiele unten funktionieren
mit der Arduino IDE Version 13. Frühere Versionen der IDE arbeiten besser, wenn PROGMEM
nach dem Variablennamen eingefügt wird.
const dataType variableName[] PROGMEM = {}; // Benutze dies
const PROGMEM dataType variableName[] = {}; // oder das,
+
`const dataType PROGMEM variableName[] = {}; // aber nicht diese Version!
PROGMEM
kann auch für einzelne Variablen benutzt werden, dies ist aber nur sinnvoll, wenn ein großer Block von Daten gespeichert werden soll. Normalerweise ist das bei Arrays,
Strings (die auch Arrays sind) und weiteren komplizierteren Datenstrukturen der Fall.
Die Benutzung von PROGMEM
erfolgt in 2 Schritten. Nachdem die Daten in den Flash-/Programm-Speicher geladen wurden, müssen spezielle Methoden verwendet werden, um diese Variablen
wieder auszulesen und zu bearbeiten. Diese sind auch in der pgmspace.h-Softwarebibliothek definiert.
Das folgende Code-Fragment zeigt, wie ein unsigned chars (bytes) und ints (2 bytes) in PROGMEM
geschrieben und wieder ausgelesen werden können.
// Speichere einige unsigned ints
const PROGMEM uint16_t charSet[] = { 65000, 32796, 16843, 10, 11234};
// Speichere einen String
const char signMessage[] PROGMEM = { "I AM PREDATOR, UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};
unsigned int displayInt; // Rückgabewert der Funktion zum Auslesen der Daten
char myChar; // Definiere einen Char, um diesen zu bearbeiten
void setup() {
Serial.begin(9600); // Initialisiere den seriellen Port
while (!Serial); // Warte, bis der serielle Port verbunden ist
// Setup code:
// Lies einen 2-Byte-Int
for (byte k = 0; k < 5; k++) {
displayInt = pgm_read_word_near(charSet + k); // Lies den charSet-Wert wieder aus dem Flash-/Programm-Speicher
Serial.println(displayInt); // Gib den gelesenen Wert aus
}
Serial.println(); // Schreibe eine neue Zeile
// Lies einen Char
for (byte k = 0; k < strlen_P(signMessage); k++) {
myChar = pgm_read_byte_near(signMessage + k); // Lies den signMessage-Wert wieder aus dem Flash-/Programm-Speicher
Serial.print(myChar); // Gib den gelesenen Wert aus
}
Serial.println(); // Schreibe eine neue Zeile
}
void loop() {
// ...
}
Array von Strings
Wenn mit einer großen Menge an Text gearbeitet wird (Beispiel: Projekt mit LCD-Display), ist es häufig nötig, Texte in ein Array von Strings zu packen. Dadurch, dass Strings bereits selbst Arrays sind, entsteht dadurch ein 2-dimensionales Array.
Diese großen Datenstrukturen können in den Flash-/Programm-Speicher geladen werden. Das Beispiel unten zeigt das.
/*
PROGMEM string demo
Wie man ein Stringarray in den Flash-/Programm-Speicher lädt und wieder liest.
Für weitere Informationen siehe:
http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
Stringarrays in den Flash-/Programm-Speicher zu laden, ist etwas komplizierter,
das Beispiel ziegt dies jedoch.
*/
#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "String 0"; // Definiere deine Strings "String 0" ist hier nur ein Beispiel
const char string_1[] PROGMEM = "String 1"; // Definiere deine Strings "String 1" ist hier nur ein Beispiel
const char string_2[] PROGMEM = "String 2"; // Definiere deine Strings "String 2" ist hier nur ein Beispiel
const char string_3[] PROGMEM = "String 3"; // Definiere deine Strings "String 3" ist hier nur ein Beispiel
const char string_4[] PROGMEM = "String 4"; // Definiere deine Strings "String 4" ist hier nur ein Beispiel
const char string_5[] PROGMEM = "String 5"; // Definiere deine Strings "String 5" ist hier nur ein Beispiel
// Initialisiere die Tabelle von Strings
const char* const string_table[] PROGMEM = { string_0, string_1, string_2, string_3, string_4, string_5 };
char buffer[30]; // Der Buffer zum Lesen der Daten muss so lang sein wie der längste String!
void setup() {
Serial.begin(9600); // Initialisiere den seriellen Port
while(!Serial); // Warten, bis der serielle Port verfügbar ist
Serial.println("OK"); // Schreibe "OK" auf den seriellen Port
}
void loop() {
/* Spezielle Funktionen zum Lesen der Daten sind nötig. "strcpy_P" kopiert den String vom
Flash-/Programm-Speicher in dem SRAM/ den Buffer. Stelle sicher, dass der empfangende
String lang genug ist für den String.
*/
// Iteriere über das Array: 6-mal, da 6 Strings im Array sind
for (int i = 0; i < 6; i++) {
strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Casts und Dereferenzierung des Speichers
Serial.println(buffer); // Gib den gelesenen Wert aus
delay(500); // Warte eine halbe Sekunde
}
}
Bitte beachte, dass die Variablen entweder global definiert oder als static
definiert sein müssen, um mit PROGMEM
zu funktionieren.
Der folgende Code wird nicht funktionieren, wenn er in einer Funktion ausgeführt wird:
const char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n";
Der folgende Code wird funktionieren, wenn er in einer Funktion ausgeführt wird:
const static char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n"
=== Das F()
-Makro
Wenn eine Instruktion wie
Serial.print("Write something on the Serial Monitor");
benutzt wird, wird der String bevor er auf den seriellen Ausgang geschrieben wird, normalerweise in den RAM gespeichert. Der RAM kann aber sehr leicht volllaufen, wenn der Code sehr viel auf den seriellen Port schreibt. Wenn du noch freien Flash-/Programm-Speicher hast, kannst du dem Compiler sagen, dass er die Werte in den Flash-/Programm-Speicher schreiben soll:
Serial.print(F("Write something on the Serial Monitor that is stored in FLASH"));