Hallo Gemeinde,
vielleicht zunächst vorweg: Die Frage dreht sich nicht um den GCC
sondern den Keil C51 Compiler...
Ich habe ein umfangreiches struct mit Bitfeldern und einer Gesamtgröße
von 10 Byte.
1
typedefstruct{
2
unsignedintheader1:4;
3
unsignedintheader2:4;
4
unsignedintdata1:4;
5
unsignedintparity:1;
6
unsignedintstate:1;
7
8
...usw.
9
10
}SensorFrame;
So habe komfortabel Zugriff auf die einzelnen Bestandteile des
Datenpaketes.
Für die Übertragung dieses Paketes benötige ich aber jedes einzelne Bit.
Theoretisch würde ich also gern sowas machen, wie:
1
for(inti=0;i<sizeof(SensorFrame);i++){
2
3
currentBit=((frame>>i)&0x01);
4
5
...
6
}
Aber das geht afaik nicht, weil nicht garantiert ist, dass die Daten des
struct wirklich lückenlos im Speicher hintereinanderliegen.
Hat jemand eine Idee, wie einerseits eine komplexe Datenstruktur
aufbauen kann, die einfach zu handhaben ist und andererseits die
möglichkeit bietet, schnell alle bits von vorn bis hinten
durchzublättern?
Würde mich über eure Vorschläge und Ideen sehr freuen.
Danke, Andre´
Andre Duecke schrieb:> Aber das geht afaik nicht, weil nicht garantiert ist, dass die Daten des> struct wirklich lückenlos im Speicher hintereinanderliegen.
Wenn sizeof (SensorFrame) * 8 == Anszal Bits ist, dann müssen sie
lückenlos im Speicher stehen.
Jim Meba schrieb:> Andre Duecke schrieb:> Aber das geht afaik nicht, weil nicht garantiert ist, dass die Daten des> struct wirklich lückenlos im Speicher hintereinanderliegen.>> Wenn sizeof (SensorFrame) * 8 == Anszal Bits ist, dann müssen sie> lückenlos im Speicher stehen.
sizeof liefert die Größe in Byte, was soll da raus kommen?
Bei Keil gibt es "packed" um das alignment selber zu steuern. Das ist in
der Hilfe gut erklärt.
Hallo ihr,
vielen Dank schonmal.
Sorry wegen der for-Schleife. Stimmt natürlich - sizeof() liefert die
Anzahl der Bytes - nicht der Bits.
Im echten Code soll die Struktur per Interrupt bit für bit durchlaufen
werden. Da gibt es dann diese for-Schleife nicht, sondern eine globale
Variable, die von der ISR verwendet und jeweils
hochgezählt/zurückgesetzt wird.
Ich habe mir __packed mal angesehen, konnte es aber noch nicht testen,
da ich noch ein weiteres Problem habe.
wenn ich nämlich z.B. sowas mache:
1
typedefstruct{
2
unsignedintheader1:4;
3
unsignedintheader2:4;
4
unsignedintdata1:4;
5
unsignedintparity:1;
6
unsignedintstate:1;
7
}SensorFrame;
8
9
SensorFramef;
10
11
bittest=((f>>12)&0x01);
Dann erhalte ich den Compilerfehler: ">>: bad operand type".
Scheinbar kann ich auf eine Struktur keine bitshift-Operationen
anwenden.
Hat da vielleicht noch jemand einen Tip, wie man es stattdessen machen
könnte?
Vielen Dank,
Andre´
Andre Duecke schrieb:> Hat da vielleicht noch jemand einen Tip, wie man es stattdessen machen> könnte?
einfach die struct per memcopy in ein Byte Array kopieren und die Bytes
bitsweise versenden.
Die übliche Methode für solche und ähnliche Fälle beruht darauf, dass
packed structs sich in der (Faust-)Regel (d.h. die meisten Compiler
machen das seit Jahren reproduzierbar so) 1:1 ohne Informationsverlust
in ein Byte-Array casten lassen. Allerdings ist das vom Standard nicht
garantiert, so dass man bei neuen Compilern immer mal checken sollte, ob
das geht. In den letzten Jahrzehnten sind mir von der "Regel" keine
Abweichungen vorgekommen - aber "Shit happens" :-)
Das Problem zur Compile- oder Laufzeit die Größe in Bits festzustellen
lässt sich programmatisch nicht ausschliesslich unter Verwendung von
Präprozessor und Compiler lösen, da die geringste Auflösung das Byte
ist. Da musst Du #define verwenden (oder ein Äquivalent).
In gewisser Weise ist das "Pfusch". Das ist richtig. Wie gesagt, deckt
der Standard diesen Cast nicht ab (das Verhalten ist undefiniert, d.h.
jeder Compiler kann das anders machen oder sogar verweigern.)
Aber interessant ist auch, dass dieses Verhalten seit Jahrzehnten von
vielen Compilern durchgängig so implementiert ist. Der Fall tritt
einfach sehr häufig auf.
Es gibt natürlich Methoden das Standardkonform zu implementieren. Dazu
wird jedes Mitglied mit seiner Grösse einzeln berücksichtigt und heraus
geschoben. Soviel ist klar. Kann man so machen. Ob man es sollte ist
kontrovers.
Eine Ausnahme sind Projekte, in denen MISRA oder ähnliche Regelwerke
angewendet werden sollen. Dort kann der "Pfusch" verboten sein.
Eine endgültige oder allgemeingültige Aussage dazu ist schwierig. Viele
Profis machen das so. Die "Erbsenzähler" haben aber ebenso recht es zu
vermeiden.
Sind wieder alle Leute schlau hier und kennen sich mit dem Standard aus
und was nicht.... Konzentriert ihr euch mal auf das Niederregnen von
Worst-Case-Fällen mit eurem mega Fachwissen, und ignoriert die
eigentliche Lösung:
u n i o n
wieviel der Software wird letztlich auf andere Platformen Portiert oder
werden jemals von einem Anderen compiler übersetzt... im Hobby- und
Hardwarenahen bereich ist das doch schon eher ein spezialfall - gerade
bei jemanden der an soeinem Problem scheitert. Es ist SINNLOSE
Wichtigtuerei hier anzufangen sich zu beschweren welch schlechte Manier
und wie unsicher es ist, die Struktur auf ein Char-Array zu casten und
dann zu verarbeiten... wenn es funktioniert, ist es für dieses Projekt
gut genug.
Ob es funktioniert? 2min Debuggen.. fertig. Man kann aber auch weiterhin
stundenlang drüber diskutieren. Hebt das Ego diverser Leute.. bringt
aber NULL.
Habt spaß.
Was kommt denn da für ne Flachpumpe aus der Ecke?
Eine union ist der gleiche Pfusch wie ein cast.
@Klaus, wer benutzt Misra und warum macht der das? :-P
Jetzt will ich aber auch mal mitflamen: warum ist Union denn so schlecht
hier an dieser Stelle?
Ich denke, eine Union ist ein hervorragendes Mittel um Binärdaten zu
verwursten und kann, richtig und vernünftig eingesetzt, wirklich fast
Wunderdinge bei Binärprotokollen bewirken, gerade durch Schachtelungen
(siehe BSD Sockets). Sobald man solche Protokolle crunchen muss, ist man
mit einer sauberen Strukturierung durch Unions gegenüber einer
std::bitset<> überlegen, einfach weil die Union das Parsen halb für
einen übernimmt und zusätzlich noch gescheite Strukturierung schafft.
Gerade beim eventuellen späteren Debuggen von Binärdaten ist das 'ne
Offenbarung gegenüber Lösungen per Cast.
In C++ ist aufgrund der unklaren Typisierung ist für Einsteiger
natürlich die Verwendung von std::bitset<> anzuraten, für C würde ich
auf jeden Fall die Lösung per Union bevorzugen.
Sorry, das oberste 'struct' ist durch 'union' zu ersetzen:
typedef union
{
unsigned int Value;
struct
{
unsigned int header1 :4;
unsigned int header2 :4;
unsigned int data1 :4;
unsigned int parity :1;
unsigned int state :1;
//...usw. auf 32Bit aligned
} Bits;
}
} SensorFrame;
SensorFrame blub;
blub.Value = 0x30;
blub.Value = blub.Value << 2;
blub.Bits.data1 = 23;