Technische Informatik; Hardwarenahe Programmierung
You are viewing an older revision! See the latest version
Bitmanipulationen Erweitert
Um elementare Bitmanipulationen durchzuführen bietet es sich an einige Hilfsfunktionen zu definieren. Dabei gibt es zwei verschiedene Möglichkeiten diese zu realisieren:
- Als C/C++-Makro
- Als (Inline-)Funktion
- Als struct/union Bitfeld
In beiden Fällen wird bei eingeschalteter Optimierung letztendlich vom Compiler ein sehr kompakter (und identischer!) Code erzeugt, jedoch ist dringend von der Verwendung von Makros abzuraten (siehe Makro )! Im Fehlerfall zeigt der Compiler bei der Verwendung vom Makros keine eindeutigen Fehlermeldungen an, da es sich um simple Suche und Ersetzungen handelt - bei der Verwendung von Inline-Funktionen hingegen gibt es eine "brauchbare" Fehlermeldung! Ebenso abzuraten sind die Bitfelder für Bitmanipulationen, da der Einsatz meist den Code komplizierter macht als mit Verschiebe und Maskier-Operatoren.
Verwendung von Funktionen¶
BitmanipFunctions.cpp
int setBit(int x, unsigned char position) { int mask = 1 << position; return x | mask; } int clearBit(int x, unsigned char position) { int mask = 1 << position; return x & ~mask; } int modifyBit(int x, unsigned char position, bool newState) { int mask = 1 << position; int state = int(newState); // relies on true = 1 and false = 0 return (x & ~mask) | (-state & mask); } int flipBit(int x, unsigned char position) { int mask = 1 << position; return x ^ mask; } bool isBitSet(int x, unsigned char position) { x >>= position; return (x & 1) != 0; }
Verwendung von Markros¶
MakroDefinitionen.cpp
#define BOOL(x) (!(!(x))) #define BitSet(arg,posn) ((arg) | (1L << (posn))) #define BitClr(arg,posn) ((arg) & ~(1L << (posn))) #define BitTst(arg,posn) BOOL((arg) & (1L << (posn))) #define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))
Dies ist eine allgemeine Lösung für eine ganze Klasse von Problemen. Es ist natürlich möglich und sogar angebracht, jedes Mal, wenn eines dieser Makros benötigt wird, das Äquivalent eines dieser Makros mit expliziten Maskenwerten neu zu schreiben, aber warum? Denke daran, dass die Makrosubstitution im Präprozessor erfolgt und der generierte Code die Tatsache widerspiegelt, dass die Werte vom Compiler als konstant angesehen werden - dh., es ist genauso effizient, die verallgemeinerten Makros zu verwenden, als das Rad jedes Mal neu zu erfinden, wenn Sie dies benötigen Bit-Manipulation durchführen.
Testen bzw. erweitere den folgenden Code:
TestMakro.cpp
#include "mbed.h" BusOut myleds(LED1, LED2, LED3, LED4); #define BOOL(x) (!(!(x))) #define BitSet(arg,posn) ((arg) | (1L << (posn))) #define BitClr(arg,posn) ((arg) & ~(1L << (posn))) #define BitTst(arg,posn) BOOL((arg) & (1L << (posn))) #define BitFlp(arg,posn) ((arg) ^ (1L << (posn))) int bitmanip(int word) { word = BitSet(word, 2); word = BitSet(word, 7); word = BitClr(word, 3); word = BitFlp(word, 9); return word; } int main() { uint16_t mask = 0x000f; // 00000000 00001111b mask lower Nibble uint16_t value = 0x0055; // 00000000 01010101b while(1) { scanf("%d", &value); value=bitmanip(value); value=BitSet(value, 8); myleds = value & mask; printf("%x\n", value) wait(0.25); } }
struct/union bit fields¶
Eine andere Variante wäre noch mit einer C/C++ struct ein Bitfeld zu erzeugen und mit einer union für z.B. einen Buszugriff zu überlagern (siehe auch unions and bit fields. Bei einer union haben sowohl byte als auch bit einen überlappenden (gemeinsam genutzten) Speicherort. Speichere einen Wert als Byte und lese dann Bit, um die entsprechenden Bitwerte für den Wert zu erhalten. Oder wie hier in dem Programm speichere die einzelnen Bits und gebe das Nibble auf die vier blauen Leds aus:
StructUnionRegister.cpp
#include "mbed.h" BusOut myleds(LED1, LED2, LED3, LED4); struct nibble { uint8_t a:1; uint8_t b:1; uint8_t c:1; uint8_t d:1; } ; union bits { uint8_t byte; struct nibble bit; }; bits reg; int main() { while(1) { reg.bit.a=1; reg.bit.c=1; myleds = reg.byte; printf("%d\n", reg.byte); wait(0.25); } }
Unions werden in Netzwerkprotokollen verwendet. Sie können auch nützlich sein, um den Polymorphismus in C++ auszutricksen. Im Allgemeinen sind sie ein spezieller Anwendungsfall. Aber für die behandelten Beispiel sind Verschiebungen und Masken viel sauberer Code, sodass Sie für diese Aufgabe wahrscheinlich keine struct/union verwenden würden.
Weiterführende Links¶
Aufgaben¶
Siehe Aufgaben von Bitmanipulationen-Grundlegend nur mit Funktionen.
Aufgabe [UARTnLCR]:
Das UART Line Control Register (UARTnLCR) (Seite 336) bestimmt das Format der Daten, die übertragen werden und ist folgend aufgeteilt:
Bit | Symbol | Value | Description | Reset Value |
01:00 | Word Length | 00 | 5-bit character length | 0 |
01 | 6-bit character length | |||
10 | 7-bit character length | |||
11 | 8-bit character length | |||
2 | Stop Bit Select | 0 | 1 stop bit | 0 |
1 | 2 stop bits | |||
3 | Parity Enable | 0 | Disable parity generation and checking | 0 |
1 | Enable parity generation and checking. | |||
05:04 | Parity Select | 00 | Odd parity | |
01 | Even Parity. | |||
10 | Forced "1" stick parity | |||
11 | Forced "0" stick parity | |||
6 | Break Control | 0 | Disable break transmission | 0 |
1 | Enable break transmission | |||
7 | DLAB | 0 | Disable access to Divisor Latches | |
1 | Enable access to Divisor Latches |
- Setzen Sie das Register auf folgende Werte: 8-bit character, 2 stop bits, enable forced 1 stick parity,
- Das Register ist auf den Wert aus Aufgabe 1 gestetzt. Ändere das Parity Select auf Even parity und DLAB Enable access.
- Das Register ist auf den Wert aus Aufgabe 2 gestetzt. Resetiere das Register NUR mit den Reset Values.
- Schreibe eine Funktion "uartNlcr(...)" mit den entsprechenden Bits als Parameter und setzte die Register-Bits im Funktionskörper entsprechend und gib diesen Wert zurück an den Funktionsaufruf.