Hi Forum,
ich tue mich bei oben genanntem Thema schwer und bitte um eure Hilfe!
Ich habe eine struktur definiert
1
typedefstruct
2
{
3
uint8_tprotocol;
4
uint16_taddress;
5
uint16_tcommand;
6
}structIR;
Ein Array dieser Struktur wird im EEPROM abgelegt
1
structIReIRFunctions[]EEMEM={
2
{0x00,0x00,0x00},
3
{0x00,0x00,0x00},
4
{0x00,0x00,0x00},
5
{0x00,0x00,0x00},
6
{0x00,0x00,0x00},
7
{0x00,0x00,0x00},
8
{0x00,0x00,0x00},
9
{0x00,0x00,0x00},
10
{0x00,0x00,0x00},
11
{0x00,0x00,0x00},
12
{0x00,0x00,0x00}};
Jetzt möchte ich einer Funktion ein Array Element dieser Struktur
übergeben und in der Funktion die einzelnen Werte der Strukturfelder im
EEPROM Speichern.
1
Pseudocode:
2
3
setIrDataToEeprom(&eIRFunctions[0]);
4
5
voidsetIrDataToEeprom(blablub)
6
{
7
eeprom_write_byte(blablub.protocoll,11);//11 in Strukturelement protocol schreiben
8
eeprom_write_word(blablub.address,22);//22 in Strukturelement address schreiben
9
eeprom_write_word(blablub.command,33);//33 in Strukturelement command schreiben
10
}
Trotz Suche stehe ich total auf dem Schlauch!
Kann mir jemand näher bringen, zu verstehen wie ich die Aufgabenstellung
löse?
Dank im Vorraus!
Gruß
Stefan
Nun, das Schlüsselwort EEMEM ist eine GCC-Erweiterung gegenüber dem
Standard, der es Dir erlaubt, Variablen im EEPROM zu deklarieren und zu
initialisieren. An sich kennt C keinen Unterschied zwischen
Speicherarten.
Entsprechend gehen alle weiteren Operatoren immer davon aus, dass
Variablen im RAM liegen und der Compiler generiert entsprechende
Operationen des uC - einen AVR vorausgesetzt -, (in Assembler) daraus.
Wie Du sicher schon festgestellt hast, kennt der AVR keine Befehle um
Daten aus dem EEPROM zu lesen oder darein zu schreiben - jedenfalls
nicht in der gleichen Granularität wie solche Daten im RAM. D.h. es sind
etwa Daten als 8 oder 16-Bit Wort zu lesen und zu schreiben. Das ist
die, man könnte sagen: primäre Funktion eines uC. Es ist sogar so, dass
beim AVR eine gewisse Abfolge von Befehlen eingehalten werden muss und
nur Blöcke von Daten geschrieben werden können. Ausserdem sind solche
Operationen wesentlich langsamer als solche auf das RAM.
Im Detail kann demnach der '.'-Operator nicht auf Operanden im EEPROM
angewendet werden. Du musst ihn also für das EEPROM irgendwie mit einer
Funktion emulieren. Letzlich läuft das daraus hinaus, die Adresse eines
Strukturmitglieds zu ermitteln. Das wiederrum ist deswegen ein wenig
komplex, weil Du dazu einen Zeiger auf solch eine Struktur benötigst und
ausserdem voraussetzen (bzw. prüfen) musst, dass das Layout der Struktur
im RAM und EEPROM gleich ist. Das ist aber - meiner Kenntnis nach - der
Fall. Ein letzter Faktor ist die Anordnung der Strukturmitglieder, die
nicht zwingend ohne Lücken erfolgt. Dazu gibt es das "pragma packed".
Ich weiss gerade nicht ob der GCC etwa EEMEM structs per default packt.
Ich hoffe die Hinweise helfen Dir ein wenig.
>eeprom_write_byte(structIR_ptr->protocol,11);//11 in
4
>Strukturelementprotocolschreiben
5
>eeprom_write_word(structIR_ptr->address,22);//22 in
6
>Strukturelementaddressschreiben
7
>eeprom_write_word(structIR_ptr->command,33);//33 in
8
>Strukturelementcommandschreiben
9
>
10
>}
11
>
>> Aufruf (wie gehabt):>
1
>setIrDataToEeprom(&eIRFunctions[0]);
2
>
Aber eigentlich macht man auch das nicht so.
Ausgangspunkt ist erst mal, dass man ein derartiges Objekt grundsätzlich
im Speicher braucht, damit man damit in weiterer Folge damit arbeiten
kann.
Man wird also ein
1
structIRfuncData;
haben (oder ein entsprechendes Array, wenn man mehrere derartige Daten
gleichzeitig im Speicher braucht).
Gilt es da andere Werte reinzuschreiben, dann wird man das direkt in
diesem Objekt machen. Zum Beispiel durch ....
1
funcData.protocoll=11;
2
funcData.address=22;
3
funcData.command=33;
... oder wie auch immer dann die Werte zustande kommen.
Ist es Zeit die entsprechenden Objektinhalte ins EEPROM zu schreiben
bzw. vom EEPROM zu lesen, dann gibt es dafür die Funktion
eeprom_write_block, die einen kompletten zusammenhängenden
Speicherbereich ohne Ansehen der Innereien aus dem SRAM ins EEPROM
verfrachtet bzw. umgekehrt vom EEPROM ins SRAM holt. D.h. an dieser
Stelle muss ich dann gar nicht mehr wissen, wie die Struktur aufgebaut
ist. Eine derartige Spezialfunktion für die Memberweise Überführung ist
nur dann notwendig, wenn nicht alle Member der Struktur im EEPROM
gespeichert werden müssen (Padding ist auf einem AVR ja kein Thema). Im
Falle eines Arrays erledigt dann zb beim Programmstart ein einziger
eeprom_read_block die komplette Initialisierung des kompletten Arrays
aus dem EEPROM.
Karl H. schrieb:> Ausgangspunkt ist erst mal, dass man ein derartiges Objekt grundsätzlich> im Speicher braucht, damit man damit in weiterer Folge damit arbeiten> kann.
Hast Recht. War noch zu früh heute morgen. Hab wohl das EEMEM
übersehen...
Hallo Karl Heinz,
vielen Dank für deine gut verständlichen Ausführungen!
Wie man heruasliest, bin ich nur Hobby AVR Programmierer. Sobald ein
Bedürfnis entsteht, suche ich nach einer Lösung.
Heißt das, dass man darauf achten sollte zu allen EEPROM Variablen ein
Gegenstück im SRAM halten sollte ?
Also auch bei weniger komplexen Datentypen wie uint8_t etc. ?
Wenn ja, müsste ich meine Architektur überdenken.
Jetzt wollte ich die Funktion zum Schreiben des Arrayfeldes in das
EEPROM ja universell gestaltet. Wie es aussieht benötige ich hierzu drei
Übergabeparameter.
1. structIR IRFunctions[NR_FUNCTIONS];
2. structIR eIRFunctions[NR_FUNCTIONS] EEMEM;
3. sizeof(structIR)
Und genau hier stehe ich auf dem Schlauch.
- Werden EEPROM und SRAM Variable als Pointer übergeben?
- Wie geschrieben?
- und an eeprom_write_block() witergegeben
Deklaration
technikus schrieb:> Heißt das, dass man darauf achten sollte zu allen EEPROM Variablen ein> Gegenstück im SRAM halten sollte ?
Nein. Das wäre Speicherwahnwitz. Aus den gewählten Variablen-Namen kann
man schlussfolgern, dass es sich um eine IRMP-Anwendung handelt.
Du solltest Dir daher mal den Source von
DIY Lernfähige Fernbedienung mit IRMP
anschauen, wie das dort gelöst ist.
Der Trick ist ganz einfach: Du brauchst nur eine IRMP-Struct und
liest/schreibst auch nur ad hoc die Daten eines IRMP-Datensatzes -
nämlich dann, wenn Du ihn brauchst.
> Jetzt wollte ich die Funktion zum Schreiben des Arrayfeldes in das> EEPROM ja universell gestaltet. Wie es aussieht benötige ich hierzu drei> Übergabeparameter.
Benutze das EEPROM einfach als RAM, indem Du auf die Daten mit den
eeprom-Funktionen (read/write) zugreifst.
> Und genau hier stehe ich auf dem Schlauch.> - Werden EEPROM und SRAM Variable als Pointer übergeben?
Schau einfach in den Source der lernfähigen Fernbedienung. Das einzige,
was dort komplizierter ist: Die Fernbedienung dort hat mehrere virtuelle
Ebenen, mit denen die Tasten belegt werden können, d.h. dort hat man
noch eine Dimension mehr als Du hast. Ausserdem können die Tasten mit
Makros belegt werden, d.h. es wird beim Drücken einer Taste eine ganze
Serie von IRMP-Frames gesendet. Macht eine weitere Dimension mehr ;-)
Aber lass Dich nicht verwirren, sondern schreib Dir am besten 2
Funktionen:
read_irmp_data_from_eeprom (&irmp_data, n);
write_irmp_data_to_eeprom (&irmp_data, n);
mit
IRMP_DATA irmp_data; // IRMP-Daten im RAM
uint8_t n; // Index in Deinem EEPROM-Array
Das Array selbst brauchst Du nur im EEPROM (also mit EEMEM definiert).
P.S.
Wenn Du Deine abgespeckte struct "structIR" nutzen willst, geht das
natürlich genauso. Dort hast Du die flags weggelassen. Spart natürlich
EEPROM-Platz. Dann musst Du halt nochmal zwischen structIR und IRMP_DATA
kopieren. Aber eine kleine Warnung: In (flags & 0xF0) stehen beim
KASEIKYO-Protokoll zusätzliche Daten, die nicht mehr in uint16_t command
passen. Wenn Du kein KASEIKYO-Protokoll benutzt, kannst Du das natürlich
ignorieren und weglassen. Wenn Du aber den Platz im EEPROM hast,
solltest Du ruhig die komplette IRMP-Struct speichern. Die Flags
solltest Du dann aber vor dem Speichern bereinigen mit:
irmp_data.flags &= 0xF0;
bzw.
irmp_data.flags &= ~IRSND_REPETITION_MASK;
, um etwaige empfangenen Repetition-flags oder andere zukünftige
IRMP-Flags auszumaskieren, d.h. zu löschen.
technikus schrieb:> Heißt das, dass man darauf achten sollte zu allen EEPROM Variablen ein> Gegenstück im SRAM halten sollte ?> Also auch bei weniger komplexen Datentypen wie uint8_t etc. ?
Du holst dir das in den SRAM Speicher, was du zum arbeiten brauchst.
Wenn du 25 Konfigurationen im EEPROM hast, von denen aber immer nur eine
die aktive Konfiguration ist, dann holst du dir auch nur diese eine.
Das willst du sowieso machen, denn der Zugriff aufs EEPROM ist langsam!
Daher willst du das EEPROM nicht einfach nur wie einen Speicher ansehen,
so wie jeden anderen auch. Ausser natürlich du hast keine andere Wahl.
Du kannst den EEPROM wie das Hochregallager ansehen, in dem du Werkzeuge
auf Dauer verstaust. Die Werkzeuge, die du zum Arbeiten brauchst, die
hast du aber vor dir auf der Werkbank. Mit denen arbeitest du laufend.
> Und genau hier stehe ich auf dem Schlauch.> - Werden EEPROM und SRAM Variable als Pointer übergeben?
Ja natürlich. Arrays werden an Funktonen in C immer so 'übergeben', dass
die Funktion die Startadresse des Arrays bekommt.
Hallo Technikus,
wenn ich Konfigurationswerte im EEPROM ablege, sorge ich dafür, dass die
Strukturen gepackt sind.
Es könnte sonst passieren, dass bei unterschiedlichen
Compiler-Einstellungen, die Werte auf unterschiedlichen Bytepositionen
liegen.
Und dann liest du mit dem neuen Compilat nicht mehr das ein, was beim
alten Compilat noch ging.
Stichworte sind hier:
__attribute__((_packed_)) und als Compileroption -fpack-struct
Grüße, Adib.
Adib schrieb:> Hallo Technikus,>> wenn ich Konfigurationswerte im EEPROM ablege, sorge ich dafür, dass die> Strukturen gepackt sind.
Die sind auf einem AVR sowieso immer gepackt, weil es keinen Grund gibt
warum der Compiler auf einem AVR padding Bytes einfügen sollte.
Viel schwerwiegender ist, dass du mittels EEMEM keine Kontrolle darüber
hast, wo der Linker die EEMEM Variablen im EEPROM anordnet.