Dalam pemrograman AVR, bit register selalu diatur dengan menggeser 1ke kiri ke posisi bit yang sesuai - dan mereka dibersihkan oleh komplemen yang sama.
Contoh: untuk ATtiny85, saya mungkin menetapkan PORTB, b 4 seperti ini:
PORTB |= (1<<PB4);
atau hapus seperti ini:
PORTB &= ~(1<<PB4);
Pertanyaan saya adalah: Mengapa dilakukan seperti ini? Kode paling sederhana berakhir dengan sedikit pergeseran bit. Mengapa bit didefinisikan sebagai posisi bit alih-alih topeng.
Misalnya, tajuk IO untuk ATtiny85 menyertakan ini:
#define PORTB _SFR_IO8(0x18)
#define PB5 5
#define PB4 4
#define PB3 3
#define PB2 2
#define PB1 1
#define PB0 0
Bagi saya, akan jauh lebih logis untuk mendefinisikan bit sebagai masker (seperti ini):
#define PORTB _SFR_IO8(0x18)
#define PB5 0x20
#define PB4 0x10
#define PB3 0x08
#define PB2 0x04
#define PB1 0x02
#define PB0 0x01
Jadi kita bisa melakukan sesuatu seperti ini:
// as bitmasks
PORTB |= PB5 | PB3 | PB0;
PORTB &= ~PB5 & ~PB3 & ~PB0;
untuk mengaktifkan dan mematikan bit b 5 , b 3 , dan b 0 . Sebagai lawan:
// as bit-fields
PORTB |= (1<<PB5) | (1<<PB3) | (1<<PB0);
PORTB &= ~(1<<PB5) & ~(1<<PB3) & ~(1<<PB0);
Kode bitmask membaca lebih jelas: set bit PB5, PB3dan PB0. Selain itu, tampaknya akan menghemat operasi karena bit tidak perlu lagi digeser.
Saya pikir mungkin itu dilakukan dengan cara ini untuk menjaga generalisasi untuk memungkinkan kode porting dari AVR n- bit ke m- bit (contoh 8-bit ke 32-bit). Tapi ini tampaknya tidak terjadi, karena #include <avr/io.h>memutuskan untuk file definisi khusus untuk mikrokontroler target. Bahkan mengubah target dari ATtiny 8-bit ke Atmega 8-bit (di mana definisi bit berubah secara sintaksis dari PBxmenjadi PORTBx, misalnya), memerlukan perubahan kode.
_BV(b)alih - alih(1<<b)membuat semuanya berantakan tak perlu. Saya biasanya mendefinisikan bit mnemonics dengan_BV(), mis#define ACK _BV(1).