Hallo,
es geht mal wieder um das Thema Flash-ROM.
Ich habe zur Zeit im RAM ein Array vom Typ float definiert.
Nun möchte ich die Werte dieses Arrays in einem externen Flash
unterbringen (nicht Teil des Controllers! Es handelt sich auch nicht um
einen AVR). Ich könnte jetzt eine Section im Linkerskript erstellen,
dass auf den Speicherbereich des Flash verweist, und das Array in dieser
Section definieren. Der Flash-Baustein kann aber ja nicht als "normaler"
Speicherbereich angesprochen werden, da zum Beschreiben zunächst
verschiedene Kommandos an bestimmte Adressen geschrieben werden müssen.
Sehe ich das richtig, dass ich somit eine Variable oder ein Array nicht
innerhalb des Flash-Speicherbereichs definieren kann? Dann müßte ich
"manuell" die gewünschten Werte in das Flash schreiben und beim Startup
in das besagte Array kopieren.
Gruß
TechInfo
Korrekt. Wobei man optimieren kann, wenn man weiss wozu die Werte im
Flash gebraucht werden. Wenn es z.B. eine Tabelle mit festen Werten ist,
kann man sich das Zurückkopieren ins RAM ggf. sparen.
Wenn die Funktion nur lesend auf das Ziel des Pointers zugreift
(Stichwort: const float * im Prototyp), kann man über eine "Verzicht auf
RAM" Optimierung nachdenken. Wenn Schreibzugriff stattfindet natürlich
nicht.
Ich habe im Hinterkopf, dass da bei deinem
Mikrocontroller/Flashcontroller/Flash-ROM mal was war mit 8, 16 und
32-Bit Zugriffen.
Durch die Einschränkungen beim Flashzugriff (Alignment) kann es
erforderlich sein, dass man den Pointerzugriff an sich nochmals
aufbereiten muss (per Funktion oder Makro).
Wie gross ist float auf deinem Target (sizeof(float))? Oft ist float 4
Bytes (32-Bit) gross und wenn du die direkt aus dem Flash auslesen
könntest, d.h. wenn ein 32-Bit Zugriff klappt...
float ist 32 Bit groß und du erinnerst dich richtig, der FLash-Zugriff
lief bei mir nur über short (16 Bit).
Habe nochmal nachgeschaut, die Fkt. erwartet keinen Pointer sondern ein
Array:
float xx[]
In diesem Zusammenhang ist float xx[] und float *xx äquivalent.
Siehe http://www.dclc-faq.de/kap2.htm Frage 2.4 "Warum sind dann Array-
und Zeigerdeklarartionen als formale Parameter einer Funktion
austauschbar?"
Schon klar.
Wollte damit nur sagen dass der Paramter nicht const ist.
Was sollte ich jetzt Deiner Meinung nach machen?
Wenn ich eine Section im Linkerskript erstelle, und dann ein Array in
dieser Section definiere, macht das der Compiler dann überhaupt in einem
Nur-Lese-Speicher? Und wie initialisiere ich das Array? Das kann ich bei
der Definition ja nicht machen, also brauche ich wohl eine Routine
dafür.
Genau so würde ich auch vorgehen.
Ich würde eine Initialisierungsroutine nehmen, die die Werte berechnet
oder per Übertragung vom PC holt und einmalig ins Flash schreibt.
Entweder einen Wert nach dem anderen oder alle zusammen über den Umweg
eines temporären Arrays im RAM. Das derart gefüllte Flash könnte man
sogar weiterverwenden, wenn sich das Programm im Controller ändert.
Im eigentlichen Programm oder Routinen liest man die Werte direkt aus
dem Flash. Wenn man sowieso mit eigenen Makros/Funktionen lesen muss
(wg. 16-Bitzugriff und Zusammensetzen zweier Zugriffe/Werte zum 32-bit?
Float), ist eine entsprechende Sektion im Linkerskript nicht dringend
notwendig. Man kann dann einfach über direkte Adressen gehen statt über
symbolische Namen/Sektionen.
Danke.
Also, ich definiere keine Variable im Flash, sondern tippe meine
verwendeten Adressen direkt ein, wenn ich auf die Werte zugreifen will.
Wie kann ich denn nun einen Float-Wert im Flash abspeichern, wenn ich
keine Variable definiere? Wie setze ich die Werte zusammen?
Meinst du mit Variable das ganze Array? Ganz ohne Variable wird es IMHO
nicht gehen. Ich denke, man braucht ohne Array im RAM mindestens eine
Variable mit dem Platz für ein Arrayelement im RAM.
Beim Zusammensetzen würde ich bei dieser Variable mit einer union
arbeitet, in der der Platz für ein float und der Platz für die beiden
unsigned shorts (als struct organisiert) übereinander liegen (wenn auf
deinem Target sizeof(float)= 2*sizeof(unsigned short)). Vom Programm aus
dann das float beschreiben und die beiden unsigned shorts dann ins Flash
wegschreiben bzw. umgekehrt beim Lesen.
Aha, interessanter Gedanke.
Aber wie mache ich aus einer Float-Zahl zwei unsigned short Werte?
Fließkomma-Zahlen werden doch ganz anders gespeichert als Integer.
Trenne ich die oberen von den unteren 16 Bit? Und entspricht das dann
bei Rekombination wieder dem Float-Wert?
Wie ein Union funktioniert ist mir klar.
Aber nur weil der gleiche Speicherplatz benutzt wird, heißt dass ja
nicht dass die float-Werte 1:1 durch die beiden unsigned short-Werte
dargestellt werden. Die Variablen im union können ja jeden x-beliebigen
Wert, unabhängig voneinander besitzen.
Wenn ich der float-Variable im union den Wert 23.5 zuweise, wie müßte
dann der Wert der beiden short-Variablen sein?
Man brauchst keine unions, das ganze haben wir doch schon durchgekaut.
Beitrag "Re: ext. Flash-ROM in C beschreiben"
Wenn dein Float-Array im Flash Bereich und der Flashbereich ein Teil des
Adressraums ist, dann kann die CPU ganz normal lesend darauf zugreifen.
Die Tatsache, daß auf den Speicherbereich hardwaremäßg nur mit 16 Bit
zugegriffen wird, aber ein 32 Bit Zugriff benötigt oder verwendet wird,
wird von der Hardware TRANSPARENT und UNSICHTBAR erledigt.
Ein Pointer auf das Feld ist also ein ganz normaler Pointer und kann von
jeder Funktion verwendet werden. Wenn Du den Pointer noch mit const
kennzeichnest, dann teilst Du dem Compiler nur noch mit, daß der
Speicherbereich nicht geändet werden darf (kann). Wenn irgend ein
Programmteil versucht das Array zu beschreiben, dann bekommt man schon
beim compilieren einen Fehler.
Zum Initialisieren des Feldes hat (schreibt) man einen Funktion, welche
einen vorgegebenen Speicherbereich mit einer gewissen Länge in das Flash
kopiert.
Dieses Kopieren erfolgt jedesmal, wenn sich die Werte im Flash-Array
ändern sollen. Ein Kopieren jedesmal nach dem Einschalten ist nicht
notwendig, sogar schädlich, weil das Flash schon die Werte enthält und
das Flash nur einen bestimmte Anzahl von Programmierzyklen aushält.
Man braucht also eine Funktion :
write_flash (void *pFlash, const void *Quelle, int len);
Diese Funktion kopiert den Speicher vom Pointer Quelle ins Flash an die
Stelle pFlash im Flash über die Länge len. len wird in Byte angegeben.
Zum Initialisieren des Array im Flash ruft man diese Funktion auf :
write_flash((void *) &FloatArray, &InitFloatArray, sizeof(FloatArray));
InitFloatArray ist im RAM, kann deshalb mit Benutzerdaten beliebig
gefüllt werden. InitFloatArray hat die selbe größe wie FloatArray und
dessen Werte werden nach FloatArray kopiert.
Innerhalb der Funktion write_flash werden die Pointer als Zeiger auf 16
Bit Werte umgecastet.
write_flash (void *pFlash, const void *Quelle, int len)
{
uint16_t *pTarget = pFlash;
uint16_t *pSource = pQuelle;
for (int i = 0; i < len/2; i++) {
write_single_location(pTarget++, pSource++);
}
}
Klaus
@Klaus
Du hast recht, ich hatte an die von dir gepostete Funktion gar nicht
mehr gedacht. Bei deinem Beispiel ist es aber zwingend erforderlich,
eine Section im Linkerskript zu erstellen, und die Variable dann in
dieser Section zu definieren, oder?
Und noch was:
Wenn ich ein Array
float werte[10];
im Flash definiere über eine Section, kann ich dann nicht einfach mit
werte[i] darauf (lesend) zugreifen?
Kein AVR? Oh, sorry, das hatte ich angenommen, da es hier kaum um was
anderes geht ...
Wenn es eine von-Neumann-Maschine (also beispielsweise ein MSP430) ist,
dann kannst Du in der Tat genauso wie angenommen lesend auf Deine
"variable" zugreifen. Selbstverständlich.
Ich habe jetzt folgendes ausprobiert. Dabei orientiere ich mich an
Klaus' Beispiel, allerdings benutze ich zunächst eine Variable und kein
Array, benötige also keine Schleife:
Im Flash befindet sich danach der Wert 10.062499 in Float-Darstellung
bzw. 0x4120 in 16-Bit-Darstellung.
Die Variable ergebnis enthält ebenfalls 0x4120. Wie kann ich erreichen,
dass in ergebnis der Wert als "echtes" float abgespeichert wird?
Zu beachten ist auch, dass der float-Wert im Flash eigentlich zu ungenau
ist.
O Mann, o Mann, Du stehts wirklich ein bischen auf der Leitung.
Dein float braucht 32 bit, also 4 Byte.
Deine Schreiboperation schreibt aber nur 2 Byte ( = short), also nur die
Hälfte von Float, deshalb braucht es ja die Schleife. Die Schleife in
meinem Beispiel zählt nämlich shorts, also 2-Byte Blöcke.
> Im Flash befindet sich danach der Wert 10.062499 in> Float-Darstellung bzw. 0x4120 in 16-Bit-Darstellung.
Äh, die 16-Bit-Darstellung ist nicht in Ordnung.
sizeof (float) != 2
Ich weiß nun nicht, wie sich Dein spezifisches Flash-Rom verhält, rein
prinzipiell sollte das aber so funktionieren:
Nein, das ist falsch.
Nach der Unlock Sequenz werden nur 16 Bit vom Flash aktzeptiert.
Die CPU schreibt das Float (32 Bit), diese werden von der Hardware in 2
16 Bit Zugriffe aufgespalten. Der erste davon löst eine Änderung des
Flashs aus, der 2. Zugriff bewirkt nichts.
Es wird also wiederum nur die Hälfte des float Wertes im Flash
gespeichert.
@Klaus
Aaaah, so langsam komm ich dahinter, vielen Dank. Ich stand wirklich auf
dem Schlauch. Na klar, für einen 32-Bit Wert muss ich ja zwei
Schreiboperationen machen:
Bis auf die letzte Zeile funktionierts. Aber eben die letzte Zeile
funktioniert nicht, denn pFlash zeigt ja jetzt um sizeof (unsigned int)
daneben.
Du musst also pFlash wieder dekrementieren.
Sieht gut aus, an der Speicherstelle im Flash steht nun in der 1.
Adresse 0x4120 und an der darauffolgenden 0x00c0. In der Float-Ansicht
des Debuggers erscheint 10.0001831.
Das Auslesen von version in ergebnis ergibt aber irgendeinen
Zufallswert. Möglich, dass das mit einem schon vorher vorhandenen Fehler
zusammenhängt.
Stefan B. wrote:
> Das ist der Clou einer union> http://www.wachtler.de/ck/8_7_struct_union.html#SECTION00097200000000000000
Btw, das ist übrigens nicht der Clou einer Union. In deinem Artikel dort
steht, dass der Programmierer darauf achten muss, dass in die Union über
den gleichen Weg gelesen wird, wie geschrieben. Und ich meine mich
erinnern zu können, dass auch nur dieser Weg in irgendwelchen Normen für
C-Compiler definiert sein muss. Der Rest ist Plattform oder
Compilerabhängig.
Wenn man aber über Float hereinschreibt und über Short herausliest, ist
das nicht mehr Regelkonform.