Technische Informatik; Hardwarenahe Programmierung
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 Maskieren und Verschieben¶
Verwende Funktionen mit entsprechenden Parametern und Rückgabewerten für die Aufgaben von Bitmanipulationen-Grundlegend.
Aufgaben Register¶
1. 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.
- u.ä.
2. Aufgabe [UARTnLSR]:
Beim NXP LPC1768 ist das Line Status Register UARTnLSR (Seite 339) ein schreibgeschütztes Register, das die Statusinformationen zu den UARTn TX- und RX-Blöcken bereitstellt:
Bit | Symbol | Value | Description | Reset Value |
0 | Receiver Data Ready (RDR) | 0 | The UARTn receiver FIFO is empty. | |
1 | The UARTn receiver FIFO is not empty. | 0 | ||
1 | Overrun Error (OE) | 0 | Overrun error status is inactive. | |
1 | Overrun error status is active. | 0 | ||
2 | Parity Error (PE) | 0 | Parity error status is inactive. | |
1 | Parity error status is active. | 0 | ||
3 | Framing Error (FE) | 0 | Framing error status is inactive. | |
1 | Framing error status is active. | 0 | ||
4 | Break Interrupt (BI) | 0 | Break interrupt status is inactive. | |
1 | Break interrupt status is active. | 0 | ||
5 | Transmitter Holding Register Empty (THRE)) | 0 | UnTHR contains valid data. | |
1 | UnTHR is empty. | 1 | ||
6 | Transmitter Empty (TEMT) | 0 | UnTHR and/or the UnTSR contains valid data. | |
1 | UnTHR and the UnTSR are empty. | 1 | ||
7 | Error in RX FIFO (RXFE) | 0 | UnRBR contains no UARTn RX errors or UnFCR[0]=0. | |
1 | UARTn RBR contains at least one UARTn RX error. | 0 | ||
31:8 | - | Reserved, the value read from a reserved bit is not defined. | NA |
- Schreibe ein entsprechendes Mbed-Programm, dass die einzelnen Statusinformationen abfragt und entsprechende Meldungen mittels printf-Nachrichten ausgibt.
- Erweitere obiges Mbed-Programm um eine Funktion, die eine Nachricht ausgibt und die 4 Leds blinken, wenn ein Fehler aufgetreten ist.
- Erweitere obiges Mbed-Programm um eine Funktion, die überprüft ob die Reset Values im Register stehen.
- u.ä.