Ich möchte gerne eine Struktur mit unterschiedlichen Integer variablen
unterschiedlicher Größe byteweise auslesen/schreiben. es geht hier ganz
einfach um EEPROM Inhalte eines AVR, der EEPROM-Inhalt soll extern
zusammengestellt werden, die Definition der Struktur befindet sich in
einem Headerfile das von der AVR Firmware eingebunden wird.
Eine Struktur deshalb, weil nur die Reihenfolge der Variablen nur
innerhalb einer Solchen garantiert ist (Gegenteil von
-no-toplevel-reorder)
Also z.B.
struct eeprom_val
{
uint8_t bla;
uint16_t fasel;
uint32_t foo;
int16_t bar;
}ee-val;
Wie zur Hölle vereinbare ich nun eine union die die Struktur und ein
Array gleicher Länge enthält, die mir Zugriff auf den Speicherbereich er
Struktur ermöglicht?
Gruß,
Holm
Bei der 9 wäre ich mir nicht ganz so sicher.
Der Compiler darf zwischen den struct-Membern Padding-Bytes einfügen.
(Das kann man mit Compilerdirektiven verhindern)
Dirk B. schrieb:> Bei der 9 wäre ich mir nicht ganz so sicher.> ...Padding-Bytes...
Ich glaube, der Compiler darf sogar die Reihenfolge ändern.
Die Konstruktion sollte man sowieso unter besondere Beobachtung stellen.
Etwas universeller:
Nop schrieb:> Dirk B. schrieb:>>> Der Compiler darf zwischen den struct-Membern Padding-Bytes einfügen.>> Aber auf einem 8-bitter (AVR) gibt's dazu doch keinen Grund?
Mein Haken daran ist, das ich die Daten auf einer 64Bit Maschine
erzeugen will und im AVR dann lesen. Das ist hier also durchaus zu
beachten ..und zu unterbinden.
Gruß,
Holm
Holm T. schrieb:> Mein Haken daran ist, das ich die Daten auf einer 64Bit Maschine> erzeugen will und im AVR dann lesen.
In dem Fall empfehle ich allein schon wegen Endianess eine
Serialisierung, daß Du also wirklich byteweise in das Array schreibst.
Ach ja, und auf dem AVR dann entweder die passende Deserialisierung,
also byteweise lesen und shiften, oder falls das nicht möglich ist,
sondern direkt aus dem Flash gelesen werden muß, daß Du auf 64bit die
Bytes byteweise so in das Array schiebst, daß die Endianess auf AVR
schon mit berücksichtigt wird.
Das Lesen auf AVR wäre dann nur noch Dereferenzieren des
struct-Pointers. Padding sollte es auf AVR ja nicht geben.
Ralf G. schrieb:> Dirk B. schrieb:>> Bei der 9 wäre ich mir nicht ganz so sicher.>> ...Padding-Bytes...> Ich glaube, der Compiler darf sogar die Reihenfolge ändern.> Die Konstruktion sollte man sowieso unter besondere Beobachtung stellen.>> Etwas universeller:>
1
>#defineEE_SIZEsizeof(structee_struct)
2
>structee_struct
3
>{
4
>uint8_tbla;
5
>uint16_tfasel;
6
>uint32_tfoo;
7
>int16_tbar;
8
>};
9
>unionee_union
10
>{
11
>structee_structee_val;
12
>uint8_tee_array[EE_SIZE];
13
>};
14
>
ja..besondere Bobachtung ... :-)
Ich habe früher mal die Variablen einfach nacheinander deklariert und
das ging jahrelang gut. Ab gcc4.8 habe ich dann groß Augen bekommen,
weil die plötzlich in umgekehrter Reihenfolge im EEprom standen,
vertreibe ließ sich das mit "-f-no-toplevel-reorder". Jörg sagte mir
damals das die Reihenfolge nur in einer Struktur nicht eränder wird, das
ich nun mit maschinenabhängigem Padding zu kämpfen habe ist ein anderes
Paar Schuhe.
Das Konstrukt oben ging IMHO auch schief, mir fällt jetzt dazu die
Fehlermeldung nicht mehr ein (habe das vorige Woche probiert).
Ich probiere mal..
Gruß,
Holm
Holm T. schrieb:> Jörg sagte mir> damals das die Reihenfolge nur in einer Struktur nicht eränder wird, das> ich nun mit maschinenabhängigem Padding zu kämpfen habe ist ein anderes> Paar Schuhe.
Hinzu kommt auch noch die Byte-Reihenfolge. unions sind nunmal
grundsätzlich nicht zur Konvertierung von Daten da, sondern nur zum
Speicher sparen. Einfach Pointer umcasten ist genauso verkehrt. Die
einzig korrekte (mit wohldefiniertem Ergebnis) Vorgehensweise ist es,
die Bytes einzeln mittels Bitshifts in/aus Arrays zu packen. Das ist in
C leider recht umständlich (weswegen dann doch oft die union-Variante
genommen wird), funktioniert dafür aber garantiert immer auf allen
Plattformen mit allen Compilern/Versionen/Optionen.
Dr. Sommer schrieb:> Hinzu kommt auch noch die Byte-Reihenfolge. unions sind nunmal> grundsätzlich nicht zur Konvertierung von Daten da
Type punning via unions ist doch mit C99 erlaubt?
Nop schrieb:> Type punning via unions ist doch mit C99 erlaubt?
Aber das Ergebnis ist nicht definiert (da plattformabhängig), es treten
immer noch die Probleme mit Padding/Alignment und Byte-Reihenfolge auf.
Hier eine Möglichkeit wie man es (in C++) mittels Meta-Programmierung
korrekt macht (mit immer garantiertem Ergebnis):
Beitrag "Re: Endianess beim Kopieren automatisieren"
Naja...das muß nicht wahnsinnig portabel sein, es geht nur um die
Parametrisierung von Schrittmotorantrieben, konkret um ein paar
Drehzahlen und was hier exemplarabhängig wird, die Anpassung an die
Ungenauigkeiten der verwendeten Trapez-Spindeln.
Ich muß ein Progrämmchen machen mit dem die Mechaniker "im Feld" den
Kram einstellen können. Es gibt nur den AVR der die Prominhalte ausliest
und das Parametrisierungsprogramm das die Werte setzt. Ich muß das also
nicht so portabel schreiben das es auch auf einem Z80 oder 6502
funktioniert..der Aufwand sollte sich also auch in Grenzen halten.
Aus Erfahrung weiß ich aber nun auch das ein avr-gcc da auch schon ein
moving Target ist.
Portabel war ein Lochstreifen..ich denke über Leser und Stanzer nach :-)
Gruß,
Holm
Holm T. schrieb:> Ich muß ein Progrämmchen machen mit dem die Mechaniker "im Feld" den> Kram einstellen können.
Da liegt ja schon der Haken... was passiert wenn du das Programm mal für
x86, mal für AMD64, mal mit GCC, mal mit MSVC kompilierst? Die musst du
dazu bringen sich immer gleich zu verhalten... Die alten Mac's hatten ja
Big Endian Prozessoren, bei denen wäre das Problem dann schon
schwieriger geworden...
Dr. Sommer schrieb:> Aber das Ergebnis ist nicht definiert (da plattformabhängig)
Nicht definiert oder implementationsabhängig? Ansonsten kann man auf
64bit, wie schon gesagt, byteweise in das Array schieben. Man muß halt
nur die Bytes (Endianess) an die richtige Stelle verfrachten.
Im simpelsten Fall setzt man auf AVR über das struct einmal jedes Byte
und läßt sich das z.B. über die serielle ausgeben, wo welches Byte zu
liegen kommt, ungefähr so (0x31 ist '1'):
Nop schrieb:> Nicht definiert oder implementationsabhängig?
Ok, falsch ausgedrückt, es ist letzteres. Im C Standard ist nunmal kein
Ergebnis definiert, bei der Nutzung von Bitshifts aber schon...
Nop schrieb:> Ansonsten kann man auf 64bit, wie schon gesagt, byteweise in das Array> schieben. Man muß halt nur die Bytes (Endianess) an die richtige Stelle> verfrachten.
Oder man macht es gleich auf beiden Seiten so, dann ist's richtig.
Dr. Sommer schrieb:> Oder man macht es gleich auf beiden Seiten so, dann ist's richtig.
Richtig schon, und sofern möglich, würde ich das auch bevorzugen. Dann
ist der embedded code auch z.B. auf x86 algorithmisch testbar, und man
fällt nicht auf die Nase, wenn in drei Jahren das Projekt mit einem
anderen Controller laufen soll.
Wenn das aber aus Performancegründen nicht möglich ist, sondern die
Daten direkt und gültig aus dem Flash gelesen werden müssen, dann wäre
der kleine Hack von oben meine nächste Wahl.
Nop schrieb:> Wenn das aber aus Performancegründen nicht möglich ist
Wird von Compilern meistens wegoptimiert. Insbesondere auf 8 Bit
Architekturen sollte sich nicht wirklich ein Unterschied ergeben...
Dumpfbacke schrieb:> Hab ich was überlesen? Ich finde es nicht .....Dumpfbacke schrieb:> #pragma pack(1)
Das ist eine Compiler spezifische Erweiterung. So was sollte man sowieso
nur nutzen wenn's gar nicht anders geht. Gepackte structs gehen je nach
Datentypen auf mancher Hardware gar nicht (zB mit uint64_t auf ARM). Bei
x86 sollte es gehen (ist aber langsam).
Dr. Sommer schrieb:> Das ist eine Compiler spezifische Erweiterung.
Welcher weit verbreitete Compiler versteht das nicht?
Ohne zu wissen was der TO wirklich benutzt wette ich dass
sein Compiler das kann.
Dr. Sommer schrieb:> So was sollte man sowieso> nur nutzen wenn's gar nicht anders geht.
Dann lieber dämlich zu Fuss die Bytes fehlerträchtig
'rüberschieben, gell?
Dr. Sommer schrieb:> Bei x86 sollte es gehen (ist aber langsam).
Der TO hat keine Ansprüche an die Geschwindigkeit gestellt.
Auf heutigen 64-Bit Systemen sollte diese kleine Daten-
verarbeitung auch keine wesentliche Rolle spielen.
Dumpfbacke schrieb:> Welcher weit verbreitete Compiler versteht das nicht?
Also ich programmiere normalerweise C(++) -Programme, keine
GCC-Programme, und halte mich an die Gegebenheiten der Sprache, nicht
die des Compilers...
Dumpfbacke schrieb:> Dann lieber dämlich zu Fuss die Bytes fehlerträchtig> 'rüberschieben, gell?
Eine Lösung wurde bereits genannt:
Beitrag "Re: union mit Struktur und Array gleicher Größe?"
Damit funktioniert es garantiert immer korrekt auf allen Compilern und
es ist auch nicht sonderlich fehlerträchtig.
Das übliche Phänomen hier, dass man partout nicht die korrekte Lösung
nutzen will und sich lieber mit halben Spezial-Lösungen rumschlägt die
manchmal funktionieren...
Dr. Sommer schrieb:> Eine Lösung wurde bereits genannt:
Funktioniert nicht für C, da in C++.
> Das übliche Phänomen hier, dass man partout nicht die korrekte Lösung> nutzen will
Byteweise Serialisierung ist korrekt und wird auch bei
Host/Network-Problemen angewandt.
Nop schrieb:> Funktioniert nicht für C, da in C++.
Bin mir grad nicht sicher wo C verlangt wurde...?
Nop schrieb:> Byteweise Serialisierung ist korrekt und wird auch bei> Host/Network-Problemen angewandt.
Ja. Genau dazu rate ich ja auch, eben ggf. gekapselt damit es einfacher
geht.
Eigentlich sollte man für jeden Datentyp eine separates Funktionspaar
schreiben. Habe ich mir hier mal gespart. Habe auch nicht wirklich auf
Bugs geprüft. Sollte aber zumindest die Idee zeigen.
Dr. Sommer schrieb:> Nop schrieb:>> Funktioniert nicht für C, da in C++.> Bin mir grad nicht sicher wo C verlangt wurde...?
Gar nicht. Ich verstehe es mehr als eine Anmerkung.
Dr. Sommer schrieb:> Nop schrieb:>> Wenn das aber aus Performancegründen nicht möglich ist> Wird von Compilern meistens wegoptimiert. Insbesondere auf 8 Bit> Architekturen sollte sich nicht wirklich ein Unterschied ergeben...
Bei 8-Bittern mag das sein, auf einem ARM sieht das ganz anders aus. Ein
byte-weises Laden und Zusammenbasteln eines 32 Bit Typs kostet 7
Instruktionen (4 Loads, 3 ORs mit Shift).
Wenn man jedoch weis das die Daten sauber im Speicher ausgerichtet sind,
dann kostet das genau eine Instruktion wenn man die Variable direkt
lädt. Ich weis nicht ob der Compiler bei modernen ARMs (M3/M4) die auch
Zugriffe auf ungerade Addressen können aus dem ganzen Byte-Gewurstel da
automatisch einen Load/Store draus macht, bei uns war das jedoch gar
nicht möglich, da die Daten aus den Peripherien kommen (Device Memory
Attribute), dort sind solche unausgerichteten Zugriffe generell nicht
möglich.
Wenn der Hauptteil der Software darin besteht solche Strukturen zu
zerlegen und wieder zusammenzubasteln, z.B. in Kommunikationsanwendungen
dann explodiert einem die Programmgröße. Ich habe vor ein paar Jahren
dahingehend eine Optimierung machen müssen, das hat bei uns gut 100 kB
Code gespart (bei etwa 500kB Codegröße) ...
Dr. Sommer schrieb:> Holm T. schrieb:>> Ich muß ein Progrämmchen machen mit dem die Mechaniker "im Feld" den>> Kram einstellen können.>> Da liegt ja schon der Haken... was passiert wenn du das Programm mal für> x86, mal für AMD64, mal mit GCC, mal mit MSVC kompilierst?
Das passiert definitiv nicht. Warum sollte ich ein solches Programm, das
nur ein einziges Mal benötigt wird auf unterschiedlichen Architekturen
mit unterschiedlichen Optimierungen bauen?
> Die musst du> dazu bringen sich immer gleich zu verhalten... Die alten Mac's hatten ja> Big Endian Prozessoren, bei denen wäre das Problem dann schon> schwieriger geworden...
htons(), htonl(), ntohs(), ntohl()... :-)
Gruß,
Holm
Andreas M. schrieb:> Ein> byte-weises Laden und Zusammenbasteln eines 32 Bit Typs kostet 7> Instruktionen (4 Loads, 3 ORs mit Shift).
Meistens ist ein 4-Byte-Wert nicht auf 4 einzelne Bytes aufgeteilt,
sondern 2x2Byte oder 1x1 + 1x3 Bytes, also 2 Loads und ein OR mit
Shift...
Andreas M. schrieb:> Wenn man jedoch weis das die Daten sauber im Speicher ausgerichtet sind
Der Punkt ist: Genau das sind sie nicht, weil sie über ein Interface
reinkommen. Daher ja der ganze Aufwand.
Andreas M. schrieb:> Ich weis nicht ob der Compiler bei modernen ARMs (M3/M4) die auch> Zugriffe auf ungerade Addressen können aus dem ganzen Byte-Gewurstel da> automatisch einen Load/Store draus macht
m.W. nicht.
Andreas M. schrieb:> Wenn der Hauptteil der Software darin besteht solche Strukturen zu> zerlegen und wieder zusammenzubasteln
Was soll man machen? Man muss nunmal irgendein Protokoll definieren, und
das muss man wohl oder übel (ent)packen. Die Frage ist lediglich ob man
es korrekt (Bitshifts) oder gefrickelt (unions/casts) macht.
Holm T. schrieb:> Warum sollte ich ein solches Programm, das> nur ein einziges Mal benötigt wird auf unterschiedlichen Architekturen> mit unterschiedlichen Optimierungen bauen?
Es kommt vor, dass Projekte wachsen und neue Wünsche kommen. Wenn es
dann heißt "portier das doch mal auf ein Android-Tablet" beißen einen
solche Schnellschuss-Lösungen schnell in den Hintern...
Holm T. schrieb:> htons(), htonl(), ntohs(), ntohl()... :-)
Das sind POSIX-Funktionen (schon wieder irgendeine nichtportable
Erweiterung). Wie gesagt, Bitshifts sind portabel, und auch eleganter,
denn: Bei Bitshifts schreibt man hin was man machen will und der
Compiler sorgt dafür dass es richtig gemacht wird. Bei htons & Co
schreibt man eine eventuell nötige Operation hin die ggf. ausgeführt
wird - m.E. komplizierter und fehlerträchtiger.
Mikro 77 hat gezeigt wie es in C richtig, portabel, ohne Erweiterungen
geht. Der genannte C++ Code ist mehr oder weniger ein Wrapper um genau
dieses Vorgehen.
PS: Ich sollte mal hier einen Wiki-Artikel verfassen, der das ein für
alle Mal (die Frage kommt ja alle paar Wochen) korrekt zusammenfasst und
die richtige Lösung zeigt...
Dr. Sommer schrieb:> Andreas M. schrieb:>> Ein>> byte-weises Laden und Zusammenbasteln eines 32 Bit Typs kostet 7>> Instruktionen (4 Loads, 3 ORs mit Shift).> Meistens ist ein 4-Byte-Wert nicht auf 4 einzelne Bytes aufgeteilt,> sondern 2x2Byte oder 1x1 + 1x3 Bytes, also 2 Loads und ein OR mit> Shift...
Das verstehe ich nicht. Bei mir besteht ein 32-Bit Wert in jedem
Raw-Blob aus 4 einzelnen Bytes. Sobald ich von zwei Byte oder drei Byte
spreche lege ich schon implizit eine Struktur des Raw-Blobs fest.
Wenn dieser Raw Blob beim Deserialisieren vom Programmcode in Form von
einzelnen Bytes eingelesen wird, dann benötige ich genau vier
Bytezugriffe plus Schiebeoperations und OR's:
Mit Byte-Zugriffen meine ich:
1
uint8_traw[4];
2
uint32_tvalue;
3
4
value=((uint32_t)raw[0]<<24)|
5
((uint32_t)raw[1]<<16)|
6
((uint32_t)raw[2]<<8)|
7
((uint32_t)raw[3]<<0);
sieht dann in thumb (ARM966) übersetzt in etwa so aus (Kopiert aus
original code, nicht obiges beispiel, aber identischer Aufbau,
Mit Thumb-2 kann man die Shifts in die Orrs reinkombinieren):
1
be: 7be0 ldrb r0, [r4, #15]
2
c0: 7ba3 ldrb r3, [r4, #14]
3
c2: 0200 lsls r0, r0, #8
4
c4: 4318 orrs r0, r3
5
c6: 7c23 ldrb r3, [r4, #16]
6
c8: 041b lsls r3, r3, #16
7
ca: 4318 orrs r0, r3
8
cc: 7c63 ldrb r3, [r4, #17]
9
ce: 061b lsls r3, r3, #24
10
d0: 4318 orrs r0, r3
Vieleicht kannst Du erklären was du mit im 2x2Byte oder 1+1x3Byte
meinst?
Andreas M. schrieb:> Wenn dieser Raw Blob beim Deserialisieren vom Programmcode in Form von> einzelnen Bytes eingelesen wird, dann benötige ich genau vier> Bytezugriffe plus Schiebeoperations und OR's:
Na, wenn die 4 Bytes aufeinander folgen, sind 3 davon in einem Word, und
das restliche im vorherigen oder darauffolgenden Word. Bzw 2 und 2. Da
muss man nicht jedes Byte einzeln lesen, sondern kann mit LDRH 2 auf
einmal lesen. Ich hab mich aber vertan, in der 3+1 Variante sind es 3
Lesezugriffe (LDRH+LDRB+LDRB).
Andreas M. schrieb:> Ich weis nicht ob der Compiler bei modernen ARMs (M3/M4) die auch> Zugriffe auf ungerade Addressen können aus dem ganzen Byte-Gewurstel da> automatisch einen Load/Store draus macht
Das habe ich jetzt doch mal ausprobiert, und siehe da: Der GCC kann's!
Das:
1
uint32_ttest(uint8_t*p){
2
uint32_ta=p[1];
3
uint32_tb=p[2];
4
uint32_tc=p[3];
5
uint32_td=p[4];
6
returna|(b<<8)|(c<<16)|(d<<24);
7
}
Wird zu:
1
00000000 <test(unsigned char*)>:
2
0: f8d0 0001 ldr.w r0, [r0, #1]
3
4: 4770 bx lr
Das ist natürlich effizienter als mehrere einzelne Lesezugriffe, aber
nicht so effizient wie ein korrekt ausgerichteter Zugriff, und falls man
unaligend Zugriffe im Prozessor abgeschaltet hat, gibts einen Fault - in
dem Fall kann man dieses Verhalten mit -mno-unaligned-access abschalten.
Das zeigt also - mit diesem Code beschreibt man korrekt und portabel
einen Little-Endian-uint32_t -Zugriff in einem "raw" Array, welcher je
nach Plattform sogar optimiert wird. Kann er nicht optimiert werden,
funktioniert er dennoch richtig (dann hätte man ohnehin keine Wahl es
anders zu machen).
Dr. Sommer schrieb:> Andreas M. schrieb:>> Wenn dieser Raw Blob beim Deserialisieren vom Programmcode in Form von>> einzelnen Bytes eingelesen wird, dann benötige ich genau vier>> Bytezugriffe plus Schiebeoperations und OR's:> Na, wenn die 4 Bytes aufeinander folgen, sind 3 davon in einem Word, und> das restliche im vorherigen oder darauffolgenden Word. Bzw 2 und 2. Da> muss man nicht jedes Byte einzeln lesen, sondern kann mit LDRH 2 auf> einmal lesen.
Und woher genau soll der Compiler wissen, wenn er in einer Funktion ein
byte-pointer übergeben bekommt, ob dieser auf eine 0er, 1er 2er oder 3er
addresse zeigt? Das müsste zur Laufzeit geprüft werden und dann
entsprechend spezifischer code angesprungen werden. Das ist teurer als
einfach gleich byte-zugriffe zu machen.
>> Andreas M. schrieb:>> Ich weis nicht ob der Compiler bei modernen ARMs (M3/M4) die auch>> Zugriffe auf ungerade Addressen können aus dem ganzen Byte-Gewurstel da>> automatisch einen Load/Store draus macht> Das habe ich jetzt doch mal ausprobiert, und siehe da: Der GCC kann's!> Das:>
1
uint32_ttest(uint8_t*p){
2
>uint32_ta=p[1];
3
>uint32_tb=p[2];
4
>uint32_tc=p[3];
5
>uint32_td=p[4];
6
>returna|(b<<8)|(c<<16)|(d<<24);
7
>}
> Wird zu:>
1
00000000 <test(unsigned char*)>:
2
> 0: f8d0 0001 ldr.w r0, [r0, #1]
3
> 4: 4770 bx lr
> Das ist natürlich effizienter als mehrere einzelne Lesezugriffe, aber> nicht so effizient wie ein korrekt ausgerichteter Zugriff, und falls man> unaligend Zugriffe im Prozessor abgeschaltet hat, gibts einen Fault - in> dem Fall kann man dieses Verhalten mit -mno-unaligned-access abschalten.
Ja mit modernen Cortex M3/M4 funktioniert geht das so. Allerdings nicht,
wenn p auf "Device Memory" zeigt. Dort gehen solche Zugriffe
grundsätzlich nicht, egal ob in der CPU aktiviert oder nicht. In meinem
Anwendungsfall liegen die Ethernet Frames in genau solchem
Device-Memory, ich bin also auf Byte-Zugriff angewiesen, es sei denn ich
weis wo mein Basis-Zeiger liegt.
Andreas M. schrieb:> Und woher genau soll der Compiler wissen, wenn er in einer Funktion ein> byte-pointer übergeben bekommt, ob dieser auf eine 0er, 1er 2er oder 3er> addresse zeigt?
Ja bei einem Parameter gehts wohl nicht. Bei globalen Arrays mit
explizitem Alignment wäre es denkbar, aber das kann der GCC wohl auch
nicht.
Andreas M. schrieb:> Dort gehen solche Zugriffe> grundsätzlich nicht, egal ob in der CPU aktiviert oder nicht.
Ja das stimmt.
Andreas M. schrieb:> ich bin also auf Byte-Zugriff angewiesen
Genau. Der Zugriff per union/cast wäre aber gar nicht erst möglich,
daher ist der Zugriff mit einzelnen Bytes nicht ineffizienter. Also auch
hier kein Argument für union/cast. Oder was war nochmal deine Aussage?
Holm T. schrieb:> struct eeprom_val> {> uint8_t bla;> uint16_t fasel;> uint32_t foo;> int16_t bar;> }ee-val;
Vermutlich ee_val?
> Wie zur Hölle vereinbare ich nun eine union die die Struktur und ein> Array gleicher Länge enthält, die mir Zugriff auf den Speicherbereich> er Struktur ermöglicht?
Das brauchst du garnicht.
Alles was du für eeprom_read_block und eeprom_write_block brauchst ist
die Startadresse (&ee_val) sowie die Größe (sizeof(ee_val) oder
sizeif(struct eeprom_val)).
Wenn du die Init-Werte auf dem Host erzeugst, dann erzeug doch einfach
einen passenden Initializer. Das Host-Formal ist doch egal.
avr:
Holm T. schrieb:> Ich habe dann aber doppelte, einzeln zu wartende Definitionen für die> selbe Datenstruktur, genau das wollte ich vermeiden.
Du willst eine Struktur initialisieren und den Initializer auf einem PC
generieren. Oder du sprichst in Räteln...
Holm T. schrieb:> Da ist nicht viel Rätselhaftes dran. Da ich die Firmware für den Atmel> ja auch geschrieben habe, möchte ich die selben Header verwenden.
Kannst da ja auch, solchge der daraus generierte Coden nicht auf einem
AVR laufen soll. Wenn du Daten generierst, kannst du mit allem
moglichen Gezauber AVR-Kompatibilität herstellen, aber du kannst duch
genauso die gewünschten Initializer erzeugen. Eine Vorstellung, wie die
C-Objekte aussehen und welche Felder sie haben und wie sie initialisiert
werden sollen brauchst du doch ohnehin?
Johann L. schrieb:> Holm T. schrieb:>> Da ist nicht viel Rätselhaftes dran. Da ich die Firmware für den Atmel>> ja auch geschrieben habe, möchte ich die selben Header verwenden.>> Kannst da ja auch, solchge der daraus generierte Coden nicht auf einem> AVR laufen soll.
Das ist selbstverständlich.
> Wenn du Daten generierst, kannst du mit allem> moglichen Gezauber AVR-Kompatibilität herstellen, aber du kannst duch> genauso die gewünschten Initializer erzeugen. Eine Vorstellung, wie die> C-Objekte aussehen und welche Felder sie haben und wie sie initialisiert> werden sollen brauchst du doch ohnehin?
Ja.
Ich möchte nur die Datenstruktur an sich nur in einer Datei warten die
von beiden Compilern eingebunden wird. Die Unterschiede der beiden
Architekturen sind mir schon klar.
Gruß,
Holm