Technische Informatik; Hardwarenahe Programmierung

You are viewing an older revision! See the latest version

Bitmanipulationen Erweitert

Inhalt

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.

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:

BitSymbolValueDescriptionReset Value
01:00Word Length005-bit character length0
016-bit character length
107-bit character length
118-bit character length
2Stop Bit Select01 stop bit0
12 stop bits
3Parity Enable0Disable parity generation and checking0
1Enable parity generation and checking.
05:04Parity Select00Odd parity
01Even Parity.
10Forced "1" stick parity
11Forced "0" stick parity
6Break Control0Disable break transmission0
1Enable break transmission
7DLAB0Disable access to Divisor Latches
1Enable access to Divisor Latches
  1. Setzen Sie das Register auf folgende Werte: 8-bit character, 2 stop bits, enable forced 1 stick parity,
  2. Das Register ist auf den Wert aus Aufgabe 1 gestetzt. Ändere das Parity Select auf Even parity und DLAB Enable access.
  3. Das Register ist auf den Wert aus Aufgabe 2 gestetzt. Resetiere das Register NUR mit den Reset Values.
  4. 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.

All wikipages