Forum: Compiler & IDEs Struct in Byte


von Mathiable (Gast)


Lesenswert?

Guten Tag

Ich habe einen Struct mit mehreren Bit Variabeln und muss diese dann via 
I2C an einen anderen MyC weiter senden. Ist es nun möglich einen Struct 
Byte weise auszulesen? Habe beispielsweise so etwas:

1
struct  {
2
3
  unsigned X1:2;
4
  unsigned X2:2;
5
  unsigned X3:2;
6
7
  unsigned iO1:1;
8
  unsigned iO2:1;
9
  unsigned iO3:1;  
10
11
  unsigned Fehler:4;
12
13
  unsigned Auf:1;
14
  unsigned Zu :1;
15
16
  unsigned On :1;
17
18
}IO;

das ergäbe 2 Byte. Kann ich die nun direkt weiter senden oder komme ich 
um das zusammen fassen in einen Char nicht herum? Die Daten währen so 
nämlich aktueller als wenn ich sie ab und zu zusammen fasse.

Vielen Dank schon mal...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Du kannst eine union drüberlegen, die als alternativen (zur struct)
Datentyp einen Array von zwei uint8_t enthält.  Solange Quell- und
Zielmaschine die gleiche Architektur haben (byte order, alignment),
funktioniert diese Vorgehensweise.

Vorsicht: das bloße Hinschreiben eines "unsigned" als Datentyp
impliziert ein "unsigned int".  Je nachdem, wie groß ein unsigned
int auf deiner Maschine ist, kann das sich ergebende Bitfeld
(implementierungsabhängig) dabei auch mehr als nur 16 bit groß
sein.  Besser ist es, den Datentyp so groß zu machen, wie er
wirklich notwendig ist, um die Anzahl von Bits zu fassen, bei
dir also "uint8_t" statt "unsigned" zu schreiben.  Dann sollte
der Compiler versuchen, die gesamte struct in "uint8_t" zu fassen,
und wenn das nicht genügt (wie hier) mit dem nächst größeren
fortzufahren.

von Mathiable (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Du kannst eine union drüberlegen, die als alternativen (zur struct)
> Datentyp einen Array von zwei uint8_t enthält.  Solange Quell- und
> Zielmaschine die gleiche Architektur haben (byte order, alignment),
> funktioniert diese Vorgehensweise.


Vielen Dank für die Antwort. Union war schon mal ein gutes Stichwort. 
Beim groben Informieren ist dann das heraus gekommen:

1
union
2
{
3
  uint8_t wort1;
4
  struct  {
5
6
    uint8_t X1:2;
7
    uint8_t X2:2;
8
    uint8_t X3:2;
9
10
    uint8_t iO1:1;
11
    uint8_t iO2:1;
12
  }
13
  
14
  uint8_t wort2;
15
  struct  {
16
    uint8_t iO3:1;  
17
    uint8_t Fehler:4;
18
19
    uint8_t Auf:1;
20
    uint8_t Zu :1;
21
22
    uint8_t On :1;
23
  };
24
25
}IO;

Kann das so funktionieren, oder müsste ich beide Wörter in eine uint16_t 
Variable bzw. in 2 Union ablegen?
Und das LSB währe dann jeweils die erste Variable?

von Karl H. (kbuchegg)


Lesenswert?

Mathiable schrieb:

> Und das LSB währe dann jeweils die erste Variable?

Das ist der Nachteil der Bitfelder:
Es ist nicht definiert, in welcher Reihenfolge die Bits aufgeteilt 
werden müssen. Theoretisch kann das der Compiler machen wie er will.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Theoretisch kann das der Compiler machen wie er will.

Praktisch isses egal, wenn Sender und Empfänger mit gleichem Compiler
und gleicher Architektur arbeiten.

Ich würde es so schreiben:
1
union {
2
  struct  {
3
4
    uint8_t X1:2;
5
    uint8_t X2:2;
6
    uint8_t X3:2;
7
8
    uint8_t iO1:1;
9
    uint8_t iO2:1;
10
    uint8_t iO3:1;
11
12
    uint8_t Fehler:4;
13
14
    uint8_t Auf:1;
15
    uint8_t Zu :1;
16
17
    uint8_t On :1;
18
  } s;
19
  uint8_t b[2];
20
}IO;

von Mathiable (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Praktisch isses egal, wenn Sender und Empfänger mit gleichem Compiler
> und gleicher Architektur arbeiten.
>
> Ich würde es so schreiben:

Das bedeutet wenn ich IO.b[0] sende und beim Empfänger das Byte in 
dieselben Union auch wider in IO.b[0] schreibe, sollte es 1:1 übertragen 
werden ohne das ich weiss was nun genau übertragen wurde? Das währe 
nämlich sehr komfortabel :-)

Werde es so mal versuchen, wenn es nicht funktioniert melde ich mich 
wieder. Vielen Dank.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mathiable schrieb:
> sollte es 1:1 übertragen
> werden ohne das ich weiss was nun genau übertragen wurde?

So isses.

von Andreas F. (aferber)


Lesenswert?

Jörg Wunsch schrieb:
> Besser ist es, den Datentyp so groß zu machen, wie er
> wirklich notwendig ist, um die Anzahl von Bits zu fassen, bei
> dir also "uint8_t" statt "unsigned" zu schreiben.

Nein. Nach Standard sind die einzigen allgemein zulässigen Datentypen 
für Bitfelder _Bool, signed int und unsigned int. Alles andere ist 
implementation defined.

> Je nachdem, wie groß ein unsigned
> int auf deiner Maschine ist, kann das sich ergebende Bitfeld
> (implementierungsabhängig) dabei auch mehr als nur 16 bit groß
> sein.

Das ist ja gerade der Witz an Bitfeldern, dass die Breite explizit 
vorgegeben wird, und sich nicht aus dem verwendeten Typ ergibt. Welche 
Speichereinheit für Bitfelder verwendet wird ist unabhängig vom Typ, 
auch _Bool-Felder können je nach Architektur in 32bit-Einheiten 
alloziert werden, wobei dann ggf. natürlich bis zu 32 Felder zu einer 
Einheit zusammengefasst werden.

Andersherum wird ein Schuh draus, das Bitfeld darf auf keinen Fall 
größer als der verwendete Typ werden, also ist z.B. auf AVR folgendes 
unzulässig:
1
struct s {
2
        unsigned int x : 17;
3
};

Will man Felder grösser als ein int bzw. die garantierten minimalen 16 
Bits für int haben, muss man notgedrungen auf implementationsabhängiges 
Verhalten ausweichen.

Andreas

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

OK, ich habe wohl zu viel die GCC-Doku und zu wenig den Standard
in Erinnerung gehabt. ;-)  Gerade nochmal nachgelesen, du hast
latürnich Recht.

von Rolf Magnus (Gast)


Lesenswert?

Mathiable schrieb:
> Die Daten währen so nämlich aktueller als wenn ich sie ab und zu zusammen
> fasse.

Warum?

von Mathiable (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Warum?

Das Gerät ist in Slave Modus und muss sofort antworten können. Wenn ich 
die Daten zuerst aufarbeiten muss, kommt es ev. zu Fehlern und da ich 
möglichst schnell arbeiten will, ist es von Vorteil wenn ich die 
einzelnen Bit permanent aktualisiere, anstelle von ab und zu mal alle zu 
aktualisieren. Ausserdem ist es bequemer…

von Rolf Magnus (Gast)


Lesenswert?

Mathiable schrieb:
> Das Gerät ist in Slave Modus und muss sofort antworten können.

Definiere "sofort". Wieviele Nanosekunden dürfen's denn maximal sein?

> Wenn ich die Daten zuerst aufarbeiten muss, kommt es ev. zu Fehlern und da
> ich möglichst schnell arbeiten will, ist es von Vorteil wenn ich die
> einzelnen Bit permanent aktualisiere,

Naja, wenn es dir so um extremst-Performance geht, sind Bitfelder 
sowieso nicht ideal. Da versteckt der Compiler nur das ganze 
Bitgeschiebe, das bei jedem Zugriff nötig ist. Du hast nichts über die 
Zielarchitektur geschrieben, aber z.B. auf AVR sind Bitshifts recht 
aufwendig.

> anstelle von ab und zu mal alle zu aktualisieren.

Es soll nicht "ab und zu" gemacht werden, sondern wenn die Daten 
gesendet werden. Da machst du einen Sendepuffer, und da kopierst du die 
Daten rein, wenn gesendet werden soll.

von Mathiable (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Definiere "sofort". Wieviele Nanosekunden dürfen's denn maximal sein?

Es muss natürlich nicht so extrem schnell gehen, aber es wird sicher von 
Vorteil sein wenn die Daten schon bereit stehen anstelle sie zuerst 
aufzubereiten, es sind Schluss endlich doch einige Bytes die ich 
aufbereiten müsste.

Rolf Magnus schrieb:
> Du hast nichts über die Zielarchitektur geschrieben, aber z.B. auf AVR
> sind Bitshifts recht aufwendig.

So wie ich das verstanden habe kann ich nun einfach den Datenblock 
übertragen, ohne mir sorgen darüber zu machen, in welcher Reihenfolge 
das ganze passiert, also sind Bitshifts nicht nötig.


Rolf Magnus schrieb:
> Es soll nicht "ab und zu" gemacht werden, sondern wenn die Daten
> gesendet werden. Da machst du einen Sendepuffer, und da kopierst du die
> Daten rein, wenn gesendet werden soll.

Was spricht dagegen den "Sendepuffer" permanent zu aktualisieren, 
anstelle von zwischenspeichern?

von Rolf Magnus (Gast)


Lesenswert?

Mathiable schrieb:
> Rolf Magnus schrieb:
>> Du hast nichts über die Zielarchitektur geschrieben, aber z.B. auf AVR
>> sind Bitshifts recht aufwendig.
>
> So wie ich das verstanden habe kann ich nun einfach den Datenblock
> übertragen, ohne mir sorgen darüber zu machen, in welcher Reihenfolge
> das ganze passiert, also sind Bitshifts nicht nötig.

Du hättest auch den Satz davor lesen sollen. Was ich meinte war, daß der 
Compiler für jeden Zugriff auf ein Element deines Bitfeldes den nötigen 
Code erzeugen muß. Und der ist auch nicht weniger aufwendig, als der, 
den du selbst hinschreiben müßtest, wenn du es von Hand machen würdest 
(also statt einem Bitfeld einen Integer zu verwenden, wo du die Bits 
"von Hand" einsortierst). Dabei geht's nicht um das Rausschreiben am 
SPI, sondern um den Zugriff auf die Variablen.

Mathiable schrieb:
> Rolf Magnus schrieb:
>> Es soll nicht "ab und zu" gemacht werden, sondern wenn die Daten
>> gesendet werden. Da machst du einen Sendepuffer, und da kopierst du die
>> Daten rein, wenn gesendet werden soll.
>
> Was spricht dagegen den "Sendepuffer" permanent zu aktualisieren,
> anstelle von zwischenspeichern?

Ja was denn nun? Oben meintest du, die Daten seien nicht aktuell genug, 
wenn du es nur "ab und zu" machst, deswegen meinte ich, daß du's doch 
einfach direkt vor dem Senden machen kannst, also genau dann, wenn sie 
gebraucht werden, und auf einmal ist es kein Problem mehr, das immer zu 
machen?

von Mathiable (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Ja was denn nun? Oben meintest du, die Daten seien nicht aktuell genug,
> wenn du es nur "ab und zu" machst, deswegen meinte ich, daß du's doch
> einfach direkt vor dem Senden machen kannst, also genau dann, wenn sie
> gebraucht werden, und auf einmal ist es kein Problem mehr, das immer zu
> machen?

Das ist der Punkt warum ich den Thread erstellt habe. Ich möchte nicht 
die Daten ständig aufbereiten, sondern die Daten einfach versenden. Ich 
möchte die ausgewerteten Daten in eine Variable schreiben und diese dann 
Versenden. So wie ich dich verstanden habe, möchtest du dass ich die 
Daten in einfache Variabeln zwischenspeichere und dann vor dem Senden 
zusammen trage. Ich möchte aber das Zwischengespeicherte direkt 
versenden ohne es zuerst aufzubereiten müssen. Dies macht die 
Unit-Struct Kombination ganz gut.
Ich verstehe aber nicht ganz warum du mir ständig einen anderen weg 
zeigen willst obwohl andere mit einen funktionierenden Weg gezeigt 
haben, der genau das macht was ich möchte. Für mich ist das Thema 
nämlich seit 2 Tagen abgeschlossen.

von Rolf Magnus (Gast)


Lesenswert?

Mathiable schrieb:
> Ich verstehe aber nicht ganz warum du mir ständig einen anderen weg
> zeigen willst obwohl andere mit einen funktionierenden Weg gezeigt
> haben, der genau das macht was ich möchte.

Das will ich gar nicht. Ich will dich nur darauf aufmerksam machen, daß 
der Zugriff auf die Bitfeld-Elemente mehr Performance kosten könnte, als 
du denkst. Die Geschwindigkeit scheint dir ja sehr wichtig zu sein.

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.