Bitmanipulation
Bitoperatoren
Bitoperatoren stammen ursprünglich aus dem Bereich des Maschinen-Codes und Assembler, existieren aber auch in den meisten Hochsprachen, wie C.
>> nach rechts schieben << nach links schieben (Bsp.: a<<b ist das gleiche wie a * 2^b; bzw. bei 1<<3 wird die 1 um drei Stellen nach links geschoben) | bitweises ODER & bitweises UND ^ bitweises exklusives ODER ~ bitweises NICHT
Bitoperatoren in dieser Schreibweise können in Assemblercode für konstante Ausdrücke benutzt werden.
Beispiel:
ldi temp, (1<<3) | (1<<1) | (1<<2) | (1<<0)
entspricht:
ldi temp, 8 | 2 | 4 | 1
entspricht:
ldi temp, 15
Bitmaske
Im Folgenden ist häufiger von dem Begriff Bitmaske die Rede. Damit wird eine Folge von einzelnen Bits bezeichnet, die den Zustand null („0“) oder eins („1“) darstellen können.
Bitmasken werden im allgemeinen dazu verwendet, um unter Anwendung eines Operators (z. B. UND, ODER, XOR) eine Eingabe zu manipulieren. Das Ergebnis ist dann die Anwendung des Operators und der Bitmaske auf die Eingabe.
Wenn ein Operator eine Funktion mit zwei Argumenten ist, dann lässt sich dessen Anwendung wie folgt schreiben:
Ergebnis = Operator( Eingabe, Bitmaske )
Die Bitmaske ist häufig eine Konstante, da diese z. B. die Information über die Position einer Information in einem Register darstellt. Das kann z. B. ein Überlaufflag in einem Timer-Statusregister sein.
Bits setzen
Wenn in einem Byte mehrere Bits auf „eins“ gesetzt werden sollen, wird dies durch eine ODER-Verknüpfung erreicht. Alle Bits, welche in der Bitmaske gleich „1“ sind, werden auf „1“ gesetzt. Alle Bits, die in der Maske auf „0“ gesetzt sind, bleiben unverändert.
AVR-Assembler
sbr r16, 0b11110000 ; setzt Bits 4..7 in r16, ist ein Pseudobefehl,
; funktioniert nur für die Arbeitsregister r16..r31
ori r16, 0b11110000 ; setzt Bits 4..7 in r16, ori ist identisch mit sbr,
; funktioniert nur für die Arbeitsregister r16..r31
sbi PORTB, 5 ; setzt Bit 5 in Port B;
sbi PORTB, PB5 ; identisch, besser lesbar;
; funktioniert nur für die IO-Register 0..0x1F
; Für IO-Register mit IO-Adresse 0x20..0x3F muss
; in/out verwendet werden:
in r16, TIMSK ; setzt Bit TOIE1 in TIMSK
sbr r16, (1<<TOIE1)
out TIMSK, r16
; Für IO-Register oberhalb der IO-Adresse 0x3F muss
; lds/sts verwendet werden:
; setzt Bit RXCIE0 in UCSR0B
lds r16, UCSR0B
sbr r16, (1<<RXCIE0)
sts UCSR0B, r16
Man beachte den Unterschied! Eine „5“ würde von sbr
als „setze Bit 2 und 0“ gedeutet (=0b00000101), während sbi
sie als „setze Bit 5“ versteht. Der Befehl sbr
erwartet ein Bitmuster für eine ODER-Verknüpfung, während der Befehl sbi
die Bitnummer benötigt. Darauf sind auch die Includefiles von Atmel im AVR-Studio (Assembler) als auch WinAVR (C) ausgelegt. Die Namen der Bits sind als Bitnummer definiert. Das ist wichtig, wenn man Register von großen AVRs manipuliert, z. B. ATmega48. Hier muss aus der Bitnummer über eine Schiebeoperation erst das Bitmuster gemacht werden.
Standard-C
PORTB |= 0xF0; // Kurzschreibweise, entspricht PORTB = PORTB | 0xF0; bitweises ODER
/* übersichtlicher mittels Bit-Definitionen */
#define MEINBIT0 0
#define MEINBIT1 1
#define MEINBIT2 2
PORTB |= ((1 << MEINBIT0) | (1 << MEINBIT2)); // setzt Bit 0 und 2 in PORTB auf "1"
Die letzte Zeile „entschlüsselt“:
(1 << n)
: Zuerst wird durch die „<<
“-Ausdrücke eine „1“ n-mal nach links geschoben. Dies ergibt somit (in Binärschreibweise) 0b00000001 für(1 << MEINBIT0)
und 0b00000100 für(1 << MEINBIT2)
.|
: Das Ergebnis wird bitweise ODER-verknüpft, also 0b00000001 oder 0b00000100 wird zu 0b00000101.|=
: Diese Maske wird mit dem aktuellen Inhalt von PORTB bitweise ODER-verknüpft und das Ergebnis PORTB wieder zugewiesen.
PORTB |= variable; // Kurzschreibweise
PORTB = PORTB | variable; // lange Schreibweise
- Ist PORTB vorher z. B. 0b01111010, dann ist der Inhalt nach der Operation 0b01111010 oder 0b00000101 = 0b01111111, die gewünschten Bits sind somit gesetzt!
Anmerkung: Will man das gezeigte Beispiel der Bitmanipulation auf größere Datentypen anwenden, ist zu beachten, dass der Compiler in der Operation (1 << MEINBIT1)
stillschweigend, gemäß den C-Regeln, die 1 als Integer-Typ ansieht. Beim AVR-GCC bedeutet das 16-Bit/signed und die folgende Operation bringt ggf. nicht das gewünschte Ergebnis (Stichwort: „Integer Promotion“).
Angenommen, Bit 15 soll in einer 32-Bit weiten Variable gesetzt werden.
#define MEINBIT15 15
#define MEINBIT42 42
uint32_t reg_32; /* uint32_t definiert per typedef z. B. in stdint.h */
uint64_t reg_64; /* uint64_t definiert per typedef z. B. in stdint.h */
reg_32 |= (1 << MEINBIT15); /* FEHLER: Setzt die Bits 31..15, da ((int)1 << 15) == 0xFFFF8000 */
reg_32 |= ((uint32_t)1 << MEINBIT15); /* Hier wird nur Bit 15 gesetzt. */
reg_32 |= (1U << MEINBIT15); /* */
reg_32 |= (1L << MEINBIT15); /* andere Schreibweise. */
reg_64 |= (1LL << MEINBIT42); /* Hier wird nur Bit 42 gesetzt,
andere Schreibweise für 64 Bit (long long). */
Bei Compilern für 32-bit-Controller (z. B. ARM7TDMI) sind Integers per default 32-bit und Konstanten sind somit implizit ebenfalls 32-bit weit. Man sollte aber dennoch die oben gezeigte Vorgehensweise verwenden, um Probleme zu vermeiden, die entstehen könnten, wenn Code unter verschiedenen Plattformen/Compilern verwendet werden soll.
Bits löschen
Wenn in einem Byte mehrere Bits auf „null“ gesetzt werden sollen, wird dies durch eine UND-Verknüpfung erreicht. Alle Bits, welche in der Bitmaske gleich „0“ sind, werden auf „0“ gesetzt. Alle Bits, die in der Maske auf „1“ gesetzt sind, bleiben unverändert.
AVR-Assembler
cbr r16, 0b00001111 ; löscht Bits 0..3 in r16, ist ein Pseudobefehl,
; funktioniert nur für die Arbeitsregister r16..r31
andi r16, 0b11110000 ; löscht Bits 0..3 in r16, andi ist identisch mit cbr,
; funktioniert nur für die Arbeitsregister r16..r31
andi r16, ~0b00001111 ; andere Schreibweise, hier wird die Bitmaske durch ~ invertiert,
; dadurch kann man einfach alle zu löschenden Bit als '1' angeben
; so wie bei den Bitmasken für das Setzen von Bits (positive Logik)
cbi PORTB, 5 ; löscht Bit 5 in Port B
cbi PORTB, PB5 ; identisch, besser lesbar
; funktioniert nur für die IO-Register 0..31
; Für IO-Register mit IO-Adresse 0x20..0x3F muss
; in/out verwendet werden, weil dieser Bereich nicht
; bitadressierbar ist:
in r16, TIMSK ; löscht Bit TOIE1 in TIMSK
cbr r16, 1<<TOIE1
out TIMSK, r16
; Für IO-Register oberhalb der IO-Adresse 0x3F muss
; lds/sts verwendet werden:
; löscht Bit RXCIE0 in UCSR0B
lds r16, UCSR0B
cbr r16, 1<<RXCIE0
sts UCSR0B, r16
Auch hier gilt: Man beachte den Unterschied! Eine „5“ würde von cbr
als „lösche Bit 2 und 0“ gedeutet, während cbi
sie als „lösche Bit 5“ versteht. Der Befehl cbr
erwartet ein Bitmuster für eine UND-NOT-Verknüpfung (nicht zu verwechseln mit NAND), während der Befehl cbi
die Bitnummer benötigt. Darauf sind auch die Includefiles von Atmel im AVR-Studio (Assembler) als auch WinAVR (C) ausgelegt. Die Namen der Bits sind als Bitnummer definiert. Das ist wichtig, wenn man Register von großen AVRs manipuliert, z. B. ATmega48. Hier muss aus der Bitnummer über eine Schiebeoperation „<<
“ erst das Bitmuster gemacht werden.
Standard-C
PORTB &= 0xF0; // entspricht PORTB = PORTB & 0xF0; bitweises UND
// Bits 0..3 (das "niederwertige" Nibble) werden gelöscht
/* übersichtlicher mittels Bit-Definitionen */
#define MEINBIT0 0
#define MEINBIT1 1
#define MEINBIT2 2
PORTB &= ~((1 << MEINBIT0) | (1 << MEINBIT2)); // löscht Bits 0 und 2 in PORTB
Die letzte Zeile entschlüsselt:
(1 << n)
: Zuerst wird durch die „<<
“-Ausdrücke eine „1“ n-mal nach links geschoben. Dies ergibt somit (in Binärschreibweise) 0b00000001 für(1 << MEINBIT0)
und 0b00000100 für(1 << MEINBIT2)
.|
: Das Ergebnis wird bitweise ODER-verknüpft, also 0b00000001 oder 0b00000100 wird zu 0b00000101.~
: Der Wert in der Klammer wird bitweise invertiert, aus 0b00000101 wird 0b11111010.&=
: PORTB wird mit der berechneten Maske UND-verknüpft und das Ergebnis wieder PORTB zugewiesen.
PORTB &= variable; // Kurzschreibweise
PORTB = PORTB & variable; // lange Schreibweise
- Ist PORTB vorher z. B. 0b01111111, dann ist der Inhalt nach der Operation 0b01111111 und 0b11111010 = 0b01111010, die gewünschten Bits 0 und 2 sind somit gelöscht.
Die C-Ausdrücke mittels Definitionen von Bitnummern und Schiebeoperator (<<
) sehen auf den ersten Blick etwas „erschreckend“ aus und sind mehr „Tipparbeit“, funktionieren aber universell und sind deutlicher und nachvollziehbarer als „handoptimierte“ Konstanten. Bei eingeschalteter Optimierung löst der Compiler die Ausdrücke mit konstanten Werten bereits zur Compilierungszeit auf und es entsteht kein zusätzlicher Maschinencode. Bei AVR sind die Definitionen meist Teil der Entwicklungsumgebungen (bei avr-libc z. B. implizit durch #include <avr/io.h>
). Sie entsprechen den Angaben und Beispielen in den Datenblättern und sind damit de facto ein Standard beim Zugriff auf Bits in Hardware-Registern.
Wichtiger Hinweis: Die ODER-Verknüpfung und die anschließende Invertierung kann man nicht vertauschen (Theorem von DeMorgan)! Folgendes Beispiel soll die Richtigkeit der Aussage zeigen:
~(0b0001 | 0b0010) == 0b1100
~0b0001 | ~0b0010 == 0b1111
Noch ein wichtiger Hinweis: Der Operator ~
mit einem Operanden vom Typ int
negiert nur so viele Bits, wie der Typ int
hat.
Will man ein Bit in einer breiteren Variablen löschen, dann sollte die nach links zu shiftende „1“ den Typ dieser Variablen haben.
Ein Programm, welches das verdeutlicht:
#include <stdio.h>
#include <stdint.h>
int main(int argc, const char* argv[])
{
int bit = 60;
uint64_t ui64;
printf("sizeof(int)=%d\n", (int)sizeof(int));
ui64 = 0xFFFFFFFFFFFFFFFF;
ui64 &= ~(1<<60); /* Keine Wirkung bei sizeof(int) < 8 */
/* gcc warnt sogar:
* gcc -Wall bit_clear.c -o bit_clear
* bit_clear.c: In function ‘main’:
* bit_clear.c:11:5: warning: left shift count >= width of type [enabled by default]
*/
printf("%d\n", ui64!=0xFFFFFFFFFFFFFFFF);
ui64 = 0xFFFFFFFFFFFFFFFF;
ui64 &= ~((uint64_t)1<<60); /* OK */
printf("%d\n", ui64!=0xFFFFFFFFFFFFFFFF);
ui64 = 0xFFFFFFFFFFFFFFFF;
ui64 &= ~(1LL<<60); /* auch OK, und kürzer */
printf("%d\n", ui64!=0xFFFFFFFFFFFFFFFF);
ui64 = 0xFFFFFFFFFFFFFFFF;
ui64 &= ~(1<<bit); /* Ohne Warnung, und funktioniert manchmal, je nach Optimierung */
printf("%d\n", ui64!=0xFFFFFFFFFFFFFFFF);
return 0;
}
/* Ausgabe auf meinem PC ohne Optimierung:
* sizeof(int)=4
* 0
* 1
* 1
* 1
* Ausgabe auf meinem PC mit -O2
* 0
* 1
* 1
* 0
*/
Niederwertigstes gesetztes Bit löschen (Standard-C)
Folgender Code löscht von allen 1-Bits in einer Integer-Variablen das niederwertigste, unabhängig von der Position desselben.
Beispiel: 01101000 → 01100000
uint8_t byte;
byte = irgendwas();
byte = byte & (byte - 1); /* Diese seltsame Operation löscht das
niederwertigste 1-Bit */
Beispiel:
Byte : 01101000 Byte-1: 01100111 Ergebnis: 01100000
Das funktioniert also mit jeder beliebigen Zahl.
Dies kann bspw. zur schnellen Paritätsgenerierung eingesetzt werden:
uint8_t pareven(uint8_t byte) {
uint8_t par = 0;
while(byte) {
byte = byte & (byte - 1);
par = ~par;
}
return par;
}
Das genannte gilt natürlich nicht nur für 8-Bit-Integers, sondern für beliebige, vom Compiler unterstützte Wortlängen.
Bits invertieren
Im allgemeinen Sprachgebrauch oft Toggeln genannt (aus dem Englischen: to toggle). Wenn in einem Byte mehrere Bits invertiert („getoggelt“) werden sollen, wird dies durch eine XOR-Verknüpfung erreicht. Alle Bits, welche in der Bitmaske gleich „1“ sind, werden invertiert. Alle Bits, die in der Maske auf „0“ gesetzt sind, bleiben unverändert.
AVR-Assembler
Bei AVRs erlaubt dies folgender Assemblercode. Hier wird ein Ausgangspin invertiert.
sbic PortB, 0 ; Überspringe den nächsten Befehl, wenn das Bit 0 im Port gelöscht ist
rjmp ClrBitNow ; Springe zu ClrBitNow
sbi PortB, 0 ; Setze Bit 0 in PortB
rjmp BitReady ; Springe zu BitReady
ClrBitNow:
cbi PortB, 0 ; Lösche Bit 0 in PortB
BitReady:
Noch kürzer geht's so: Die zweite Zeile mit dem Befehl ldi
lädt die Bitmaske, in welcher die zu toggelnden Bits auf „1“ gesetzt sind. In diesem Beispiel wird das dritte Bit invertiert. Der Vorteil dieser Methode ist neben der Kürze und Übersichtlichkeit auch die Möglichkeit, bis zu acht Bits gleichzeitig zu toggeln. Diese Methode ist natürlich auch auf normale Daten anwendbar, nicht nur auf IO-Ports.
in R24, PORTE ; Daten lesen
ldi R25, 0x04 ; Bitmaske laden, hier Bit #2
eor R24, R25 ; Exklusiv ODER
out PORTE, R24 ; Daten zurückschreiben
Eine andere Möglichkeit gibt es, wenn man nur das 8. Bit kippen will:
in r16, PORTB
subi r16, 0x80
out PORTB, r16
Standard-C
PORTB ^= (1<<PB0); /* XOR, Kurzschreibweise für PORTB = PORTB ^ (1<<PB0) */
Neuere ATmegas
Bei den neueren ATmegas (z. B. ATmega48) kann man (auf Ausgang gesetzte) IO-Pins direkt ohne den Umweg über Register toggeln, indem man das entsprechende Bit im PINx-Register setzt:
sbi PIND, 2 ; Bit 2 von Port D toggeln
8051er
cpl bitadresse
Bits prüfen
Will man prüfen, ob ein oder mehrere Bits in einer Variablen gesetzt oder gelöscht sind, muss man sie mit einer Bitmaske UND-verknüpfen. Die Bitmaske muss an den Stellen der zu prüfenden Bits eine „1“ haben, an allen anderen eine „0“.
- Ist das Ergebnis gleich null, sind alle geprüften Bits gelöscht.
- Ist das Ergebnis ungleich null, ist mindestens ein geprüftes Bit gesetzt.
- Ist das Ergebnis gleich der Bitmaske, sind alle geprüften Bits gesetzt.
AVR-Assembler
Der AVR hat spezielle Befehle, um direkt einzelne Bits in den CPU-Registern r0…r31 sowie den IO-Registern 0…0x1F zu prüfen.
; Befehle zur Prüfung von einzelnen Bits
sbrs r16,3 ; überspringe den nächsten Befehl, wenn in r16 Bit #3 gesetzt ist
rjmp bit_ist_nicht_gesetzt
sbrc r16,5 ; überspringe den nächsten Befehl, wenn in r16 Bit #5 gelöscht ist
rjmp bit_ist_nicht_geloescht
sbis timsk,3 ; überspringe den nächsten Befehl, wenn in timsk Bit #3 gesetzt ist
rjmp bit_ist_nicht_gesetzt
sbic timsk,5 ; überspringe den nächsten Befehl, wenn in timsk Bit #5 gelöscht ist
rjmp bit_ist_nicht_geloescht
; Befehle zur Prüfung von mehreren Bits
andi r16,0b1010 ; prüfe Bit #1 und #3 in r16
breq alle_bits_sind_geloescht
andi r16,0b1010 ; prüfe Bit #1 und #3 in r16
brne mind_ein_bit_ist_gesetzt
andi r16,0b1010 ; prüfe Bit #1 und #3 in r16
cpi r16,0b1010
breq alle_bits_sind_gesetzt
Standard-C
// prüfe, ob Bit 4 in der Variablen tmp gelöscht ist
// die Klammer ist wichtig!
if (!(tmp & 0x10)) {
// hier die Anweisungen, wenn das Bit gelöscht ist
}
// prüfe, ob Bit 0 und Bit 4 in der Variablen tmp gelöscht sind
// die Klammer ist wichtig!
if ((tmp & 0x11) == 0) {
// hier die Anweisungen, wenn beide Bits gelöscht sind
}
// prüfe, ob Bit 0 oder Bit 4 in der Variablen tmp gesetzt ist
if (tmp & 0x11) {
// hier die Anweisungen, wenn mindestens ein Bit gesetzt ist
}
// prüfe, ob Bit 0 oder Bit 4 in der Variablen tmp gelöscht sind
if (~tmp & 0x11) {
// hier die Anweisungen, wenn mindestens ein Bit gelöscht ist
}
// prüfe, ob Bit 4 in der Variablen tmp gesetzt ist
if (tmp & 0x10) {
// hier die Anweisungen, wenn das Bit gesetzt ist
}
// prüfe, ob Bit 0 und Bit 4 in der Variablen tmp gesetzt sind
// die Klammer ist wichtig!
if ((tmp & 0x11) == 0x11) {
// hier die Anweisungen, wenn beide Bits gesetzt sind
}
Hilfsfunktionen zur Bitmanipulation in C/C++
Um "einfacher" elementare Bitmanipulationen durchzuführen bietet es sich an einige Hilfsfunktionen zu definieren. Dabei gibt es zwei verschiedene Möglichkeiten diese zu realiseren:
- Als C-Makro C_Makros
- Als Inline-Funktion
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 Ersetzungen handelt - bei der Verwendung von Inline-Funktionen hingegen gibt es eine "brauchbare" Fehlermeldung!
Beispiele - Inline Variante
// Achtung: Zugriffe erfolgen über Pointer
// PORTA, PB2 setzen
BIT_SET(&PORTA, PB2);
// PORTC, PB0 löschen
BIT_CLEAR(&PORTC, PB0);
// PORTA, PB2 direkt setzen
// HIGH
BIT_BOOL_SET(&PORTA, PB2, 1);
// LOW
BIT_BOOL_SET(&PORTA, PB2, 0);
Beispiele - MakroVariante
// Achtung: Zugriffe erfolgen direkt über die Variablen/Portnamen
// PORTA, PB2 setzen
BIT_SET(PORTA, PB2);
// PORTC, PB0 löschen
BIT_CLEAR(PORTC, PB0);
Um die Hilfsfunktionen verwenden zu können einfach folgenden Code in eine neue Header-Datei (z.B. BitIO.h) kopieren:
Hilfsfunktionen als Inline-Methoden
Achtung: Wenn nur ein C Compiler verwendet wird, kennt dieser den Typ "bool" nicht, dieser muss dann vorher definiert werden!
/**
* BitIO.h
* @author Andi Dittrich <http://andidittrich.de>
* @version 1.0
* @license MIT Style X11 License
*/
#include <inttypes.h>
#ifndef BITIO_H_
#define BITIO_H_
// set bit
static inline void BIT_SET(volatile uint8_t *target, uint8_t bit) __attribute__((always_inline));
static inline void BIT_SET(volatile uint8_t *target, uint8_t bit){
*target |= (1<<bit);
};
// set clear
static inline void BIT_CLEAR(volatile uint8_t *target, uint8_t bit) __attribute__((always_inline));
static inline void BIT_CLEAR(volatile uint8_t *target, uint8_t bit){
*target &= ~(1<<bit);
};
// bit toogle
static inline void BIT_TOGGLE(volatile uint8_t *target, uint8_t bit) __attribute__((always_inline));
static inline void BIT_TOGGLE(volatile uint8_t *target, uint8_t bit){
*target ^= (1<<bit);
};
// set bit by boolean
static inline void BIT_BOOL_SET(volatile uint8_t *target, uint8_t bit, bool enable) __attribute__((always_inline));
static inline void BIT_BOOL_SET(volatile uint8_t *target, uint8_t bit, bool enable){
if (enable){
BIT_SET(target, bit);
}else{
BIT_CLEAR(target, bit);
}
};
#endif /* BITIO_H_ */
Hilfsfunktionen als C-Makro (nicht empfohlen)
/* Bit setzen */
#define set_bit(var, bit) ((var) |= (1 << (bit)))
/* Bit löschen */
#define clear_bit(var, bit) ((var) &= (unsigned)~(1 << (bit)))
/* Bit togglen */
#define toggle_bit(var,bit) ((var) ^= (1 << (bit)))
Bitmanipulation beim MSP430
Beim MSP430 und dessen Compilern sind die Bitnamen meist anders definiert. Und zwar nicht als Bitnummer, sondern als Bitmuster. Darum schreibt man dort die Bitzugriffe in C anders. Das kann auch bei anderen Mikrocontrollern bzw. C-Compilern so sein. Wichtig ist, dass man seine eignen Definitionen in der gleichen Weise wie der Compiler anlegt, um Verwirrung zu vermeiden, siehe Strukturierte Programmierung auf Mikrocontrollern
// Definition von Bitnamen in den Headerfiles des Compilers
#define PD4 4 // Definition im AVR GCC als Bitnummer
#define PD5 5
#define P14 (1<<4) // Definition im MSP430 GCC als Bitmuster
#define P15 (1<<5)
// Bitmanipulation im Programm
DDRD = (1<<PD5) | (1<<PD4); // AVR GCC
P1DIR = P15 | P14; // MSP430 GCC
Siehe auch
- Forumsbeitrag: Bits aus einem Array extrahieren
- Forumsbeitrag: Bits für ein Schieberegister zusammenstellen, TLC5941
- Forumsbeitrag: TLC5947 und ATmega16 Bitmanipulation
- Forumsbeitrag: Bitreihenfolge ändern
- Forumsbeitrag: Übersichtliche Abfrage von vielen Pins
- Forumsbeitrag: Verschiedene Möglichkeiten der Bitmanipulation bei IO-Registern
Weblinks
- AVR Built-in Functions – spezielle Funktion im avr-gcc zur schnellen Bitvertauschung (englisch)
- Sean Eron Anderson: Bit Twiddling Hacks – Sammlung von komplexeren Bitmanipulationen (englisch)
- Günther Jena: Bitmanipulation mit C – mit Übungen