Samuel schrieb:
> Gäbe es auch eine "schönere" Variante von...
Schönheit liegt wie immer im Auge des Betrachters.
Hier im Forum sieht man in aller Regel die Bitfummelei, wie sie dir
nicht gefällt - mir auch nicht.
Man kann sich aber mit Bitfeldern ganz gut behelfen, wenn ich dein
Anliegen richtig verstehe.
Beispiel:
1 | typedef union
|
2 | {
|
3 | struct
|
4 | {
|
5 | unsigned bit0:1;
|
6 | unsigned bit1:1;
|
7 | unsigned bit2:1;
|
8 | unsigned bit3:1;
|
9 | unsigned bit4:1;
|
10 | unsigned bit5:1;
|
11 | unsigned bit6:1;
|
12 | unsigned bit7:1;
|
13 | };
|
14 | uint8_t byte;
|
15 | } eightbits_t;
|
16 | ...
|
17 | eightbits_t val =
|
18 | {
|
19 | {
|
20 | .bit0 = 0,
|
21 | .bit1 = 1,
|
22 | .bit2 = 0,
|
23 | .bit3 = 1,
|
24 | .bit4 = 0,
|
25 | .bit5 = 0,
|
26 | .bit6 = 1,
|
27 | .bit7 = 0
|
28 | }
|
29 | };
|
30 |
|
31 | val.byte = 0; // alle Bits auf 0
|
32 | val.bit0 = 0; // unterstes Bit auf 0
|
33 | val.bit0 = 1; // unterstes Bit auf 1
|
34 | ...
|
Wenn man die Variable nicht bitweise, sondern komplett initialisieren
will, geht das natürlich auch:
1 | ...
|
2 | eightbits_t val = { .byte = 0 };
|
3 | ...
|
Die Frage, ob das jetzt allerdings schöner ist, kann beliebig langes
Gezeter verursachen.
Auf eine ähnliche Weise mappe ich mir auch Bits oder Bitgruppen in
Registern auf Bitfelder, wenn mir die übliche Schreibweise mit der
Bitschieberei auf die Nerven geht.
Dazu kann man mißbrauchen, daß die Register in den Adreßbereich
eingeblendet sind wie Speicher, und die Registernamen eh nur Makros
sind, die dann über die Adresse mit etwas Casten aussehen wie Variablen.
Beispiel: in den Headerdateien, die für einen atmega8 verwendet werden,
steht letztlich etwas wie:
1 | ...
|
2 | #define PORTB (*(volatile uint8_t *)((0x17) + 0x20))
|
3 | ...
|
, wenn PORTB an der Stelle 0x37 in den Speicher eingeblendet ist.
Das kann man mißbrauchen, um mit &PORTB wieder an die 0x37 zu kommen und
sich dort stattdessen ein Bitfeld vorzustellen:
1 | ...
|
2 | // in einer Headerdatei von mir:
|
3 | #ifdef DDRB
|
4 | #define ddrb (*(volatile eightbits_t*)(&DDRB))
|
5 | #define portb (*(volatile eightbits_t*)(&PORTB))
|
6 | #define pinb (*(volatile eightbits_t*)(&PINB))
|
7 | #endif /* ifdef DDRB */
|
8 | ...
|
9 | // in einer C++ von mir:
|
10 | ddrb.bit0 = 0; // Datenrichtung für LED setzen
|
11 | portb.bit0 = 0; // LED aus
|
12 | portb.bit0 = 1; // LED ein
|
13 | ...
|
14 | // oder etwas sprechender:
|
15 | #define DDR_IN (0)
|
16 | #define DDR_OUT (1)
|
17 | ...
|
18 | #define MEINELED_PORT portb.bo
|
19 | #define MEINELED_DDR ddrb.bo
|
20 |
|
21 | ...
|
22 | MEINELED_DDR = DDR_OUT; // Datenrichtung für LED setzen
|
23 | MEINELED_PORT = 0; // LED aus
|
24 | MEINELED_PORT = 1; // LED ein
|
25 | ...
|
Für spezielle Register mache ich mir dann Bitfelder, die nicht unbedingt
einzelne Bits abbilden, sondern zusammengehörende Gruppen, die auf
einmal gesetzt werden können.
Beispiel: Wenn ein Register ADMUX laut Datenblatt aus einem 5 Bit langen
Bereich mux für die Kanalnummer besteht, einem Bit adlar und einem zwei
Bit langen refs, dann kann man sowas schreiben:
1 | ...
|
2 | typedef union
|
3 | {
|
4 | uint8_t byte;
|
5 | struct
|
6 | {
|
7 | unsigned int mux:5;
|
8 | unsigned int adlar:1;
|
9 | unsigned int refs:2;
|
10 | };
|
11 | } admux_byte8bits_t;
|
12 |
|
13 | #define admux (*(volatile admux_byte8bits_t*)&ADMUX)
|
14 | ...
|
15 | admux.mux = iKanal; // Kanalnummer setzen (5 Bits)
|
16 | ...
|
Damit will ich jetzt keine Stildiskussion anfachen, sondern zeigen wie
es technisch gehen kann - wenn man es so will.