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.
sumber
_BV(b)
alih - alih(1<<b)
membuat semuanya berantakan tak perlu. Saya biasanya mendefinisikan bit mnemonics dengan_BV()
, mis#define ACK _BV(1)
.Jawaban:
Tidak, tidak sama sekali. Pergeseran hanya dalam kode sumber C, bukan dalam kode mesin yang dikompilasi. Semua contoh yang Anda tunjukkan dapat dan akan diselesaikan oleh kompiler pada waktu kompilasi karena mereka adalah ekspresi konstan sederhana.
(1<<PB4)
hanyalah cara untuk mengatakan "bit PB4".Masuk akal juga bagi pemrogram manusia untuk memberi nama bit berdasarkan indeks mereka (mis. 5) dan bukan berdasarkan bit mask mereka (mis. 32) karena cara ini angka berurutan 0..7 dapat digunakan untuk mengidentifikasi bit bukan kekuatan canggung dari dua (1, 2, 4, 8, .. 128).
Dan ada alasan lain (mungkin alasan utama):
File C-header tidak hanya dapat digunakan untuk kode-C tetapi juga untuk kode sumber assembler (atau kode assembler yang diuraikan dalam kode sumber C). Dalam kode assembler AVR Anda pasti tidak hanya ingin menggunakan topeng bit (yang dapat dibuat dari indeks dengan menggeser sedikit). Untuk beberapa instruksi assembler manipulasi bit AVR (misalnya SBI, CBI, BST, BLD) Anda harus menggunakan indeks bit sebagai operator langsung dalam kode op instruksi mereka.
Hanya jika Anda mengidentifikasi bit SFR berdasarkan indeks(bukan dengan bit mask) Anda dapat menggunakan pengidentifikasi tersebut secara langsung sebagai instruksi assembler langsung operan. Kalau tidak, Anda harus memiliki dua definisi untuk setiap bit SFR: satu mendefinisikan indeks bitnya (yang dapat digunakan misalnya sebagai operan dalam instruksi assembler bit manipulasi bit tersebut) dan satu mendefinisikan bit mask-nya (yang hanya dapat digunakan untuk instruksi di mana seluruh byte dimanipulasi).
sumber
Mungkin pergeseran bit bukan satu-satunya kasus penggunaan untuk
PB*
definisi. Mungkin ada kasus penggunaan lain di manaPB*
definisi digunakan secara langsung dan bukan sebagai jumlah shift. Jika demikian maka saya percaya prinsip KERING akan mengarahkan Anda untuk mengimplementasikan satu set definisi yang dapat digunakan untuk kedua kasus penggunaan (sepertiPB*
definisi ini ) daripada dua set definisi yang berbeda yang memiliki informasi berulang.Sebagai contoh, saya menulis sebuah aplikasi yang dapat melakukan pengukuran dari hingga 8 saluran ADC. Ini memiliki satu antarmuka untuk memulai pengukuran baru di mana Anda dapat menentukan beberapa saluran melalui bidang 8-bit, satu bit untuk setiap saluran. (Pengukuran dilakukan secara paralel ketika beberapa saluran ditentukan.) Kemudian memiliki antarmuka lain yang mengembalikan hasil pengukuran untuk saluran individu. Jadi satu antarmuka menggunakan nomor saluran sebagai pergeseran ke bidang-bit dan antarmuka lainnya menggunakan nomor saluran secara langsung. Saya mendefinisikan satu enumerasi untuk mencakup kedua kasus penggunaan.
sumber