Forum: Mikrocontroller und Digitale Elektronik C-Variablen aus I2C EEPROM "booten". Wie deklarieren


von Hanns-Jürgen M. (yogy)


Lesenswert?

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.

von Bimbo. (Gast)


Lesenswert?

Ich hole Popcorn.

von Walter T. (nicolas)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Hanns-Jürgen M. (yogy)


Lesenswert?

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.

von Andre (Gast)


Lesenswert?

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.

von Hanns-Jürgen M. (yogy)


Lesenswert?

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.

von Karl M. (Gast)


Lesenswert?

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"

von Hanns-Jürgen M. (yogy)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?


von e^-j*pi (Gast)


Lesenswert?

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.

von c or c++ (Gast)


Lesenswert?

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

von Einer K. (Gast)


Lesenswert?

Ein toller Hecht, du bist!
Der Code ist allerdings leider untestbar und damit unbrauchbar.

von foobar (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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