Hallo zusammen, folgende Aufgabe: Ich will Variablen beim Systemstart (oder bei bedarf) aus dem externen I2C-Bus EEPROM "booten", also die werte initialisieren. Wie ich das EEPROM beschreibe und lese ist mir bekannt. Das funktioniert auch. Nur ich will die EEPROM-Variablen, oder besser deren Adresse im EEPROM, definieren. Funktionell soll das so aussehen: Lese Variable EEP_Var_1 aus dem EEPROM und speichere sie als Var_1 im Arbeitsspeicher. Var_2 entsprechend. Arbeitsspeicher-deklaration z.B.: U16 Var_1, Var_2; Nur, wie mache ich das idealerweise für das EEPROM: Ein Möglichkeit wäre: Ich lege das Variable für Variable getrennt fest, z.B. #define EEP_Var_1 0X200 //Lege die Adressse von EEP_Var_1 im Eeprom auf 0x200 #define EEP_VaR_2 0X202 //Lege die Adressse von EEP_Var_2 im Eeprom auf 0x202 Das scheint mir ziemlich umständlich. Gibt es da eine einfachere Methode? Ähnlich wie: struct EEP { U16 EEP_Var_1; U16 EEP_Var_2; }; Leider landet eine Struct-Declaration irgendwo im RAM... An enum habe ich auch gedacht, aber enum kann nicht automatisch mit 8,16 oder 32 Bitbreite umgehen, das müßte dann wieder manuell geschehen.. Ach ja, ich verwende Standard-C Danke für jeden Tip.
Hanns-Jürgen M. schrieb: > Nur, wie mache ich das idealerweise für das EEPROM: Bau ein großes Struct. Pack da alles rein, was ins EEPROM gehört. Schreib es am Stück ins EEPROM. Lies es am Stück aus dem EEPROM. -> Profit.
Hanns-Jürgen M. schrieb: > Nur ich will die EEPROM-Variablen, oder besser deren Adresse im > EEPROM, definieren. Das brauchst Du nicht, denn der Compiler kann eh nicht direkt darauf zugreifen. Und sollte es auch nicht, wegen der begrenzten Schreibzyklen. Du definierst im RAM ein Struct mit allen Variablen, die im EEPROM überleben sollen. Und dann hast Du 2 Kopierroutinen. Die eine kopiert vom EEPROM in den SRAM, dazu brauchst Du ihr nur einen Pointer auf die Struct und die Startadresse im EEPROM zu übergeben, sowie die Größe der Struct in Byte (sizeof(struct)). Und die andere kopiert vom RAM in den EEPROM. Entweder es gibt schon eine eeprom.h, die beide Routinen verfügbar macht oder Du schreibst sie selber.
Walter T. schrieb: > Hanns-Jürgen M. schrieb: >> Nur, wie mache ich das idealerweise für das EEPROM: > > Bau ein großes Struct. Pack da alles rein, was ins EEPROM gehört. > Schreib es am Stück ins EEPROM. Lies es am Stück aus dem EEPROM. -> > Profit. Peter D. schrieb: > Hanns-Jürgen M. schrieb: >> Nur ich will die EEPROM-Variablen, oder besser deren Adresse im >> EEPROM, definieren. > > Das brauchst Du nicht, denn der Compiler kann eh nicht direkt darauf > zugreifen. Und sollte es auch nicht, wegen der begrenzten Schreibzyklen. > > Du definierst im RAM ein Struct mit allen Variablen, die im EEPROM > überleben sollen. > Und dann hast Du 2 Kopierroutinen. Die eine kopiert vom EEPROM in den > SRAM, dazu brauchst Du ihr nur einen Pointer auf die Struct und die > Startadresse im EEPROM zu übergeben, sowie die Größe der Struct in Byte > (sizeof(struct)). > Und die andere kopiert vom RAM in den EEPROM. > Entweder es gibt schon eine eeprom.h, die beide Routinen verfügbar macht > oder Du schreibst sie selber. Ja danke, ich glaube, so werde ich es machen.
Richtig schön wird es, wenn die Eeprom.h noch eine CRC über die Daten berechnet und ggf. einen Fehler wirft. Je nachdem wie häufig geschrieben wird ist auch ein simples wear-leveling interessant, Atmel hatte dazu eine ganz gute Appnote.
Andre schrieb: > Richtig schön wird es, wenn die Eeprom.h noch eine CRC über die Daten > berechnet und ggf. einen Fehler wirft. Je nachdem wie häufig geschrieben > wird ist auch ein simples wear-leveling interessant, Atmel hatte dazu > eine ganz gute Appnote. Die vorgänger SW habe ich 1992 für einen 68HC11F1 mit internem EEPROM geschrieben.Sie beinhaltete ein Controlbyte, um festzustellen, ob das EEPROM überhaupt beschrieben ist, und eine Checksum über alle Daten. Das ist hier natürlich auch vorgesehen, vlt. nutze ich eine CRC8. Die Zugriffsroutinen (schreiben und lesen von bytes, words oder longwords sowie von Blöcken über I2C) habe ich schon länger fertig.
Noch ein wichtiger Hinweis für die AVR-Welt, man muss das BOD Fusebit für die verwendete Spannungsversorgung einschalten! Peter (peda) hat für den EEpromzugriff eine Block Kopierroutinen geschrieben. [1] https://www.avrfreaks.net/forum/tut-c-smart-eeprom-usage?name=PNphpBB2&file=viewtopic&t=91306 [2] Beitrag "Re: eeprom int32_t lesen/schreiben"
Karl M. schrieb: > Noch ein wichtiger Hinweis für die AVR-Welt, man muss das BOD Fusebit > für die verwendete Spannungsversorgung einschalten! > > Peter (peda) hat für den EEpromzugriff eine Block Kopierroutinen > geschrieben. > > [1] > https://www.avrfreaks.net/forum/tut-c-smart-eeprom-usage?name=PNphpBB2&file=viewtopic&t=91306 > > [2] Beitrag "Re: eeprom int32_t lesen/schreiben" ja, danke für den Link. BTW: Bei mir geht es nicht um einen AVR mit internem EEPROM sondern um einen SAM3X8 mit extern über I2C angebundenen 24C256 EEPROM.
Der Vollständigkeit halber: Zwar ist ein "memcpy" einer struct am einfachsten und schnellsten, jedoch ist Padding und Alignment zu bedenken. Es kann es auch fummelig werden, da das Hinzufügen od. Datentypänderungen dazu führen kann, dass die Version mit Geräten im Feld nicht mehr genutzt werden kann.
Alles in ein struct ist die einfachste Lösung, hat aber auch den Nachteil, dass alles in einem struct liegen muss. Alternativ ein Array in dem man die Addressen für verschiedene Variablen vorhält und dann einzeln laden kann. Ist aber fehleranfälliger. Ich hatte mir mal was in C++ gebaut, dass automatisch offsets für Variablen im Eeprom berechnet und automatisch Objekte anlegt, mit denen typsicher Variablen gelesen/geschrieben werden können. Für jede Variable wird sizeof(typ) + 1 allokiert, eine checksum wird mit abgespeichert.
1 | namespace storage { |
2 | STORAGE_VAR(bool, boolVar1, false); //typ, name, default wert falls checksum nicht passt |
3 | STORAGE_VAR(int, intVar1, 11); |
4 | STORAGE_ARRAY(char, serial, "helloworld", 10); //typ, name, default, länge |
5 | }
|
Dann Benutzung über:
1 | bool boolVar1; |
2 | int intVar1; |
3 | char serial[11]; |
4 | bool readOk = storage::boolVar1.read(boolVar1); |
5 | storage::boolVar1.write(boolVar1); |
6 | storage::intVar1.read(intVar1); |
7 | storage::serial.read(serial); // does not compile, array size does not match |
Ein toller Hecht, du bist! Der Code ist allerdings leider untestbar und damit unbrauchbar.
>> Du definierst im RAM ein Struct mit allen Variablen, die im EEPROM >> überleben sollen. > > Ja danke, ich glaube, so werde ich es machen. Hab ich zwar nicht ausprobiert, aber es sollte auch möglich sein, alle Variablen statt in eine struct in ein extra Segment zu packen[1] . Im Linkerscript wird für das Segment eine passende Start- und End-Referenz erzeugt. Der gesamte Block kann dann per Helferroutine in einem Rutsch geladen und gespeichert werden. Gibt bestimmt Leute, die das schon gemacht haben und passende Linker-Script-Fragmente liefern können ... [1] z.B.:
1 | #define EESAVE __attribute__ ((section ("eesave")))
|
2 | extern uint8_t __eesave_start[], __eesave_end[]; |
3 | int x EESAVE = 0; |
4 | struct foo y EESAVE = { ... }; |
5 | ...
|
6 | eewriteblock(promt_addr, __eesave_start, __eesave_end - __eesave_start) |
7 | ...
|
8 | eewreadblock(promt_addr, __eesave_start, __eesave_end - __eesave_start) |
9 | ...
|
Wichtig ist, wie die Variablen im RAM liegen. Wenn die da lückend sind, wird die ganze Sache nur unnötig kompliziert. Denn dann kann man sie nicht mehr mit Blockroutinen sichern und restoren. C++ mag das zwar verstecken können, muß aber trotzdem komplizierten Code erzeugen. Man hat also nichts damit gewonnen.
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.