Forum: Compiler & IDEs Bitfelder Byte-weise auslesen


von W. M. (muhh)


Lesenswert?

Hey Leute,

ich muss über CAN Daten schicken. Leider sind die Informationen Byte 
übergreifend, d.h. es gibt z.B. Daten die bei Byte 0 Bit 6 beginnen und 
bis Byte 1 Bit 3 gehen. Das Bitfeld würde mir die möglichkeit geben, die 
Daten strukturiert einzutragen und sie dann aneinanderstehen auszulesen. 
Leider habe ich noch keine Möglichkeit gefunden, wie ich das Bitfeld 
Byte-weise auslesen kann um meinen CAN-Frame mit Daten zu füllen.

Wisst ihr wie man sowas machen kann?

Viele Grüße

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Na lies das Bitfeld eben aus, der Compiler bastelt den Wert schon 
korrekt zusammen (abhängig vom ABI der jeweiligen Architektur).

von René B. (reneb)


Lesenswert?

Falls du mit C arbeitest:
Sortier es halt passend in eine Bit-Struktur:
struct blala
{ int B0:1; int B1:1; ... }
und definier dir einen unsigned char-union über die Struktur (union). Da 
fummelt dir der Compiler alles so zusammen, dass du es mit normalen 
Byte-Operationen überziehen kannst.

von W. M. (muhh)


Lesenswert?

Johann L. schrieb:
> Na lies das Bitfeld eben aus, der Compiler bastelt den Wert schon
> korrekt zusammen (abhängig vom ABI der jeweiligen Architektur).

Danke für den Tipp, wenn ich wüsste wie, hätte ich nicht gefragt ;)

René B. schrieb:
> Falls du mit C arbeitest:

Ja, tue ich.

> Sortier es halt passend in eine Bit-Struktur:
> struct blala
> { int B0:1; int B1:1; ... }
> und definier dir einen unsigned char-union über die Struktur (union). Da
> fummelt dir der Compiler alles so zusammen, dass du es mit normalen
> Byte-Operationen überziehen kannst.
1
union CANFeld{
2
  struct
3
  {
4
    uint8_t bit1 :  6;
5
    uint8_t bit2 :  7;
6
    uint8_t bit3 :  1;
7
  } BitFeld;
8
};

Aber wie greife ich nun mit Bit-Operationen darauf zu, sodass ich z.B. 
die Variable uint8_t test mit den ersten 8 bit der union fülle? Hab noch 
keine Möglichkeit gefunden. Was ich nicht möchte ist das hier:
1
test = CanFeld.BitFeld.bit1 | (CANFeld.BitFeld.bit2<<6);

von Steffen R. (steffen_rose)


Lesenswert?

Du hast doch schon mit dem union angefangen. Nun fehlt parallel zur 
Struktur noch dein Byte array.

Allerdings ist deine Lösung arg architekturabhängig.
Die Shiftoperationen sind allgemeingültig.

Wenn dein Prozessor keine Bitoperationen unterstützt kann es sogar 
bedeuten, dass deine Bitoperationen mehr Code erzeugen.

von W. M. (muhh)


Lesenswert?

Steffen Rose schrieb:
> Du hast doch schon mit dem union angefangen. Nun fehlt parallel zur
> Struktur noch dein Byte array.

Okay, also ich hatte mir vorgestellt, das Ganze auch in eine Struktur zu 
verpacken. Nochmal der Code für die Union und mein CAN-Struct zusammen. 
Ist jetzt erstmal eine Rohfassung, aber es geht auch einfach eher darum, 
die Daten irgendwie sortiert zu kriegen.
1
union CANFeld {
2
  struct
3
  {
4
    uint8_t bit1 :  6;
5
    uint8_t bit2 :  7;
6
    uint8_t bit3 :  1;
7
  } BitFeld;
8
};
9
10
struct CANFrame{
11
  uint32_t ID;
12
  uint8_t DLC;
13
  uint8_t Byte0;
14
  uint8_t Byte1;
15
  uint8_t Byte2;
16
  uint8_t Byte3;
17
  uint8_t Byte4;
18
  uint8_t Byte5;
19
  uint8_t Byte6;
20
  uint8_t Byte7;
21
} CAN_Msg;

Den DLC (DataLengthCode) und ID (Identifier) werde ich selbst schreiben, 
die Bytes sollen jetzt mit den Daten aus der union CANFELD gefüllt 
werden. Das heißt ich schreibe die ersten 8bit der union in 
CAN_Msg.Byte0, die zweiten 8bit in Byte1 usw.
Da fehlt mir jetzt jeglicher Ansatz. Kann ich da mit Pointern arbeiten?

Ich habe das mal mit Strukturen probiert, allerdings hat man dort das 
Problem, dass die Datentypen nicht übereinstimmen, der Compiler gibt 
hier eine Fehlermeldung aus.

von Karl H. (kbuchegg)


Lesenswert?

Willy M. schrieb:

>
1
> union CANFeld {
2
>   struct
3
>   {
4
>     uint8_t bit1 :  6;
5
>     uint8_t bit2 :  7;
6
>     uint8_t bit3 :  1;
7
>   } BitFeld;
8
> };
9
>

eine union mit nur einem Member ist sinnlos. Wozu soll das gut sein.
Der Trick bei einer union besteht doch genau darin, dass
1
union a {
2
  int m1;
3
  long m2;
4
};
die beiden Mitglieder der union (m1 und m2) in genau demselben Speicher 
angelegt werden. Die beiden (können auch mehr sein) teilen sich 
praktisch denselben Speicher. Es ist, wie wenn du 2 Schablonen aus Folie 
über den Speicher legst, auf der einen steht m1 (mitsamt der Zuordnung 
der Bytes zu einem int) und auf der anderen steht m2 (ebenfalls mitsamt 
der Zuordung derselben Bytes zu diesmal einem long). Das ein long mehr 
Bytes im Speicher braucht als ein int, tangiert dich nur insofern, als 
dann eben bei m1 nicht der komplette für die union reservierte Speicher 
benutzt wird, sondern nur Auszüge davon. Die union ist immer genau so 
groß, wie der größte Member, derer die übereinander gelegt werden.

du hast eine
1
union a {
2
  struct irgendwas
3
};
mit wem teilt sich denn diese Struct denselben Speicher? Das gibt es ja 
keinen 2.ten Member.

Du könntest
1
union a {
2
  struct irgendwas;
3
  unsigned char bytes[länge];
4
};

dann teilt sich die struct mit dem Array denselben Speicher. De facto 
hast du damit 2 Zugriffspfade in den Speicher geschaffen. In dem einen, 
über die struct, hast du dem Speicher eine Struktur übergestülpt, in der 
du über einem Problem angepasste logische Namen auf den Speicher 
zugreifen kannst. Im andern Teil der union fasst du genau denselben 
Speicher einfach nur als Array von unsigned char (also Bytes) auf - 
jegliche logische Einteilung (deshalb hat man sich ja eine struct 
gebaut) bzw. Aufteilung derselben Speicherplätze ist nicht mehr 
vorhanden.


> Da fehlt mir jetzt jeglicher Ansatz. Kann ich da mit Pointern arbeiten?

Kann man auch.

> Ich habe das mal mit Strukturen probiert, allerdings hat man dort das
> Problem, dass die Datentypen nicht übereinstimmen, der Compiler gibt
> hier eine Fehlermeldung aus.

Wenn man weiß was man tut und sich sicher ist, das richtig zu machen, 
dann kann man den Compiler mit einem Cast ruhig stellen. Aber dann muss 
man sicher sein, auch wirklich zu wissen was man tut.

von W. M. (muhh)


Lesenswert?

Hmmm... okay, dann ist meine union vielleicht gar nicht nötig? Ich habe 
das wegen diesem Beitrag in eine Union verpackt:

René B. schrieb:
> Sortier es halt passend in eine Bit-Struktur:
> struct blala
> { int B0:1; int B1:1; ... }
> und definier dir einen unsigned char-union über die Struktur (union)

Was ich will ist, das Bitfeld Byteweise mit hintereinanderstehenden 
Daten in meine einzelnen Bytes der CAN-Message zu übertragen.

Mal ein anderes Beispiel. Ich möchte auch hier mein uint8_t byte1 mit 
den ersten 8bits aus meiner Struktur füllen. Folgenden Code habe ich 
dazu geschrieben:
1
struct test
2
  {
3
    uint8_t bit1 :  6;
4
    uint8_t bit2 :  7;
5
    uint8_t bit3 :  1;
6
  };
7
8
int main(void)
9
{
10
  struct test BitFeld;
11
  struct test *Ptr;
12
13
  Ptr = &BitFeld;
14
15
  BitFeld.bit1 = 0x2A;
16
  BitFeld.bit2 = 0x2A;
17
18
  uint8_t byte1;
19
20
  for (int i = 0; i < 8; ++i)
21
  {
22
    byte1 |= ((uint8_t) Ptr[i])<<i;
23
  }
24
25
  printf("%d\n", byte1);
26
27
  return 0;
28
}

Folgende Fehlermeldung erhält man:
1
error: invalid cast from type 'test' to type 'uint8_t {aka unsigned char}'
2
   byte1 |= (uint8_t) Ptr[i];

Zumal dieser Code vermutlich nicht das machen wird, was ich möchte. Kann 
ich mit dem Index des Pointers auf die einzelnen Bits zugreifen? 
Vermutlich springt er nicht nur ein Bit weiter, sondern um die Größe der 
struct test...

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

>> Da fehlt mir jetzt jeglicher Ansatz. Kann ich da mit Pointern arbeiten?
>
> Kann man auch.

Aber bei dir brauchts das eigentlich gar nicht, schon alleine deshalb 
weil du in CANFrame kein Array hast (was ich sowieso nicht so recht 
verstehe. welchen Sinn hat es Variablen durchzunummerieren anstelle von 
gleich einem Array? Verkomplizert doch nur alles, weil man nicht 
ordentlich und vernünftig mit Schleifen arbeiten kann. durchnummerierte 
Variablen, die alle vom selben Datentyp sind, sind IMHO ein 
Designfehler. Eigentlich sollte das ein Array sein.
1
struct CanBits
2
{
3
  uint8_t bit1 :  6;
4
  uint8_t bit2 :  7;
5
  uint8_t bit3 :  1;
6
};
7
8
union CANFeld {
9
  struct CanBits BitField;
10
  uint8_t        Bytes[ sizeof struct CanBits ];
11
};
12
13
struct CANFrame{
14
  uint32_t ID;
15
  uint8_t DLC;
16
  uint8_t Byte0;
17
  uint8_t Byte1;
18
  uint8_t Byte2;
19
  uint8_t Byte3;
20
  uint8_t Byte4;
21
  uint8_t Byte5;
22
  uint8_t Byte6;
23
  uint8_t Byte7;
24
};
25
26
...
27
union CANField values;
28
struct CANFrame Msg;
29
30
  // befüllen über die Struktur.
31
  // Der Compiler kümmert sich darum, dass die Bits an die richtigen
32
  // Stellen in der Struktur kommen
33
  values.BitField.bit1 = 4;
34
  values.BitField.bit2 = 8;
35
  values.BitField.bit3 = 1;
36
37
  // übertragen in den CANFrame, diesmal über den anderen Pfad der union
38
  // zugreifen, in dem ganz einfach auf die Bytes im Array zugegriffen wird.
39
  Msg.Byte0 = values.Bytes[0];
40
  Msg.Byte1 = values.Bytes[1];
41
...

von Karl H. (kbuchegg)


Lesenswert?

Willy M. schrieb:

> Was ich will

Was du eigentlich wollen solltest, ist erst mal ein C-Buch 
durcharbeiten!


> ... Vermutlich ...

Basierend auf 'vermutlich' kann man nicht programmieren. Entweder man 
kennt seine Sprache oder man kennt sie nicht. Wenn man sie nicht kennt, 
dann muss man sie eben lernen. Dazu gibt es LIteratur.

Aber das hab ich dir mitlerweile schon 15 mal gesagt. Warum glaubst du 
es denn nicht?

von W. M. (muhh)


Lesenswert?

Oh man... Jetzt habe ich es glaube ich verstanden. Steht ja auch in den 
Kommentaren, aber ich möchhte das nochmal in eigene Worte fassen.

Man definiert sich eine Union mit einem Bitfeld und einem Array mit 
Bytes. Man beschreibt nun das Bitfeld mit den entsprechenden Daten, 
liest sie aber über das Array aus. Was dann passiert ist, dass die Daten 
des Bitfeldes anders Interpretiert werden, d.h. in Bytes gefasst werden, 
auf die ich nun zugreifen kann.

Ist das so richtig?
Das ist natürlich genial, aber ich wäre niieeeeee darauf gekommen. Danke 
vielmals für deine Bemühungen, du hast mir sehr weitergeholfen :) Danke 
natürlich auch an die anderen :)



Viele Grüße
Willy

EDIT:
Karl Heinz schrieb:
>> Was ich will
>
> Was du eigentlich wollen solltest, ist erst mal ein C-Buch
> durcharbeiten!
>
>> ... Vermutlich ...
>
> Basierend auf 'vermutlich' kann man nicht programmieren. Entweder man
> kennt seine Sprache oder man kennt sie nicht. Wenn man sie nicht kennt,
> dann muss man sie eben lernen. Dazu gibt es LIteratur.
>
> Aber das hab ich dir mitlerweile schon 15 mal gesagt. Warum glaubst du
> es denn nicht?

Die Antwort war vor deinem, ich hab bloß solange gebraucht den zu 
schreiben, deswegen hatte ich deinen nicht gelesen, danach konnte ich 
den Beitrag leider nicht mehr löschen ;) Nochmals vielen Dank :)

von Karl H. (kbuchegg)


Lesenswert?

Willy M. schrieb:

>     byte1 |= ((uint8_t) Ptr[i])<<i;
1
      byte1 = ((uint8_t*)Ptr)[0];
2
      byte2 = ((uint8_t*)Ptr)[1];

oder gleich den ständigen Cast vorziehen, indem man Ptr als einen 
'Pointer auf Byte' einrichtet.
1
int main(void)
2
{
3
  struct test BitFeld;
4
  uint8_t    *Ptr;
5
6
  Ptr = (uint8_t*)&BitFeld;
7
8
  BitFeld.bit1 = 0x2A;
9
  BitFeld.bit2 = 0x2A;
10
11
  byte1 = Ptr[0];
12
  byte2 = Ptr[1];
13
  byte3 = Ptr[2];
14
  ...

oder eben die union Variante.

Alle Methoden sind letzten Endes gleichwertig mit genau dem gleichen 
Problem.

von W. M. (muhh)


Lesenswert?

So, ich habe das jetzt nochmal in eine c-Datei gepackt, damit ich damit 
etwas herumspielen kann.
1
#include <stdio.h>
2
#include <stdint.h>
3
4
  struct CANBits {
5
    uint8_t bit1 :  6;
6
    uint8_t bit2 :  7;
7
    uint8_t bit3 :  1;
8
  };
9
10
  union CANFeld {
11
    struct CANBits   BitFeld;
12
    uint8_t      Bytes[sizeof(struct CANBits)];
13
  };
14
15
int main(void)
16
{
17
  
18
  union CANFeld test;
19
20
  test.BitFeld.bit1 = 0x2A;  // 0010 1010
21
  test.BitFeld.bit2 = 0x21;  // 0010 0001
22
  test.BitFeld.bit3 = 0;
23
24
  uint8_t byte1 = test.Bytes[0];
25
26
  printf("%x\n", byte1);
27
  return 0;
28
}

Erwartete Ausgabe: 6a       (0110 1010)
Bekommene Ausgabe: ea       (1110 1010)

Das gleiche bekomme ich heraus, wenn ich schreibe
1
test.BitFeld.bit2 = 0x2A;

Woran könnte das liegen?

von tictactoe (Gast)


Lesenswert?

Das könnte daran liegen, dass deine struct nicht gepackt ist, weil du 
als Datentyp uint8_t verwendest. Vielleicht gehts damit besser:
1
struct CANBits {
2
    uint16_t bit1 :  6;
3
    uint16_t bit2 :  7;
4
    uint16_t bit3 :  1;
5
    uint16_t padding : 2;
6
};
Damit die letzten 2 Bits auf einen kontrollierbaren Wert (z.B. Null) 
setzen kannst, habe ich dir noch einen Member padding dazugeschrieben.

von W. M. (muhh)


Lesenswert?

Was mein Compiler tut ist, dass er die Daten an den Anfang eines neuen 
Bytes schreibt, wenn der Inhalt der Variable des Bitfelds nicht mehr in 
das Restbyte passt.

Beispiel:
1
struct CANBits {
2
  uint8_t bit1 :  6;
3
  uint8_t bit2 :  3;
4
  uint8_t bit3 :  6;
5
};
6
7
...
8
9
test.BitFeld.bit1 = 0x7;  // 00 0111
10
test.BitFeld.bit2 = 0x3;  // 011
11
test.BitFeld.bit3 = 0;          // 00 0000
12
13
// Ausgabe
14
15
sizeof: 3
16
Byte1:  7  // eigentlich sollte es so aussehen: 011|00 0111
17
Byte2:  3  // da das letzte bit aber nicht mehr ins Byte reinpasst
18
Byte3:  0  // wird es an den Anfang des nächsten Bytes geschrieben

Nun habe ich wieder das Problem, dass ich die Daten nicht Byte 
übergreifend schreiben kann. Weiß jemand, ob man den Compiler da 
austricksen kann?

von Rolf Magnus (Gast)


Lesenswert?

Willy M. schrieb:
> Hmmm... okay, dann ist meine union vielleicht gar nicht nötig?

Ich wüßte auch nicht, wozu man hier extra eine zusätzliche union 
brauchen sollte, abgesehen davon, daß unions dafür auch gar nicht 
gedacht sind.

Willy M. schrieb:
> Was mein Compiler tut ist, dass er die Daten an den Anfang eines neuen
> Bytes schreibt, wenn der Inhalt der Variable des Bitfelds nicht mehr in
> das Restbyte passt.

Kein Wunder. Du beschränkst es ja selbst auf 8 Bit, indem du als Typ 
uint8_t angibst.

von Markus F. (mfro)


Lesenswert?

Willy M. schrieb:
> Man definiert sich eine Union mit einem Bitfeld und einem Array mit
> Bytes. Man beschreibt nun das Bitfeld mit den entsprechenden Daten,
> liest sie aber über das Array aus. Was dann passiert ist, dass die Daten
> des Bitfeldes anders Interpretiert werden, d.h. in Bytes gefasst werden,
> auf die ich nun zugreifen kann.
>
> Ist das so richtig?
> Das ist natürlich genial,

Das ist tatsächlich einigermaßen genial, bloß alles andere als portabel.

Leider erlaubt der C-Standard den Compilern alle Arten von Freiheiten, 
was die Implementierung von Bitfeldern angeht (insbesondere packing, 
alignment und byte order) und was mit einem Compiler funktioniert, kann 
mit einem anderen ein völlig anderes Ergebnis liefern.

Wenn Du sicher gehen willst, bleibt nichts anderes als die Bytes "zu 
Fuß" zusammenzupfriemeln.

von Ein (Gast)


Lesenswert?

Markus F. schrieb:
> Wenn Du sicher gehen willst, bleibt nichts anderes als die Bytes "zu
> Fuß" zusammenzupfriemeln.

Es gibt nun mal zwei Welten: Big- und Little Endian.

Und die tolle Programmiersprache C kommt auch nach 40 Jahren nicht damit 
zurecht ...

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.