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.
Die Werte werden an eine Funktion weiter geben, die einen Pointer auf float erwartet. Also muss ich den Umweg über das RAM gehen, oder?
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?
Das ist der Clou einer union http://www.wachtler.de/ck/8_7_struct_union.html#SECTION00097200000000000000
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?
Nein, bei Harvard-Maschinen wie dem AVR geht das nicht. Da musst Du zwingend die Zugriffsfunktionen aus pgmspace.h verwenden.
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:
1 | float version __attribute__ ((section (".flash"))); |
2 | |
3 | int main (void) { |
4 | |
5 | float wert=10; |
6 | unsigned short *pFlash=(void*)&version; |
7 | unsigned short *pWert=(void*)&wert; |
8 | float ergebnis=0; |
9 | |
10 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA; |
11 | *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass |
12 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //WriteCommand |
13 | *pFlash=*pWert; |
14 | |
15 | ergebnis=*((int*)pFlash) & 0x0000FFFFu; |
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:
1 | float version __attribute__ ((section (".flash"))); |
2 | |
3 | int main (void) { |
4 | |
5 | float wert = 10.0; |
6 | float *pFlash = &version; |
7 | float ergebnis = 0.0; |
8 | |
9 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA; |
10 | *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass |
11 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //WriteCommand |
12 | |
13 | // schreiben
|
14 | *pFlash = Wert; |
15 | |
16 | // lesen
|
17 | ergebnis = *pFlash; |
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.
Tja, das liegt dann an der spezifischen Funktionsweise Deines Flashs. Funktionieren sollte dann dies hier:
1 | float version __attribute__ ((section (".flash"))); |
2 | |
3 | int main (void) { |
4 | |
5 | float wert = 10.0; |
6 | unsigned int *pInt; |
7 | unsigned int *pFlash; |
8 | float ergebnis = 0.0; |
9 | |
10 | pInt = (unsigned int *) &Wert; |
11 | pFlash = (unsigned int *) &version; |
12 | |
13 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA; |
14 | *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass |
15 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //WriteCommand |
16 | |
17 | // schreiben 1
|
18 | pFlash[0] = pInt[0]; |
19 | |
20 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA; |
21 | *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass |
22 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //WriteCommand |
23 | |
24 | // schreiben 2
|
25 | pFlash[1] = pInt[1]; |
26 | |
27 | |
28 | // lesen
|
29 | ergebnis = *((float *) pFlash); |
@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:
1 | float wert=10.0; |
2 | unsigned short *pFlash=(void*)&version; |
3 | unsigned short *pWert=(void*)&wert; |
4 | float ergebnis=0.0; |
5 | |
6 | |
7 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA; |
8 | *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass |
9 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //Write Command |
10 | *pFlash++=*pWert++; |
11 | |
12 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA; |
13 | *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //Unlock Bypass |
14 | *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //Write Command |
15 | *pFlash=*pWert; |
16 | |
17 | ergebnis=*pFlash; |
Das müßte doch dann funktionieren, oder?
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.
ergebnis = version; kontrolliert ob die Flash Variable wirklich korrekt gesetzt wurde.
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.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.