Dalam pemrograman AVR, bit register selalu diatur dengan menggeser 1
ke 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
, PB3
dan 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 PBx
menjadi 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)
.