Hallo Comunity,
ich habe etwas im Tutorial gestöbert und bin über Bitfelder gestolpert:
1
struct
2
{
3
unsigned bStatus_1:5; // 5 Bit zuweißen
4
unsigned bStatus1_1:2; // 2 Bit zuweißen
5
unsigned bStatus_2:1; // 1 Bit zuweißen
6
} test; // Name der Variable
Jetzt ist das wie im Tutorial beschrieben ein unsigned char, also mit 8
Bit die ich zuweißen kann.
Wenn ich jetzt zum Beispiel 40 Bit in so einem Bitfeld bräuchte, kann
ich dann einfach 40 zuweißen oder wie kann ich dies umsetzten?
Viele Grüße
Prof.Dr.Schlau schrieb:> Dann mußt du 5 structs anlegen. 5Byte a 8Bit = 40Bit
Also die Structs dann GLEICH nennen und dann eben andere Bitfelder
drinnen anlegen oder? also ein STRUCT ist dann IMMER gleich 8 Bit breit.
Weil der Compiler nicht meckert, wenn ich jetzt innerhalb eines Structs
40 Bit zuweiße.
Danke schonmal!
Prof.Dr.Schlau schrieb:> Dann mußt du 5 structs anlegen. 5Byte a 8Bit = 40Bit
Bullshit.
In einer struct kannst Du (ganzzahlige) Datentypen Deiner Wahl
implementieren, also z.B.
struct
{
uint8_t bStatus_1:5; // 5 Bit zuweisen
uint16_t bStatus1_1:12; // 12 Bit zuweisen
unsigned bStatus_2:1; // 1 Bit zuweisen
} test; // Name der Variable
Du kannst natürlich auch in einer struct 5 byte anlegen. Generell sind
Bitfelder aber nur sinnvoll, wenn RAM wirklich knapp wird, da mehr
Flash und Zeit verbraucht werden.
Das heißt, über solche Bitfelder braucht mein Code länger? Und auch mehr
Speicher? Ist ja irgednwie wiedersprüchlich oder?
Ich dachte mir ich Spar somit den Platz, weil jetzt hab ich ja wieder
nur (mit dem obigen Beispiel) einen UINT8_T mit 5 Bit belegt, also auch
wieder verschwendung?
Wo ist jetzt der Fehler meiner Ansicht?
Grüße
Lutz schrieb:> Generell sind> Bitfelder aber nur sinnvoll, wenn RAM wirklich knapp wird
Das ist eine gewagte Aussage. Ich wage es meinerseits zu behaupten
das die Verwendung von Bitfeldern in den allermeisten Fällen nicht aus
Speichermangel erfolgt.
Unter den ARM Controllern z.B. sind Bitfelder weit verbreitet um
auf Register zuzugreifen. Und sicher nicht weil die zu wenig Speicher
haben.
Flashverbrauch und Ausführungszeit müssen sich nicht zwangsläufig
erhöhen wenn die Alternative zu den Bitfeldern darin besteht händisch
mit Bitshifts zu arbeiten. Wenn es darum geht RAM zu sparen erhöhen
sich diese Wert aber in der Regel schon.
Doch wie gesagt, spielt das RAM-Sparen bei Bitfeldern eine eher
untergeordnete Rolle.
tutorial schrieb:> Das heißt, über solche Bitfelder braucht mein Code länger?
Möglicherweise. Wenn die tatsächliche Anordnung der Daten im
Speicher für die Funktion des Programms keine Rolle spielt, ist
der Code bestimmt langsamer.
Vielleicht hilft folgendende Abschätzung:
Wenn das Programm auch funktionieren würde wenn man in der Struct
die Bitbreite wegläßt, wird der Code wahrscheinlich größer und
langsamer. Aber sollte das Programm dann nicht laufen, ist die
Alternative wohl auch nicht besser.
Einfacher:
Wenn du dir nicht sicher bist das du Bitfelder brauchst - lass es
bleiben ;)
Okay, aaaalso ich habe Interrupts, diese setzten vieeele vieele
Arbeitsflags, also wie die LED´s sein soll, was im nächsten durchlauf
gemacht werden muss usw. Jetzt wollte ich nicht immer 8 Bit für eine
normale AN / AUS Abfrage machen, drum also ein Bitfeld, welches dann
gleich acht Flags sind.
Ich verstehe nur noch nicht, warum man in einem Struct dann nochmal ein
unsigned char einsetzt, diesem dann wiederum nur sechs Bit zuordnet.
Macht für mich iwie keinen Sinn.
Grüße
tutorial schrieb:> ich habe Interrupts, diese setzten vieeele vieele> Arbeitsflags
Da würde ein Bitfeld RAM sparen, das Programm aber würde größer und
langsamer werden. Der "Codevision AVR" - ein C-Compiler - kann bei
bei Variablen vom Typ bool einzelne Bits in dafür vorgesehenden
CPU-Registern setzen. Das geht sogar eher schneller als ein
normaler RAM Zugriff und verbraucht überhaupt kein RAM. Bis zu
32 Bool Werte sind da möglich. Das ist einer der wenigen Vorteile
dieses Compilers gegenüber dem avr-gcc.
> Ich verstehe nur noch nicht, warum man in einem Struct dann nochmal ein> unsigned char einsetzt, diesem dann wiederum nur sechs Bit zuordnet.
Es gibt zwei Anwendungsfälle für Bitfelder:
1. RAM sparen
2. Zugriff auf gegebene Datenstrukturen
Poste doch mal ein Beispiel das du nicht verstehst.
Lutz schrieb:> struct> {> uint8_t bStatus_1:5; // 5 Bit zuweisen -> Hätte doch 8 Platz?!> uint16_t bStatus1_1:12; // 12 Bit zuweisen -> Ich hätte doch 16 platz?!> unsigned bStatus_2:1; // 1 Bit zuweisen> } test; // Name der Variable
Hier jetzt mal der Code...
Da hab ich ein Struct und eben in dem variablen angelegt, wobei im
Tutorial eben nur unsigned dran steht, konnte ich hier eben die 8 Bit
verteilen und dann war gut.
Also wenn ich jetzt auf die schnelle 20 mal je 2 Bit bräuchte, die ich
WÄHREND dem Programm nach 0 ... 3 Abfrage, wie mach ich dies dann am
besten?
Grüße
Ein einsames "unsigned" bedeutet beim avr-gcc "unsigned int" bzw.
"uin16_t".
Bitfelder sind stark plattformabhängig (Maschine wie Compiler).
Der C-Standard definiert die Funktionsweise einer zusammenhängenden
Bitfolge nur innerhalb eines "in". Beim AVR also 16Bit. Ob ein
Bitfeld nun beim niederwertigsten Bit (AVR) oder beim höchstwertigen
beginnt, ist ebenfalls plattformabhängig.
Wie sich 20*2 Bits am besten verteilen lassen hängt vom Einzelfall
ab. Als Bitfeld könnte man im besten Fall mit 5 Bytes auskommen.
Ohne wären es 20 Bytes. Sollte die 15 Bytes Differenz im RAM Bedarf
tatsächlich entscheidend sein, würde ich es so machen:
1
struct{
2
uint8_tvar1:2;
3
uint8_tvar2:2;
4
uint8_tvar3:2;
5
uint8_tvar4:2;
6
uint8_tvar5:2;
7
...
8
uint8_tvar20:2;
9
}meineDaten;
Ein Zugriff auf ein Element wäre dann so möglich:
1
meineDaten.var7=2;
Doch sollte es nicht auf die 15 Bytes ankommen, würde das Programm
schneller laufen wenn man in der Struktur jeweils die ":2" wegläßt.
Strukturen wie sie Lutz genannt hat dienen nicht dazu RAM
einzusparen. Oft haben bestimmte Bits in einer Datenstruktur
eine besondere Bedeutung. Ein Beispiel ist das SPI_I2SPR Register
eines STM32F103 Controllers. Im Anhang ist ein 16Bit Register zu
sehen. Die "Variablen" I2SDIV, ODD und MCKOE belegen bestimmte
Bereiche im Register.
Mit folgender Struktur könnte man darauf zugreifen:
1
struct{
2
uint16_tI2SDIV:8;
3
uint16_tODD:1;
4
uint16_tMCKOE:1;
5
}*SPI_I2SPR=SPI_I2SPR_ADDR;
Die Bits 10-15 werden nicht benutzt. Zugriff auf die Felder erfolgt
dann so:
Prof.Dr.Schlau schrieb:> Dann mußt du 5 structs anlegen. 5Byte a 8Bit = 40Bit
klar Quatsch. Ich meinte eigentlich 5Byte a 8Bit in einen struct
(rausred...)
tutorial schrieb:> Oder soll ich eben doch lieber 20 8-Bit Variablen anlegen?
Würd ich empfehlen.
Außerden kannst du ja die Bitvariablen logisch gruppieren und dann
zusammen in einem Byte ablegen.
z.B.
1
#define I2CSTART 1 //als 2er-Potenzwerte
2
#define I2CLESEN 2
3
#define I2CSCHREIBEN 4
4
//...
5
6
uint8_ti2c_flag=0;
7
//...
8
if(i2c_flag&(I2CSTART+I2CLESEN))
9
//tu was
Sind Bitfelder eigentlich überhaupt im C-Standard? (bin mir nicht
sicher)
:-)
Floh schrieb:> Sind Bitfelder eigentlich überhaupt im C-Standard? (bin mir nicht> sicher)> :-)
Ja sind sie.
Allerdings nur sehr rudimentär und unspezifiziert. Es gibt sie. Recht
viel mehr steht da nicht drinnen.
tutorial schrieb:> Oder soll ich eben doch lieber 20 8-Bit Variablen anlegen?
Wenn du den Speicher hast, warum nicht?
Über eines musst du dir im klaren sein. Auch wenn die Syntax einfach
erscheint, den Aufwand da Bits frei zu maskieren und zu extrahieren
nimmst du dem Prozessor auch mit Bitfeldern nicht ab. So gesehen sind
Bitfelder syntaktischer Zucker für etwas, was man mit UND und ODER
Operationen genauso gut erreichen kann. Nur dass dann im Klartext dort
steht, wenn eine Operation aufwändiger ist und bei Bitfeldern eben
nicht.
Hallo und danke für die schnellen Rückmeldungen.
Ich werde jetzt ersteinmal die Variablen mit unsigned char anlegen,
falls der Platz später wirklich kanpp wird, mach ich das mit der
Gruppierung und mir UND / ODER.
Danke nochmal!
tutorial schrieb:> Ich werde jetzt ersteinmal die Variablen mit unsigned char anlegen,
Man sollte char wirklich nur für ASCI-Zeichen nutzen, ansonsten
unsigned-Werte (gerade für Flags, es sei denn, man braucht wirklich
vorzeichenbehaftete Größen). Der Grund war die nationale Sicherheit oder
so ähnlich. Am besten ist sowieso die Nutzung von uint8_t etc. als
Notation; da weiß man immer, wie groß der Datentyp wirklich ist (ist ja
leider plattformabhängig).
Bitfeld schrieb:> Das ist eine gewagte Aussage. Ich wage es meinerseits zu behaupten> das die Verwendung von Bitfeldern in den allermeisten Fällen nicht aus> Speichermangel erfolgt.> Unter den ARM Controllern z.B. sind Bitfelder weit verbreitet um> auf Register zuzugreifen.
Du bist sicher, daß Du nicht "normale" structs meinst? Da hättest Du
natürlich recht. Hier sind aber explizit Bitfelder gemeint, welche ein
Sonderfall der structs sind.
Bitfeld schrieb:> Mit folgender Struktur könnte man darauf zugreifen:struct {> uint16_t I2SDIV : 8;> uint16_t ODD : 1;> uint16_t MCKOE : 1;> } *SPI_I2SPR = SPI_I2SPR_ADDR;
Aus der Standard Peripheral Lib ist das aber nicht, oder? Die benutzen
da auch immer byte-Vielfache.