Bitmanipulation

Da die einzelnen Bits eines Registers (z.B. Port-Register oder Timer/Counter-Register) für den Programmablauf eine Bedeutung haben, kann es erforderlich sein, nur ein Bit eines Registers zu verändern, während die restlichen Bits unverändert bleiben müssen. Unter Bitmanipulation verstehe ich also Vorgänge wie Setzen, Löschen, Invertieren oder Abfragen einzelner Bits eines Ports oder Registers.


    Setzen einzelner Bits:

    Als Beispiel eines Registers ist nachfolgend das "Timer/Counter1 Control Register B" (TCCR1B) dargestellt:

    Möchte man z.B. das Bit WGM12 setzen, ohne die anderern Bits zu verändern, kann das mit der bitweisen ODER-Anweisung gemacht werden:

    TCCR1B |= (1 << WGM12);

    Die Schreibweise "|=" ist ein sogenannter Compound-Operator und ist gleichbedeutend, aber kürzer, wie mit folgender Schreibweise:

    TCCR1B = TCCR1B | (1 << WGM12);

    Oder man verwendet das "_BV-Makro" (BV steht für Bit Value):

    TCCR1B |= _BV(WGM12); // _BV(WGM12) ist identisch mit (1 << WGM12)

    Dazu muss vorausgesagt werden: Der Arduino-Übersetzer kennt alle Register- und Bitnamen wie TCCR1B, CS10 oder WGM12, usw., diese müssen daher vorher nicht deklariert werden. Z.B. hat WGM12 den Inhalt "3", entsprechend dem 3. Bit im Register. CS11 hat den Inhalt "1", entsprechend dem 1. Bit im Register, usw.

    Die Anweisung "TCCR1B |= (1 << WGM12);" shiftet also die Zahl 1 (binär 0000 0001) um 3 Stellen nach links (= 0000 1000) und wird anschließend mit dem Inhalt des TCCR1B-Register bitweise ODER-verknüpft. Dadurch wird das Bit Nummer 3 des TCCR1B-Registers auf 1 gesetzt, ohne die anderen Bits zu verändern.

    Die Anweisung "TCCR1B |= B1000;" bzw. "TCCR1B = TCCR1B | B1000;" bewirkt dasselbe.

    Hier sieht man aber den Vorteil der oberen Schreibweise: Möchte man ein anderes Bit setzen, braucht man keine Binärzahl zu ermitteln mit der die ODER-Verknüpfung erfolgen soll, sondern ändert nur den Namen des Bits.

    Möchte man einen zweiten Pin ebenfalls als Interrupt-Eingang verwenden, geschieht das entweder durch eine zusätzliche Anweisung, z.B.:

    TCCR1B |= (1 << WGM12);

    TCCR1B |= (1 << WGM13);

    oder die Anweisungen werden in einer Zeile geschrieben:

    TCCR1B |= (1 << WGM12) | (1 << WGM13);


    Löschen einzelner Bits:

    Um einzelne Bits zu löschen, ohne die restlichen Bits im Register zu verändern, verwendet man die bitweise UND-Anweisung.

    Am Beispiel des Löschens des WGM12-Bits sieht die Anweisung folgend aus:

    TCCR1B &= ~(1 << WGM12);

    Die Anweisung "TCCR1B &= ~(1 << WGM12);" shiftet also die Zahl 1 (binär 0000 0001) um 3 Stellen nach links (= 0000 1000), wird mit dem "~"-Symbol anschließend bitweise invertiert (=1111 0111) und mit dem Inhalt des TCCR1B-Register bitweise UND-verknüpft. Dadurch wird das Bit Nummer 3 des TCCR1B-Registers auf 0 gesetzt, ohne die anderen Bits zu verändern.

    Möchte man die Bits WGM12 und WGM13 löschen, sieht die Anweisung dazu so aus:

    TCCR1B &= ~(1 << WGM12) & ~(1 << WGM13);


    Invertieren einzelner Bits:

    Ein einzelnes Bits kann - ohne die restlichen Bits im Register zu verändern -  mit Hilfe der bitweisen EXKLUSIV ODER-Anweisung invertiert werden.

    Mit folgendem Beispiel wird das WGM12-Bit invertiert:

    TCCR1B ^= (1 << WGM12);


    Abfragen einzelner Bits:

    Möchte man wissen, ob das Bit WGM12 "0" oder "1" ist, kann das mit folgender Anweisung abfragen:

    byte bitStatus = (TCCR1B & (1 << WGM12)) >> WGM12;

    oder mit Bedingungsoperator:

    bool bitStatus = (TCCR1B & (1 << WGM12)) == (1 << WGM12) ? 1 : 0;

    Die Variable bitStatus hat als Ergebnist "0", wenn das Bit WGM12 "0" ist und "1", wenn das Bit WGM12 "1" ist.