Forum: Compiler & IDEs internes EEProm als Einstellungs-speicher - wo liegt der Fehler?


von Käase (Gast)


Lesenswert?

Moin,

ich wühle mich jetzt schon seit stunden durch die Suchfunktion und das 
Tutorial, werde aber leider nicht fündig, deshalb versuche ich es jetzt 
so:

Randbedingungen: AVR Studio, ATMega 128, GCC.

Ich habe mehrer (momentan 5) Structs unterschiedlicher größe, die 
jeweils einen Datensatz an Einstellungen für meine Steuerung enthalten. 
Ich will nun jeweils 16 Speicher haben, wo ich also den aktuellen struct 
auf eine definierte Position ablege.
Am ende will ich also auf '4' klicken und er schreibt mir den Datensatz 
nummer 4 in den aktuellen Wertesatz. (ist klar, was ich mein, oder?)

Ich versuche jetzt wie folgt ein array anzulegen, wo die 
speicherpositionen liegen:
1
 uint16_t eeFooByteArray1[] EEMEM = { 1, 7 , 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, 85, 91};
Dabei beträgt der Datensatz dieses Structs 6 byte, es soll also alle 6 
byte ein neuer speicherpunkt liegen.

zum speichern nutze ich, nachdem ich die aktuellen Werte in die 
work-variable kopiert habe:
1
 eeprom_write_block(&work_pneu_sg,eeFooByteArray1[value],sizeof(tpropvent_sg_loadsave));

und zum öffnen diesen:
1
 eeprom_read_block(&work_pneu_sg,eeFooByteArray1[value],sizeof(tpropvent_sg_loadsave));

Das Problem ist jetzt, dass der erste Wert noch ordendlich gespeichert 
wird. Beim zweiten Resettet er. (bei der überprüfung des Adresswertes 
kommt auch nicht der heraus, der im Array steht und m.e. verwendet 
werden sollte..)

Ich nehme mal an, dass es sich um ein fundamentales Problem handelt.

Schonmal vielen Dank für Tips und Antworten.

von Klaus F. (kfalser)


Lesenswert?

Käase schrieb:
> Am ende will ich also auf '4' klicken und er schreibt mir den Datensatz
> nummer 4 in den aktuellen Wertesatz. (ist klar, was ich mein, oder?)
Nein

> Dabei beträgt der Datensatz dieses Structs 6 byte, es soll also alle 6
> byte ein neuer speicherpunkt liegen.
?????

Die Datensätze für die Parameter werden doch immer gleich groß sein, es 
ändert sich ja nur der Inhalt, oder?
Warum verschieden große Datensätze?

Du braucht ein struct für die Parameter die Du speichern willst, und 
dann legst Du dir ein Array aus 16 Elementen mit dieser Struct an.
Dann kannst Du auf das 1. Element zugreifen oder auf 4., usw. (lesend 
oder schreibend).

von Käase (Gast)


Lesenswert?

Hallo Klaus,

mh, also das Problem ist, dass ich 5 structs habe, die ich speichern 
will und die verschieden groß sind.

Also
struct a = 6 byte
struct b = 6 byte
struct c = 22 byte
struct d = 22 byte
struct e = 103 byte

und davon sollen jeweils 16 Datensätze im eeprom sein.

für diese Datensätze gibt es immer einen aktuellen, der vom Programm 
verwendet wird.

Jetzt habe ich eine Eingabemaske, mit der ich die Funktion aufrufe 
"speicher_struct_a(wert)" oder "lade_struct_b(wert)".

Ich will nun also auf den entsprechenden block zugreifen.
(das klärt vielleicht die Frage

>> Dabei beträgt der Datensatz dieses Structs 6 byte, es soll also alle 6
>> byte ein neuer speicherpunkt liegen.
>?????

oben hab ich ja die Tabelle angehängt mit den entsprechenden größen. Die 
Vorstellung dieses Arrays war, entsprechend alle 6 Byte (für den struct 
a) eine Adresse zu benennen, auf die ich zugreifen kann. Für die anderen 
Datensätze sähe das dann entsprechend im 22byte oder 103 byte - Raster 
aus.

von Käase (Gast)


Lesenswert?

Noch zur Ergänzung:

Wenn ich diese Form der Arrays dynamisch erzeuge, gehts:

1
uint16_t Addr_prop_sg[16] EEMEM;
2
3
//....
4
5
void init_adresses(void)
6
{
7
uint8_t i;
8
uint16_t start_addr = 200;
9
10
for(i=0; i<17; i++)
11
{
12
Addr_prop_sg[i] = start_addr + ((i) * (sizeof(uint16_t) + sizeof(int16_t) + sizeof(int16_t)));
13
}
14
}

Da das aber natürlich blödsinn ist, will ich die Werte lieber von 
vornherein einmal vorgeben...

von Stefan E. (sternst)


Lesenswert?

Käase schrieb:

> mh, also das Problem ist, dass ich 5 structs habe, die ich speichern
> will und die verschieden groß sind.
>
> Also
> struct a = 6 byte
> struct b = 6 byte
> struct c = 22 byte
> struct d = 22 byte
> struct e = 103 byte
>
> und davon sollen jeweils 16 Datensätze im eeprom sein.

Dann legst du eben im EEPROM von jeder struct ein Array der Größe 16 an.

Ich sehe momentan nicht, was das Problem sein soll.
Ich verstehe auch noch nicht, warum du im Augenblick im EEPROM 
uint16_t-Arrays anlegst, wo du doch eigentlich structs speichern willst.

von R. M. (rmax)


Lesenswert?

Klaus Falser schrieb:

> Du braucht ein struct für die Parameter die Du speichern willst, und
> dann legst Du dir ein Array aus 16 Elementen mit dieser Struct an.
> Dann kannst Du auf das 1. Element zugreifen oder auf 4., usw. (lesend
> oder schreibend).

Beim AVR kann man nicht einfach über eine Struct oder ein Array von 
Structs auf Inhalte des EEPROM zugreifen - weder lesend noch schreibend.

@Käase

Allerdings kannst Du Dir durch das Deklarieren entsprechender Structs 
und Arrays, die das Layout Deiner Daten im EEPROM wiederspiegeln das 
händische Ausrechnen und Eintippen der Offsets ersparen, was Fehlern 
vorbäugt und sich spätestens dann auszahlt, wenn Du mal einen Deiner 
Structs in der Größe verändern mußt.

Aber zu Deinem eigentlichen Problem: Könnte es sein, daß Deine 
Versorgungsspannung nicht gut genug gepuffert ist und durch die 
Stromspitze beim Schreiben ins EEPROM zusammenbricht, was dann zu dem 
Reset führt?

von R. M. (rmax)


Lesenswert?

Stefan Ernst schrieb:

> Ich verstehe auch noch nicht, warum du im Augenblick im EEPROM
> uint16_t-Arrays anlegst, wo du doch eigentlich structs speichern willst.

In dem uint16_t-Array stehen die Offsets, die die einzelnen Instanzen 
der Structs im EEPROM haben sollen.

von Käase (Gast)


Lesenswert?

>Aber zu Deinem eigentlichen Problem: Könnte es sein, daß Deine
>Versorgungsspannung nicht gut genug gepuffert ist und durch die
>Stromspitze beim Schreiben ins EEPROM zusammenbricht, was dann zu dem
>Reset führt?

Ich glabue das kann ich ausschließen. Das Problem war eher, dass ein 
völlig irrer wert auftauchte, der als adresse im EEprom herhalten 
sollte.

.. also vielleicht klärt sich mein Problem wenn ich kurz beschreibe 
warum ich diese arrays haben will: die sollen mir die Adresse der 
Datenwerte im EEProm geben. Eigentlich brauche ich die wirklich nicht im 
EEprom abzulegen, wie wäre denn die schönste methode das zu lösen?? [ich 
habe mich jetzt versucht an das Tutorial zu halten].

@Reinhard Max:
>Allerdings kannst Du Dir durch das Deklarieren entsprechender Structs
>und Arrays, die das Layout Deiner Daten im EEPROM wiederspiegeln das
>händische Ausrechnen und Eintippen der Offsets ersparen, was Fehlern
>vorbäugt und sich spätestens dann auszahlt, wenn Du mal einen Deiner
>Structs in der Größe verändern mußt.

wie genau funktioniert das? soetwas in der art suche ich, aber eben mit 
der möglichkeit, diese Adressen auch wiederzufinden..

von Käase (Gast)


Lesenswert?

Achso,

und so wie ich es oben beschreibe werden einfach nicht die Adressen 
übernommen! Wenn das klappen würde, wäre ich fürs erste zufrieden (auch 
wenn es unschön ist), aber es klappt nicht!

(vielleicht weiß da noch jemand bescheid, warum das so ist.. oder wie 
man das hinbekommt)

von Stefan E. (sternst)


Lesenswert?

Ok, also:

1)
Bei den Codezeilen im ersten Post fehlt jeweils ein eeprom_read_word für 
das Lesen der Offsets aus dem EEPROM, daher der Datenmüll.

2)
Das Array mit den Offsets (was ja auch im EEPROM liegt) kollidiert mit 
deiner eigenen Adressvergabe. Entweder du verwaltest die Lage aller 
Daten im EEPROM selber, oder du überlässt das dem Compiler. "Gemischter 
Betrieb" ist keine so gute Idee.

3)
Warum so kompliziert?
1
struct a EE_a[16] EEMEM;  // 16 struct a im EEPROM
2
3
struct a cur_a;  // aktuelles struct a im RAM
4
5
6
void speicher_struct_a (uint8_t ind) {
7
8
    eeprom_write_block(&cur_a,&EE_A[ind],sizeof(struct a));
9
}
10
11
12
void lade_struct_a (uint8_t ind) {
13
14
    eeprom_read_block(&cur_a,&EE_A[ind],sizeof(struct a));
15
}

von R. M. (rmax)


Lesenswert?

Käase schrieb:

> wie genau funktioniert das? soetwas in der art suche ich, aber eben mit
> der möglichkeit, diese Adressen auch wiederzufinden..

Hier mal ein Beispiel:
1
#include <sys/types.h>
2
#include <stdio.h>
3
4
struct foo {
5
    int foo1;
6
    int foo2;
7
};
8
9
struct bar {
10
    int bar1;
11
    int bar2;
12
};
13
14
struct foobar {
15
    struct foo fooo;
16
    struct bar baar;
17
};
18
19
void main(void)
20
{
21
    int i;
22
    for (i=0; i<16; i++) {
23
        printf("offset %d = %d\n", i, i * sizeof(struct foobar));
24
    }
25
}

Die Structs foo und bar stehen für Deine vorhandenen Structs. Die werden 
in foobar zu einem abzuspeichernden Datensatz zusammengefaßt. Davon 
sizeof() sagt Dir wie groß ein Datensatz ist. Diese Größe multipliziert 
mit der Nummer des Datensatzes gibt seinen Offset. Wenn Du nur eine 
Struct aus einem Datensatz lesen willst, kommt noch offsetof() dazu 
(stddef.h). Beispielsweise findest Du die Struct bar des dritten 
Datensatzes an der Adresse 2*sizeof(struct foobar)+offsetof(struct 
foobar, baar) .

von Käase (Gast)


Lesenswert?

Hallo Stefan, ich glaube mir wird gerade etwas klar!

Ich habe die Speicherverwaltung die ganze Zeit total falsch verstanden!!

irgendwie ist mir da einiges durch die Lappen gegangen, ich dachte die 
Adressen würde ich direkt verwalten.

Kurzum: ich habe es gerade gegengecheckt mit mehreren Structs und es 
läuft!

Vielen Dank allen, die geholfen haben und sich Gedanken gemacht haben!!

von Käase (Gast)


Lesenswert?

@Reinhard

ich lese gerade noch deine Anmerkungen.

So ginge es sicher auch, aber ist wohl komplizierter als ich es 
benötige.
Trotzdem Danke für die Erläuterungen!

Vielleicht noch eine Verständnisfrage: wo befindet sich der Zeiger auf 
die Struct - Offsets, wenn die structs doch ins EEprom gelegt sind.. 
also ich hatte ja die ganze Zeit gedacht, ich bräuchte da noch einen 
Wert / Adresse im Ram des µCs, die ich verwende. Oder braucht es eben 
genau das überhaupt nicht udn der µC liest direkt den eeprom aus um die 
Adresse zu finden?

von R. M. (rmax)


Lesenswert?

Käase schrieb:
> @Reinhard
>
> ich lese gerade noch deine Anmerkungen.
>
> So ginge es sicher auch, aber ist wohl komplizierter als ich es
> benötige.

Soweit ich es verstehe, macht meine Variante im Endeffekt genau das 
gleiche wie die von Stefan, nur daß ich es in ANSI-C gelöst habe, weil 
mir die EEMEM-Erweiterung von avr-gcc bisher nicht geläufig war.

Stefans Lösung ist etwas komfortabler zu benutzen, meine ließe sich 
vermutlich leichter anpassen, falls der Platz im internen EEPROM nicht 
mehr ausreicht und die Daten deshalb in ein externens EEPROM sollen, das 
per I²C oder SPI angebunden ist.

Mit einem Makro läßt sich meine Variante auch noch soweit vereinfachen, 
daß Du am Ende nur noch sowas in der Art schreiben mußt:
1
eeprom_read_block(&cur_a, OFFSET(i, a), sizeof(struct a))
um die Struct a des i-ten Datensatzes aus dem EEPROM zu lesen.

> Vielleicht noch eine Verständnisfrage: wo befindet sich der Zeiger auf
> die Struct - Offsets, wenn die structs doch ins EEprom gelegt sind.

Das sind eigentlich keine Zeiger im C-Sinn, sondern schlicht die 
EEPROM-Adressen, wie Du sie in Deinem ersten Beispiel explizit 
aufgelistet hattest. Übrigens müßte das auch funktionieren, wenn Du von 
eeFooByteArray1 die EEMEM-Deklaration entfernst. Das Problem war, daß Du 
das Array im EEPROM deklarierst, dann aber so verwendest, als würde es 
im RAM liegen, wo Du es in diesem Fall ja auch brauchst.

> Oder braucht es eben genau das überhaupt nicht udn der µC liest
> direkt den eeprom aus um die Adresse zu finden?

Die Adresse wird schon im RAM (bzw. einem Register) aus der Größe der 
Struct und den Array-Index berechnet, aber sie muß eben ihrerseits nicht 
im EEPROM abgelegt werden, weil das Programm ja die Struktur der Daten 
im EEPROM kennt.

von Stefan E. (sternst)


Lesenswert?

Reinhard Max schrieb:

> Soweit ich es verstehe, macht meine Variante im Endeffekt genau das
> gleiche wie die von Stefan, nur daß ich es in ANSI-C gelöst habe, weil
> mir die EEMEM-Erweiterung von avr-gcc bisher nicht geläufig war.

Das EEMEM ist keine spezielle EEPROM-Erweiterung. Es ist nur ein Makro, 
dass an die Variable ein Section-Attribut "dranpappt", damit die 
Adressvergabe für diese Variablen gesondert gehandhabt werden kann. 
Exakt den gleichen Mechanismus kann man auch verwenden, um Variablen in 
eine NoInit-Section zu legen, oder in externes Ram.

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.