Forum: Mikrocontroller und Digitale Elektronik AVR GCC Tutorial


von tutorial (Gast)


Lesenswert?

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

von Prof.Dr.Schlau (Gast)


Lesenswert?

Dann mußt du 5 structs anlegen. 5Byte a 8Bit = 40Bit

von tutorial (Gast)


Lesenswert?

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!

von Lutz (Gast)


Lesenswert?

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.

von tutorial (Gast)


Lesenswert?

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

von Bitfeld (Gast)


Lesenswert?

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 ;)

von tutorial (Gast)


Lesenswert?

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

von Bitfeld (Gast)


Lesenswert?

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.

von tutorial (Gast)


Lesenswert?

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

von Bitfeld (Gast)


Angehängte Dateien:

Lesenswert?

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_t var1  : 2;
3
  uint8_t var2  : 2;
4
  uint8_t var3  : 2;
5
  uint8_t var4  : 2;
6
  uint8_t var5  : 2;
7
  ...
8
  uint8_t var20 : 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_t I2SDIV : 8;
3
   uint16_t ODD    : 1;
4
   uint16_t MCKOE  : 1;
5
} *SPI_I2SPR = SPI_I2SPR_ADDR;
Die Bits 10-15 werden nicht benutzt. Zugriff auf die Felder erfolgt
dann so:
1
SPI_I2SPR->I2SDIV = 16;
2
SPI_I2SPR->ODD = 1;

von Prof.Dr.Schlau (Gast)


Lesenswert?

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...)

von tutorial (Gast)


Lesenswert?

Hallo wieder ;)

so, also wenn ich das jetzt anwenden will, ist die richtige Anwendung 
dann so geschrieben?:
1
struct {
2
   unsigned status0:2;
3
   unsigned status1:2;
4
   unsigned status2:2;
5
   ...
6
   unsigned status19:2;
7
} test;

Ich will das auf einem ATmega2560 durchführen, der Compiler meckert 
nicht...
Oder soll ich eben doch lieber 20 8-Bit Variablen anlegen?

von Floh (Gast)


Lesenswert?

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_t i2c_flag = 0;
7
//...
8
if(i2c_flag & (I2CSTART + I2CLESEN))
9
  //tu was

Sind Bitfelder eigentlich überhaupt im C-Standard? (bin mir nicht 
sicher)
:-)

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von tutorial (Gast)


Lesenswert?

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!

von Thorsten (Gast)


Lesenswert?

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).

von Floh (Gast)


Lesenswert?

typedef unsigned char uint8_t;
:-)

von Lutz (Gast)


Lesenswert?

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.

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.