C-Variablen aus I2C EEPROM "booten". Wie deklarieren

#5788546
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.
#5788638
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.
#5788862
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.
#5788988
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.
Gast #5789036
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"
#5789061
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.
Gast #5791336
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
Gast #5791419
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
...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren