Forum: Compiler & IDEs Variable in Flash-ROM?


von TechInfo (Gast)


Lesenswert?

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

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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.

von TechInfo (Gast)


Lesenswert?

Die Werte werden an eine Funktion weiter geben, die einen Pointer auf 
float erwartet. Also muss ich den Umweg über das RAM gehen, oder?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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...

von TechInfo (Gast)


Lesenswert?

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[]

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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?"

von TechInfo (Gast)


Lesenswert?

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.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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.

von TechInfo (Gast)


Lesenswert?

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?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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.

von Alois (Gast)


Lesenswert?

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?

von Stefan B. (stefan) Benutzerseite


Lesenswert?


von TechInfo (Gast)


Lesenswert?

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?

von Klaus F. (kfalser)


Lesenswert?

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




von TechInfo (Gast)


Lesenswert?

@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?

von TechInfo (Gast)


Lesenswert?

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?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Nein, bei Harvard-Maschinen wie dem AVR geht das nicht.
Da musst Du zwingend die Zugriffsfunktionen aus pgmspace.h verwenden.

von TechInfo (Gast)


Lesenswert?

Ist keine Harvard-Architektur.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von TechInfo (Gast)


Lesenswert?

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.

von Klaus F. (kfalser)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> 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;

  

von Klaus F. (kfalser)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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);

  

von TechInfo (Gast)


Lesenswert?

@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?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Klaus F. (kfalser)


Lesenswert?

ergebnis = version;

kontrolliert ob die Flash Variable wirklich korrekt gesetzt wurde.


von TechInfo (Gast)


Lesenswert?

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.

von Simon K. (simon) Benutzerseite


Lesenswert?

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
Noch kein Account? Hier anmelden.