Forum: Mikrocontroller und Digitale Elektronik Komisches Bitfield Padding


von Michael S. (michl-s)


Lesenswert?

Hi Leute, folgendes struct/union habe ich angelegt:
1
// DCF77 Data Struct
2
typedef struct DCF77_DATA_STREAM_struct
3
{
4
  union {
5
6
    unsigned long long int       Raw_Stream ;
7
8
    struct {
9
      unsigned int Minute_Mark :1   ;    // LSB of Long_Int_Def
10
      unsigned int BBK_Meteo    :14   ;
11
      unsigned int R       :1   ;
12
      unsigned int A1      :1   ;
13
      unsigned int Z1      :1   ;
14
      unsigned int Z2      :1   ;
15
      unsigned int A2      :1   ;
16
      unsigned int S       :1   ;
17
      unsigned int Min_Ones    :4   ;
18
      unsigned int Min_Tens    :3   ;
19
      unsigned int Min_Par     :1   ;
20
      unsigned int Hour_Ones    :4   ;
21
      unsigned int Hour_Tens    :2   ;
22
      unsigned int Hour_Par     :1   ;
23
      unsigned int Day_Ones    :4   ;
24
      unsigned int Day_Tens    :2   ;
25
      unsigned int Weekday     :3   ;
26
      unsigned int Month_Ones  :4   ;
27
      unsigned int Month_Tens  :1   ;
28
      unsigned int Year_Ones   :4   ;
29
      unsigned int Year_Tens   :4   ;
30
      unsigned int Date_Par    :1   ;  
31
      unsigned int No_Mark   :1   ;
32
      unsigned int        :4   ;
33
    };
34
  };
35
}DCF77_DATA_STREAM;

Wenn ich nun mit "sizeof" die Größe bestimme erhalte ich 10 Bytes, 
obwohl sowhl unsigned long long int 8 Bytes groß sein müsste, als auch 
das gesamte Bitfeld. Wo is'n da der Fehler???

Hinweis: MPLAB C30

von Karl H. (kbuchegg)


Lesenswert?

Michael S. schrieb:
> Hi Leute, folgendes struct/union habe ich angelegt:

Dem Compiler steht es frei zwischen die einzelnen Teile eines struct 
noch zusätzliche Padding Bytes einzufügen um zb irgendwelche 
Alignment-Anforderungen zu erfüllen. Und das wird er wohl getan haben


Und damit sind dann Bitfelder in Strukturen gleich gar nicht mehr so 
elegant wie ursprünglich gedacht.

Schreib dir Zugriffsfunktionen um dir aus dem ull mit den DCF Daten die 
relevanten Bits zu extrahieren und zurecht zu schieben. Was anderes 
macht der Compiler ja auch nicht.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Michael S. schrieb:
> Wenn ich nun mit "sizeof" die Größe bestimme erhalte ich 10 Bytes,
> obwohl sowhl unsigned long long int 8 Bytes groß sein müsste, als auch
> das gesamte Bitfeld. Wo is'n da der Fehler???

Das Problem ist wohl, dass Deine Bits - wenn man sie von oben nach unten 
zusammenzählt - nicht immer genau in 16-Bit-Integer reinpassen. Dann 
muss der Compiler Padding-Bits einfügen, damit sowas wie

  unsigned int Min_Ones    :4   ;

nicht auf zwei Integers aufgesplittet werden muss.

Ich finde Bitfelder höchst unportabel. Ich würde an Deiner Stelle davon 
Abstand nehmen.

von Achim M. (minifloat)


Lesenswert?

Im DCF-Signal gibt es doch nur 59 Bits. Was liegt näher, erstmal den 
ganzen Frame in eine 64bit Variable reinzutakten und mit Maskieren und 
geeignetem Schieben die Einzelinformationen zu extrahieren?
mfg mf

von Max P. (maxpohl)


Lesenswert?

Hallo, ich habe ähnliches vor.
Bei mir ist es eine 32bit Zahl die ich zerstückeln möchte.
Der Code ist mit dem von Michael identisch.
Funktioniert leider nicht. Hat jemand eine Ahnung woran das liegt?
Verwende ebefalls den C30.
Gruß Max

von Peter D. (peda)


Lesenswert?

Mini Float schrieb:
> Im DCF-Signal gibt es doch nur 59 Bits. Was liegt näher, erstmal den
> ganzen Frame in eine 64bit Variable reinzutakten und mit Maskieren und
> geeignetem Schieben die Einzelinformationen zu extrahieren?

Warum sollte man diesen hohen Aufwand treiben?

Die Bit kommen mit ner Wahsinns-Geschwindigkeit von einem Baud rein. Da 
hat man alle Zeit der Welt, die sofort auszuwerten. Dann wird der Code 
auch sehr schön klein und übersichtlich.
Hier ein Beispiel für die Auswertung in "Echtzeit":

Beitrag "DCF77 Uhr in C mit ATtiny26"


Peter

von Max P. (maxpohl)


Lesenswert?

In meinem Fall geht dies leider nicht. Ich bekomme meine Daten per SPI 
und muss diese so auswerten.
Hat jemand eine idee?

von Peter D. (peda)


Lesenswert?

Max Pohl schrieb:
> In meinem Fall geht dies leider nicht.

Warum nicht?

Es ist egal, wo die Bits herkommen.
Es ist einfacher, wenn es eine Regel gibt, was die Bits bedeuten.
Ansonsten nimmt man eben ein Switch mit 32 Case.


Peter

von Max P. (maxpohl)


Lesenswert?

1
typedef union GYRODATA
2
  {
3
    long Data;    
4
    struct
5
    {
6
    unsigned int P1:1;
7
    unsigned int CHK:1;
8
    unsigned int CST:1;
9
    unsigned int PWR:1;
10
    unsigned int POR:1;
11
    unsigned int NVM:1;
12
    unsigned int Q:1;
13
    unsigned int PLL:1;
14
    unsigned int empty:2;
15
    unsigned int Daten:16;
16
    unsigned int ST:2;
17
    unsigned int P0:1;
18
    unsigned int SQ:3;
19
    }; 
20
  }GYRO1;

Ich bekomme per SPI zwei 16bit Werte, diese kann ich nur als ganze Zahl 
aus dem Eingangspuffer nehmen. Dann muss ich diese 32bit Zahl wie oben 
gezeigt aufspliten um damit weiter arbeiten zu können.

von Andreas H. (andreas_h16)


Lesenswert?

Hallo,
Frank M. hat das Problem ja schon genannt. Deine Felder schneiden nicht 
exakt an den int Grenzen. Wenn Du jetzt trotzdem Bitfelder benutzen 
willst, musst Du eben Teilfelder bilden, z.B.

 unsigned int Hour_Ones    :4   ;

wird aufgesplittet in

 unsigned int Hour_Ones_a    :2   ;
 unsigned int Hour_Ones_b    :2   ;

und dann wieder bei der Auswertung zusammensetzen:

unsigned int Hour_Ones = (x.Hour_Ones_b << 2) | x.Hour_Ones_a;

Gruß,
Andreas

von Andreas H. (andreas_h16)


Lesenswert?

Max Pohl schrieb:
> Ich bekomme per SPI zwei 16bit Werte, diese kann ich nur als ganze Zahl
> aus dem Eingangspuffer nehmen. Dann muss ich diese 32bit Zahl wie oben
> gezeigt aufspliten um damit weiter arbeiten zu können.

Das gleiche hier: Bis  unsigned int empty:2; sinds 10bit

Dann müssen die Daten eben in 6 und 10 bit geteilt werden

Alternativ auf 32bit alignen (compiler option), das kostet aber size 
(also nicht so praktikabel)

von Peter D. (peda)


Lesenswert?

Vergiß die Struct:
1
#define P1  0
2
#define CHK 1
3
// usw.
4
5
if( Data & 1<<P1 ) // P1 gesetzt
6
{
7
}
8
if( Data & 1<<CHK ) // CHK gesetzt
9
{
10
}
11
uint16_t Daten = Data >> 10;


Peter

von Max P. (maxpohl)


Lesenswert?

Eine struct müsste doch wesentlich schneller sein oder etwa nicht?

von Max P. (maxpohl)


Lesenswert?

Vielen Dank Andreas das wars, jetzt geht alles.
Ich bin noch recht neu bei der C Programmierung mit C30 was ist dieses 
Alignment? Ich brauche maximale Geschwindigkeit wenn deswegen etwas mehr 
Speicher für dies verlogen geht ist mir das recht egal, solange es 
schneller geht.

von Peter D. (peda)


Lesenswert?

Max Pohl schrieb:
> Eine struct müsste doch wesentlich schneller sein oder etwa nicht?

Nur wenn die CPU spezielle Bitbefehle hat und der Compiler diese auch 
unterstützt.

Ansonsten sind Bit-Structs deutlich langsamer und erzeugen größeren Code 
als alles andere.
C ist für Bytes und größer optimiert.


Peter

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Max Pohl schrieb:
> Eine struct müsste doch wesentlich schneller sein oder etwa nicht?

Du meinst eine struct, die auf Bitfields beruht? Man kann jede 
Bitfiled-Präsentation bzw. -Operation auch "per Hand" in C machen. Peter 
Dannegger hat es Dir oben gezeigt wie. Bitfields sind vielleicht für den 
Programmierer etwas "bequemer", aber letztendlich macht der Compiler 
beim Bitfriemeln nichts anderes als Du selbst mit Maskieren und 
Schieben, um die entsprechenden Daten zu extrahieren.

Fazit: Von der Geschwindigkeit/Codegröße ist es kaum ein Unterschied 
(tatsächlich sind Bitfields sogar oft "schlechter"). Dabei sind 
Bitfields auch noch höchst unportabel. Nicht machen.

von Andreas H. (andreas_h16)


Lesenswert?

Hallo Max,

es gibt beim gcc zwei Options in dieser Richtung: -fpack-struct und 
-funsigned-bitfields.

IMHO wird aber sowieso auf 16bit aligned. Ich würde jetzt mal probieren, 
ob
1
typedef union GYRODATA
2
  {
3
    uint32_t Data;    
4
    struct
5
    {
6
    uint32_t P1:1;
7
    uint32_t CHK:1;
8
    uint32_t CST:1;
9
    uint32_t PWR:1;
10
    uint32_t POR:1;
11
    uint32_t NVM:1;
12
    uint32_t Q:1;
13
    uint32_t PLL:1;
14
    uint32_t empty:2;
15
    uint32_t Daten:16;
16
    uint32_t ST:2;
17
    uint32_t P0:1;
18
    uint32_t SQ:3;
19
    }; 
20
  }GYRO1;
nicht auch funktioniert.

Beim gcc geht folgendes:
-funsigned-bitfields macht
Make any unqualified bitfield type unsigned. By default, they are 
signed.

In dem obigen Fall müsste es aber gehen.

Und -fpack-struct bewirkt
Pack all structure members together without holes.

Kann nicht schaden.

Dem -fpack-struct kann man IMHO noch ein '=N', Potenzu von 2 mitgeben, 
als packsize.

Ich würds aber trotzdem mal so wie oben probieren, ohne Options.

von Uwe B. (boerge) Benutzerseite


Lesenswert?

MoinMoin,

ich hatte auch mal eine DCF77-Uhr 
(Beitrag "DCF77-TWI(I2C)-Slave") geschrieben. Da habe ich 
auch einen solchen "Bit-Struct" verwendet, der aber etwas anders 
aussieht:
1
volatile union dcf_t {
2
  struct  {
3
    uint8_t b[8];
4
    } bits;
5
  struct  {
6
    unsigned start_mm  : 1;  // Bit 0
7
    unsigned meteotime : 14;// Bit 1...14
8
    unsigned ruf_bit   : 1;  // Bit 15
9
    unsigned mez2mesz  : 1;  // Bit 16
10
    unsigned mez     : 1;  // Bit 17
11
    unsigned mesz     : 1;  // Bit 18
12
    unsigned switch_ss : 1;  // Bit 19
13
    unsigned begin_time: 1; // Bit 20
14
    unsigned mm     : 7;  // Bit 21...27
15
    unsigned p_mm     : 1;  // Bit 28
16
    unsigned hh     : 6;  // Bit 29...34
17
    unsigned p_hh     : 1;  // Bit 35
18
    unsigned dd     : 6;  // Bit 36...41
19
    unsigned wd     : 3;  // Bit 42...44
20
    unsigned mt     : 5; // Bit 45...49
21
    unsigned yy     : 8;  // Bit 50...57
22
    unsigned p_date     : 1; // Bit 58
23
    unsigned click     : 1; // Bit 59 (Merker, ob neue Sekunde)
24
    unsigned valid     : 2; // Bit 60...61 ("Zeitermittlungsgrad" 2=OK)
25
    unsigned reserve   : 2;  // 2 Bit Reserve
26
  } dat;
27
} dcf_temp;

Unterschied zur Ausgangsfrage ist das:
1
struct  {
2
    uint8_t b[8];
3
} bits;

Ich fand damals (und auch heute) keine schlechte Sache, denn damit ist 
dann z.B. soetwas möglich, da es sich ja eigentlich in den 
DCF77-Informationen um BCD-Codierung handelt (was die Uhrzeit betrifft):
1
dcf77.mm   = bcd2dec(dcf_temp.dat.mm);
2
dcf77.hh   = bcd2dec(dcf_temp.dat.hh);
3
dcf77.dd   = bcd2dec(dcf_temp.dat.dd);
4
dcf77.mt   = bcd2dec(dcf_temp.dat.mt);
5
dcf77.yy   = bcd2dec(dcf_temp.dat.yy);
6
dcf77.wd   = bcd2dec(dcf_temp.dat.wd);
7
dcf77.mez  = dcf_temp.dat.mez;
8
dcf77.mesz = dcf_temp.dat.mesz;

Auch das Reinschieben der Daten ist relativ elegant lösbar:
1
dcf_temp.bits.b[(idx/8)] |= 1 << (idx % 8);

wobei idx ein fortlaufender Index ist...


Grüße Uwe

PS.:
Peter Dannegger schrieb:
> Ansonsten sind Bit-Structs deutlich langsamer und erzeugen größeren Code
> als alles andere.
>
aber es sieht schöner und eleganter aus, finde ich :-)

von Andreas H. (andreas_h16)


Lesenswert?

Uwe Berger schrieb:
> aber es sieht schöner und eleganter aus, finde ich :-)

Wenns einmal funktioniert, gebe ich Dir recht ;-)

von MWS (Gast)


Lesenswert?

Da ist kein Compilerschalter nötig, das lässt sich per Attribut machen. 
Wenn ich das im Manual vom C30 richtig sehe, sollte das auch dort 
klappen:
1
typedef union GYRODATA
2
  {
3
    long Data;    
4
    struct __attribute__ ((__packed__))
5
    {
6
    unsigned int P1:1;
7
    unsigned int CHK:1;
8
    unsigned int CST:1;
9
    unsigned int PWR:1;
10
    unsigned int POR:1;
11
    unsigned int NVM:1;
12
    unsigned int Q:1;
13
    unsigned int PLL:1;
14
    unsigned int empty:2;
15
    unsigned int Daten:16;
16
    unsigned int ST:2;
17
    unsigned int P0:1;
18
    unsigned int SQ:3;
19
    }; 
20
  }GYRO1;
Nur schneller ist der Zugriff nicht, er wird langsamer besonders da 
"Daten" über 3 Bytes verstreut wird und bei jedem Zugriff diese 3 Bytes 
maskiert werden müssen.

von Peter D. (peda)


Lesenswert?

Uwe Berger schrieb:
> Ich fand damals (und auch heute) keine schlechte Sache, denn damit ist
> dann z.B. soetwas möglich, da es sich ja eigentlich in den
> DCF77-Informationen um BCD-Codierung handelt (was die Uhrzeit betrifft):
> dcf77.mm   = bcd2dec(dcf_temp.dat.mm);
> dcf77.hh   = bcd2dec(dcf_temp.dat.hh);
> dcf77.dd   = bcd2dec(dcf_temp.dat.dd);
> dcf77.mt   = bcd2dec(dcf_temp.dat.mt);
> dcf77.yy   = bcd2dec(dcf_temp.dat.yy);
> dcf77.wd   = bcd2dec(dcf_temp.dat.wd);

Ja, die BCD-Kodierung hat mich auch gestört, ne CPU rechnet ja lieber 
binär.
Deshalb lese ich direkt binär ein, d.h. jedes Bit addiert gleich seinen 
richtigen Wert (1,2,4,8,10,20,40,80).
Eine nachträgliche Umkodierung ist nicht mehr nötig.


Ein Grund ist allerdings auch, daß der der AVR-GCC long long nur 
grauslig schlecht implementiert, sodaß es nicht mehr in den ATtiny26 
paßt.
Ist schon ein Unterschied, ob man etwas mit 5KB Code implementiert oder 
nur mit knapp über 100 Byte.


Peter

von Michael S. (michl-s)


Lesenswert?

Mein DCF77 läuft in der Zwischenzeit auch zuverlässig.
Wenn ihr allerdings gleich beim EMpfang die richtigen Werte jedes 
empfangenen Bits addiert, wartet ihr dann beim EInschalten auf den 
Minutemarker? Geht ja kaum anders oder?

von Peter D. (peda)


Lesenswert?

Michael S. schrieb:
> Wenn ihr allerdings gleich beim EMpfang die richtigen Werte jedes
> empfangenen Bits addiert, wartet ihr dann beim EInschalten auf den
> Minutemarker?

Das ergibt sich automatisch, da ich nur Pakete akzeptiere, die genau 59 
Pulse lang sind. Störungen sind ja asynchron, d.h. entweder die Störung 
überdeckt Pulse oder erzeugt zusätzliche Pulse.
Auch braucht der Empfänger erstmal mehrere Impulse, um die Verstärkung 
einzuregeln.


Peter

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.