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
unsignedX1:2;
4
unsignedX2:2;
5
unsignedX3:2;
6
7
unsignediO1:1;
8
unsignediO2:1;
9
unsignediO3:1;
10
11
unsignedFehler:4;
12
13
unsignedAuf:1;
14
unsignedZu:1;
15
16
unsignedOn: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...
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.
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_twort1;
4
struct{
5
6
uint8_tX1:2;
7
uint8_tX2:2;
8
uint8_tX3:2;
9
10
uint8_tiO1:1;
11
uint8_tiO2:1;
12
}
13
14
uint8_twort2;
15
struct{
16
uint8_tiO3:1;
17
uint8_tFehler:4;
18
19
uint8_tAuf:1;
20
uint8_tZu:1;
21
22
uint8_tOn: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?
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.
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:
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.
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
structs{
2
unsignedintx: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
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…
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.
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?
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?
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.
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.