Janiiix3 schrieb
>> Wenn ich der Funktion als zweites Argument z.B "Test" übergebe,
Du übergibst der Funktion nicht "Test", sondern einen Zeiger auf "Test".
Da der Compiler so gut wie möglich optimiert, kopiert er das Argument natürlich nicht ins RAM, sondern setzt an dieser Stelle einen Zeiger auf die Zeichenkette "Test" im Flash ein.
Wenn Du der Funktion also einen unveränderlichen Text übergibst, brauchst Du dich auch nicht zu wundern, wenn bei einem Schreibversuch auf diesen Text irgendetwas unerwartetes passiert...
Das was Du schreibst ist prinzipiell richtig, aber im Fall des AVR GCC falsch, denn solange man einen String explizit nicht als Konstante markiert / Dekoriert, wird der zwar im Flash abgelegt aber beim init (Start des Programmes) dennoch in das RAM kopiert.
(Siehe auch
http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html)
Dazu muss man verstehen, dass der AVR eine sog.
Harvard Architektur ist. Das heisst er hat getrennte Busse für Daten und den Programmbereich.
Beim AVR können im RAM (veränderbar und bei Powerfail flüchtig) nur DATEN liegen, aber
niemals ausführbarer Code!
Dies ist eine ganz bewusste Designentscheidung der AVR Entwickler. Im Flash (nichtflüchtig und nur durch explizites umprogrammieren änderbar) liegen der Programmcode, aber es können da auch
unveränderliche Daten liegen! Letztere nennt sich auch der PGM (Program Memory) Space
Dann gibt es noch das EEPROM, das als nichtflüchtiger Datenspeicher fungieren kann. Aber lassen wir den hier mal außen vor.
Wo was liegt, entscheidet der Compiler und dann letzten Endes legt es der Linker fest.
Freilich liegen konstante Daten (Strings, Tabellen, etc.) erst auch mal im Flash, denn die müssen initial erst mal da sein.
ABER der GCC erzeugt beim AVR einen speziellen init code, der noch vor dem Eintritt von main ausgeführt wird.
Dieser init code kopiert Strings und andere (pseudo) Konstante Daten erst mal ins RAM (= Data Space) und dann wird die CPU darauf ganz einfach zugreifen können.
Das funktioniert immer. Aber es hat teils gravierende Nachteile:
- Man verschwendet redundant wertvollen Speicher im RAM, weil da alle Konstanten hinkopiert werden, die ja eh schon im FLASH liegen.
- Die Daten sind (obschon man sie als const gekennzeichnet hat) nicht wirklich Konstant, weil sie im RAM liegen und damit veränderlich sind.
- Die Initialisierung dauert ein wenig wegen des Kopiervorganges.
Wie bekommt man die Daten nun aber dazu wirklich konstant zu sein?
Nun man
sagt (Dekoriert die Daten) dem GCC beim Definieren der Ressourcen, dass man die ausschließlich im Flash (PGM Space) haben will. Dann werden die beim Start
NICHT ins RAM kopiert!
Gehen wir mal aus, wir wollen die Stings ausschließlich
read only im PGM-Space (Flash) haben:
Dann würde man statt statt "old school"
CodeBox C
const char* const kTestString = "This is a test string";
schreiben:
CodeBox C
#include <avr/pgmspace.h>
...
PROGMEM const char* const kTestString = "This is a test string";
Wir erinnern uns:
Harvard Architektur ->
getrennte Busse für Daten und PGM Space. Und genau da liegen nun nach dem Dekorieren mit
PROGMEM auch die konstanten Daten! Der gcc startup init code wird da dann auch nichts mehr ins RAM kopieren.
Deshalb kann der CPU Kern ohne spezielle Adressierungsmechanismen dann aber auf die Daten auch nicht aus dem FLASH zugreifen (weil wir ja getrennte Busse haben!). Hierzu hat der AVR CPU Kern aber spezielle Befehle, die es ihm erlauben, Daten aus dem PGM Space zu laden, wo ja sonst eigentlich nur Programmcode liegt.
Die avrlibc (die man bei Arduino immer dabei hat) sind im header
avr/pgmspace.h einige Funktionen deklariert, die einen Zugriff auf dauen im PGM Space erlauben, also Daten, die man wie oben gezeigt mit
PGMSPACE gekennzeichnet hat.
Bitte
http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html anschauen!
Beispiel:
CodeBox C
#include <avr/pgmspace.h>
...
PROGMEM const char* const kTestString = "This is a test string";
...schreibt man später im Code...
CodeBox C
// Temp Buffer to copy the constant data from PGM space to.
char ramBuffer[256];
// Copy the data from the PGM (Flash) Space into the RAM.
strcpy_P(ramBuffer, kTestString);
// oder besser - um Buffer Overflow zu vermeiden:
strlcpy_P(ramBuffer, kTestString, sizeof(ramBuffer));
Das Ganze erfordert etwas Übung, aber sich Gedanken zu machen, wo was liegt lohnt sich! Ganz besonders bzgl. der Aspekte Speicherverbrauch im RAM und dem Schutz der konstanten Daten vor Überschreiben.
Gerade janiix3 macht hier wie ich gesehen habe ganz eklatante und gravierende Fehler, die sicher auch damit zu tun haben, dass man erst mal versanden haben muss, wo ein Compiler Daten ablegt - ob diese konstant sind oder nicht... Aber dazu werde ich unten bei janixx3 Code noch mehr schreiben ;-)
In dem Sinne - Happy coding!