mikrocontroller.net

Forum: Compiler & IDEs Variable in Flash-ROM?


Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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[]

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?"

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht 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




Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

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

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist keine Harvard-Architektur.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
float version __attribute__ ((section (".flash")));

int main (void) {

  float wert=10;
  unsigned short *pFlash=(void*)&version;
  unsigned short *pWert=(void*)&wert;
  float ergebnis=0;

  *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA;
  *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass
  *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //WriteCommand
  *pFlash=*pWert;
  
  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.

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
float version __attribute__ ((section (".flash")));

int main (void) {

  float wert = 10.0;
  float *pFlash = &version;
  float ergebnis = 0.0;

  *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA;
  *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass
  *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //WriteCommand

  // schreiben
  *pFlash = Wert;
  
  // lesen
  ergebnis = *pFlash;  

  

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tja, das liegt dann an der spezifischen Funktionsweise Deines Flashs.

Funktionieren sollte dann dies hier:
float version __attribute__ ((section (".flash")));

int main (void) {

  float wert = 10.0;
  unsigned int *pInt;
  unsigned int *pFlash;
  float ergebnis = 0.0;

  pInt = (unsigned int *) &Wert;
  pFlash = (unsigned int *) &version;

  *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA;
  *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass
  *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //WriteCommand

  // schreiben 1
  pFlash[0] = pInt[0];

  *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA;
  *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass
  *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //WriteCommand

  // schreiben 2
  pFlash[1] = pInt[1];

 
  // lesen
  ergebnis = *((float *) pFlash);  

  

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
float wert=10.0;
unsigned short *pFlash=(void*)&version;
unsigned short *pWert=(void*)&wert;
float ergebnis=0.0;
  
  
   *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA;
   *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //UnlockBypass
   *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //Write Command
   *pFlash++=*pWert++;
  
   *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00AA;
   *((unsigned short*)(0xC00000L + 2 * 0x02AA)) = 0x0055; //Unlock Bypass
   *((unsigned short*)(0xC00000L + 2 * 0x0555)) = 0x00A0; //Write Command
   *pFlash=*pWert;
  
   ergebnis=*pFlash;

Das müßte doch dann funktionieren, oder?

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ergebnis = version;

kontrolliert ob die Flash Variable wirklich korrekt gesetzt wurde.


Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan B. wrote:
> Das ist der Clou einer union
> http://www.wachtler.de/ck/8_7_struct_union.html#SE...

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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.