Forum: Mikrocontroller und Digitale Elektronik gesucht "beste" Möglichkeit in c Bitfilds zu definieren


von fragender (Gast)


Lesenswert?

Hallo,

dieses ist eine sicher nicht allgemeingültig zu beantwortende Frage, und 
ich denke hier hat jeder seine ganz eigenen Ansichten zu. Trotzdem frage 
ich nach euren Meinungen und der Begründung warum es für euch die beste 
Möglichkeit darstellt.

Also zunächst ein bisschen Vorgabe
1
typedef struct testStruct{
2
uint16_t  VarA;
3
uint16_t  VarB;
4
? 8BitField 
5
}test_TypeDefSt

es geht darum wie am besten dieses 8 Bit Bitfild implementiert wird.

Variante 1:
1
#define FLAG1 ((uint8_t)0x01U
2
#define FLAG2 ((uint8_t)0x02U
3
#define FLAG3 ((uint8_t)0x04U
4
#define FLAG4 ((uint8_t)0x08U
5
#define FLAG5 ((uint8_t)0x10U
6
#define FLAG6 ((uint8_t)0x20U
7
#define FLAG7 ((uint8_t)0x40U
8
#define FLAG8 ((uint8_t)0x80U
9
10
typedef struct testStruct{
11
uint16_t  VarA;
12
uint16_t  VarB;
13
uint8_t BitField;
14
}test_TypeDefSt

zugriff dann über Veroderung


Variante 2: wie Variante 1 nur mit einem typedef enum für die Flags

Variante 3:
1
typedef struct FlagsStruct{
2
uint8_t flag1 :1;
3
uint8_t flag2 :1;
4
uint8_t flag3 :1;
5
uint8_t flag4 :1;
6
uint8_t flag5 :1;
7
uint8_t flag6 :1;
8
uint8_t flag7 :1;
9
uint8_t flag8 :1;
10
}flags_TypeDefSt
11
12
typedef struct testStruct{
13
uint16_t  VarA;
14
uint16_t  VarB;
15
flags_TypeDefSt BitField;
16
}test_TypeDefSt

zugriff mittels var.BitFild.flagX

ist sichergestellt das der compiler hier nur 8 bit belegt?

gibt es eine einfache Möglichkeit dieses Bielefeld wieder in einem 
uint8_t (char) abzulegen und zu senden? ist hier ein cast gefahrlos 
möglich?

Variante 4:
eure Ideen

Variante 1 ist sicher der Klassiker, allerdings sieht man beim Debuggen 
nicht welche Flags gesetzt sind, was bei größeren Strukturen dann schon 
unübersichtlich werden kann.

von Ben S. (bensch123)


Lesenswert?

Try this:
1
typedef struct FlagsStruct {
2
      uint8_t flag1 : 1;
3
      uint8_t flag2 : 1;
4
      uint8_t flag3 : 1;
5
      uint8_t flag4 : 1;
6
      uint8_t flag5 : 1;
7
      uint8_t flag6 : 1;
8
      uint8_t flag7 : 1;
9
      uint8_t flag8 : 1;
10
    }flags_TypeDefSt;
11
12
    union bla {
13
      uint8_t u8;
14
      flags_TypeDefSt flags;
15
    } blub;
16
17
    blub.u8 = 0;
18
    blub.flags.flag1 = 1;
19
    blub.flags.flag8 = 1;

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

fragender schrieb:
> ist sichergestellt das der compiler hier nur 8 bit belegt?

Nein.

fragender schrieb:
> gibt es eine einfache Möglichkeit dieses Bielefeld wieder in einem
> uint8_t (char) abzulegen und zu senden?

Nein.

fragender schrieb:
> ist hier ein cast gefahrlos
> möglich?

Nein, denn das Ergebnis ist implementation defined, d.h. es kommt je 
nach Compiler, Plattform, ... etwas anderes heraus.

Wenn du also Bitfelder (und andere Daten) verschicken und wieder 
einlesen möchtest, siehe Serialisierung. Meine µSer-Bibliothek 
ermöglicht u.a. das Senden und Empfangen von Bitfeldern auf 
plattform-unabhängige portable Weise:

https://github.com/Erlkoenig90/uSer

Siehe z.B. in der Doku:
https://erlkoenig90.github.io/uSer-doc/html/tutorial.html#ConWidth

Ben S. schrieb:
> Try this:

Ist in C auch implementation defined, und in C++ ganz verboten.

von ein Vorschlag (Gast)


Lesenswert?

fragender schrieb:
> uint8_t flag1 :1;
> uint8_t flag2 :1;
> uint8_t flag3 :1;

habe ich hier irgendwo gelesen, dass da nur Datentypes wie _Bool, 
signed/unsigned int erlaubt sind. Der Rest ist implementation defined.

Ich würde hier _Bool nehmen.

von fragender (Gast)


Lesenswert?

@Niklas G.
Vielen Dank werde mir das auf jeden Fall mal ansehen :)


ich hatte mit Absicht das Typwandeln mittels union nicht als Beispiel 
aufgeführt, da so etwas immer zu Problemen führen kann.

mit dem verwenden des uint8_t hatte ich gehofft einen 8 bit Wert 
erzwingen zu können.

Werde mich jetzt erst mal in die µSer einlesen, klingt spannend

von Peter D. (peda)


Lesenswert?

fragender schrieb:
> dieses ist eine sicher nicht allgemeingültig zu beantwortende Frage

So ist es. Eine Lösung läßt sich viel besser finden, wenn man das 
konkrete Problem kennt. Ob überhaupt ein Bitfeld benötigt wird, weiß man 
daher nicht.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ich nehme Variante 3, besetze aber die Flags dann nur mit true und 
false. Zumindest der gcc arm-abi und der avrgcc schlucken das.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Matthias S. schrieb:
> Zumindest der gcc arm-abi und der avrgcc schlucken das.

Es wird aber eben keine portable Zusammensetzung der Bits garantiert. 
Der selbe Bitfield-basierte Code kann mit unterschiedlichen Compilern 
(-Versionen, -Optionen) unterschiedliche Ergebnisse haben. Das ist für 
portablen Code nicht so gut. Daher sind Flags mit bitweisen Operationen 
besser, nur leider unübersichtlich; daher die µSer-Library, welche die 
komfortabel kapselt.

fragender schrieb:
> Werde mich jetzt erst mal in die µSer einlesen, klingt spannend

Viel Erfolg ;-)

von Rolf M. (rmagnus)


Lesenswert?

fragender schrieb:
> #define FLAG1 ((uint8_t)0x01U

Den Cast brauchst du eigentlich nicht. Wenn's unbedingt vom Typ uint8_t 
sein sollte, wäre
1
#define FLAG1 UINT8_C(0x01)
die in C angedachte Schreibweise.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Niklas G. schrieb:
> Das ist für
> portablen Code nicht so gut.

Da kümmere ich mich dann drum, wenn ich es portieren möchte. Zumindest 
zwischen STM32 und AVR funktionierts - und mir reicht das im Moment.

von Rolf M. (rmagnus)


Lesenswert?

Matthias S. schrieb:
> Niklas G. schrieb:
>> Das ist für
>> portablen Code nicht so gut.
>
> Da kümmere ich mich dann drum, wenn ich es portieren möchte.

Mit der Einstellung kommt dann meistens Code raus, bei dem ein 
Neuschreiben nicht viel mehr Aufwand ist als eine Portierung.
Wenn man von vorne herein etwas mitdenkt, kann man ohne großen 
Meheraufwand einen Großteil des Code gleich so schreiben, dass eine 
Portierung nachher trivial ist.

von A. S. (Gast)


Lesenswert?

Rolf M. schrieb:
> kann man ohne großen Meheraufwand einen Großteil des Code gleich so
> schreiben, dass eine Portierung nachher trivial ist.

In C ist der Aufwand oft riesig, wenn man mit HW oder Telegrammen 
interagiert oder verschiedene int-größen hat. (Wenn nicht, dann, braucht 
man das natürlich nicht).

Mir ist es lieber, der Code ist lesbar und abgesichert, z.B. durch 
CT-Asserts. Man kann die interaktionsfreien Teile ja portabel halten. 
Und beim Rest passt man halt die Header (Strukturen, typedefs) an.

von Niklas Gürtler (Gast)


Lesenswert?

A. S. schrieb:
> In C ist der Aufwand oft riesig

Selber schuld wenn man C nimmt ;-) In C++ ist es möglich Bibliotheken 
wie o.g. zu implementieren welche das sehr einfach machen. Man kann das 
auch von C aus einbinden wenn man eine Datei per C++ kompilieren kann.

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.