Forum: Mikrocontroller und Digitale Elektronik Datentypen mit Bitfelder


von WRNR (Gast)


Lesenswert?

Hallo.

Ich lese über SPI eine Bytefolge ein, die in etwa so aussieht:

Byte 1: 0bZZZZXXXX     (ZZZZ = Event, XXXX X-touch-point (11:8))
Byte 2: 0bXXXXXXXX     (X-touch-point (7:0))
Byte 3: 0bWWWWYYYY     (WWWW = ID, YYYY Y-touch-point (11:8))
Byte 4: 0bYYYYYYYY     (Y-touch-point (7:0))

Super wär es, direkt auf die Daten (Event, X, ID und Y) zugreifen zu 
können.
Folgende Datenstruktur hätte ich anfangs gedacht:
1
// data type for touch point
2
typedef union
3
{
4
    struct __attribute__ ((packed))
5
    {
6
        WORD event : 4;
7
        WORD xcor : 12;
8
        WORD id : 4;
9
        WORD ycor : 12;
10
    };
11
    BYTE raw[4];
12
} EP0430M06_TouchPoint;
Das geht aber nicht, da sich die Bits "falsch verteilen".
Auch wenn ich die Felder vertausche sind die Daten falsch:
1
// data type for touch point
2
typedef union
3
{
4
    struct __attribute__ ((packed))
5
    {
6
        WORD xcor : 12;
7
        WORD event : 4;
8
        WORD ycor : 12;
9
        WORD id : 4;
10
    };
11
    BYTE raw[4];
12
} EP0430M06_TouchPoint;

Kann man das irgendiwe in dieser Form lösen oder muss ich mir die Daten 
nach dem Einlesen aufbereiten?

lg

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

WRNR schrieb:
> Das geht aber nicht, da sich die Bits "falsch verteilen".

Probiere das doch einfach auf dem Trockenen mit Testdaten aus, dann 
kannst Du auch sehen, wo genau das Problem liegt.

Als Testdaten bietet sich die Bytefolge 0x12 0x34 0x56 0x78 an.

Wenn Deine Union korrekt funktioniert, müsste ja folgende Zuordnung 
herauskommen:

  event = 0x1
  xkor = 0x234
  id = 0x5
  ycor = 0x678

Also: Sieh Dir an, was rauskommt, wenn Du definierte Daten reinsteckst.

von WRNR (Gast)


Lesenswert?

Hab ich gerade getestet (2. Variante):

Raw-Daten: 0x12 0x34 0x56 0x78

Ergebnis:
Event = 0x03
X = 0x0412
ID = 0x07
Y = 0x856

Irgendwie ergibt das für mich noch keinen Sinn.
Binär sieht das dann so aus:

0001 0010  | 0011 0100    | 0101 0110 | 0111 1000
  x(7:0)   |  EV ,x(11:8) |  y(7:0)   |  ID , y(11:8)

Habe noch keine Idee wie ich das lösen kann...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> Hab ich gerade getestet (2. Variante):

Mach das nochmal mit der ersten.

von Karl H. (kbuchegg)


Lesenswert?

WRNR schrieb:
> Hab ich gerade getestet (2. Variante):
>
> Raw-Daten: 0x12 0x34 0x56 0x78
>
> Ergebnis:
> Event = 0x03
> X = 0x0412
> ID = 0x07
> Y = 0x856
>
> Irgendwie ergibt das für mich noch keinen Sinn.


mach mal aus dem WORD ein BYTE

Da hat dich der Byte-Sex gebissen.
Die Reihenfolge der Bytes in einem Word ist nicht definiert und bei dir 
offensichtlich anders als das wovon du ausgehst.

von WRNR (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> mach mal aus dem WORD ein BYTE
>
> Da hat dich der Byte-Sex gebissen

Geht nicht, da X und Y 12 bit sind.

Erste Variante:

Event = 0x02
X = 0x0341
ID = 0x06
Y = 0x785

von Karl H. (kbuchegg)


Lesenswert?

WRNR schrieb:
> Karl Heinz Buchegger schrieb:
>> mach mal aus dem WORD ein BYTE
>>
>> Da hat dich der Byte-Sex gebissen
>
> Geht nicht, da X und Y 12 bit sind.

Yep. Habs auch schon gemerkt

Dann musst du das in deinen Zuweisungen zum Byte Array berücksichtigen 
und entsprechend "gegensteuern"

  raw[1] = 0x12
  raw[0] = 0x34
  raw[3] = 0x56
  raw[2] = 0x78


Aber:
Ist das wirklich so ein Problem sich eine Funktion zu schreiben, die das 
Bitschieben erledigt? Um das Geschiebe kommst du sowieso nicht rum. Ob 
du das jetzt ausprogrammierst, oder ob es der Compiler macht ist für den 
µC einerlei. Die Arbeit hat er auf jeden Fall

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Nochmal genauer hingesehen:

> Hab ich gerade getestet (2. Variante):
>
> Raw-Daten: 0x12 0x34 0x56 0x78
>
> Ergebnis:
> Event = 0x03
> X = 0x0412
> ID = 0x07
> Y = 0x856


Du musst also, bevor Du Deine union befüllst, die Bytes tauschen, das 
erste mit dem zweiten und das dritte mit dem vierten:

Raw-Daten:   0x12 0x34 0x56 0x78

Nach Tausch: 0x34 0x12 0x78 0x58

Und Dein Ergebnis sollte

Event = 0x1
X = 0x0234
Id = 0x5
Y = 0x0678

sein.

von WRNR (Gast)


Lesenswert?

OK.

Der PIC32 ist Little Endian. Daran habe ich natürlich überhaupt nicht 
gedacht. Bei Big Endian hätte ich bei Variante 2 kein problem gehabt und 
ich wär ohne Aufbereitung davon gekommen.

Danke für den Tipp mit der Byteorder.

Vielen Dank für die flotten Antworten

von Karl H. (kbuchegg)


Lesenswert?

Da du zwischen Byte 1 und Byte 2 eine Trennung hast, müsste auch das 
funktionieren
1
typedef union
2
{
3
    struct __attribute__ ((packed))
4
    {
5
        WORD event : 4;
6
        WORD xcor : 12;
7
        WORD id : 4;
8
        WORD ycor : 12;
9
    };
10
    WORD raw[2];
11
} EP0430M06_TouchPoint;
12
13
14
  raw[0] = Byte[0] << 8 | Byte[1];
15
  raw[1] = Byte[2] << 8 | Byte[3];

dann löst der Compiler die Byteorder richtig auf. Das 8 Bit Geschiebe 
bzw. die Verorderung sollte ein guter Compiler eigentlich wegoptimieren 
und auf Direktzugriffe umändern.

von Fabian O. (xfr)


Lesenswert?

Die Anordnung der Bits in einem Bitfeld ist im C-Standard überhaupt 
nicht definiert. Man kann es sicher irgendwie hinfrickeln, dass es mit 
dem verwendeten Compiler zusammen klappt, aber portabel ist das nicht.

Ich würde die Werte lieber von Hand per Bitschieben und Ausmaskieren 
zusammenbauen. Das funktioniert immer und ist bei einem guten Compiler 
auch nicht langsamer. Eventuell ist es sogar schneller, wenn man nicht 
vorher die Bytereihenfolge in einem Extra-Schritt tauschen muss.

von Karl H. (kbuchegg)


Lesenswert?

Fabian O. schrieb:
> Die Anordnung der Bits in einem Bitfeld ist im C-Standard überhaupt
> nicht definiert.

Die Reihenfolge der Bits ist schon definiert.
Was nicht definiert ist ist, wie sich die Bytes eines Word im Speicher 
abbilden.

> Man kann es sicher irgendwie hinfrickeln, dass es mit
> dem verwendeten Compiler zusammen klappt, aber portabel ist das nicht.

Das stimmt.

Und genau genommen ist auch der Zugriff in eine Union über 2 
verschiedene Member nicht erlaubt. Der Standard sagt, dass man nur dann 
definierte Ergebnisse bekommt, wenn man über denselben Member liest, 
über den man auch geschrieben hat. Allerdings ist die Sache so, dass es 
sich hier um sowas wie die 'normative Kraft des Faktischen' handelt. 
Soll heißen, dass selbst die Leute die den C Standard machen, diese 
Technik akzeptieren (und in diversen Newsgroups auch propagierten). Sie 
können das nicht in den Standard schreiben, weil sie dann eine Byteorder 
vorschreiben müssten, was sie nicht tun wollen. Aber die Union Technik 
funktioniert seit Anbeginn der C Zeit und es ist kein Compiler bekannt 
auf dem sie nicht (mit Ausnahme der Byte-Order) funktionieren würde.

von Fabian O. (xfr)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Die Reihenfolge der Bits ist schon definiert.

Der C99-Standard sagt dazu:

An implementation may allocate any addressable storage unit large enough 
to hold a bitfield. If enough space remains, a bit-field that 
immediately follows another bit-field in a structure shall be packed 
into adjacent bits of the same unit. If insufficient space remains, 
whether a bit-field that does not fit is put into the next unit or 
overlaps adjacent units is implementation-defined. The order of 
allocation of bit-fields within a unit (high-order to low-order or 
low-order to high-order) is implementation-defined. The alignment of the 
addressable storage unit is unspecified.
(§ 6.7.2.1 Abs. 10)

Und dann ist auch noch nicht mal garantiert, dass das mit dem Typ WORD 
(schätzungsweise ein Typedef für unsigned short?) überhaupt 
funktioniert:

A bit-field shall have a type that is a qualified or unqualified version 
of _Bool, signed int, unsigned int, or some other implementation-defined 
type.
(§ 6.7.2.1 Abs. 4)

Also alles sehr wacklig. Das einzige, worauf man sich gemäß Standard 
wirklich verlassen kann, ist, dass man eine reingeschriebenes Element 
wieder über den gleichen Namen auslesen kann. Im Prinzip wie bei der 
Union ...

von Karl H. (kbuchegg)


Lesenswert?

Fabian O. schrieb:
> Karl Heinz Buchegger schrieb:
>> Die Reihenfolge der Bits ist schon definiert.
>
> Der C99-Standard sagt dazu:

Vielen Dank für deine Mühe.
Das hatte ich dann wohl komplett falsch in Erinnerung.

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.