Mengapa kode AVR menggunakan bit shifting [ditutup]

7

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.

Blair Fonville
sumber
3
Saya yang kedua ini. Bahkan memanfaatkan di mana-mana _BV(b)alih - alih (1<<b)membuat semuanya berantakan tak perlu. Saya biasanya mendefinisikan bit mnemonics dengan _BV(), mis #define ACK _BV(1).
calcium3000
2
Setelah disadari bahwa kompiler akan menafsirkan ini sebagai konstanta yang sama, yang digunakan dalam kode sumber benar-benar masalah preferensi. Dalam kode Anda sendiri, lakukan apa pun yang menurut Anda paling bijaksana; dalam memodifikasi proyek yang ada, tetap berpegang pada tradisi mereka.
Chris Stratton
3
"Karena pendekatan bitmask jelas akan menghasilkan kode pengguna akhir yang lebih mudah dibaca" - pendapat pribadi Anda. Saya merasa jauh lebih jelas menggeser 1 dan 0 ke tempat yang tepat daripada harus menebak apakah beberapa angka yang ditambahkan bersama adalah bitmask atau tidak.
Tom Carpenter
3
@TomCarpenter Menarik. Yah, mungkin saya secara tidak sengaja mengajukan pertanyaan berdasarkan opini. Either way, ada umpan balik yang bagus. Berasal dari latar belakang DSP (TI) yang lebih banyak (di mana bitmask adalah normanya), sepertinya sintaks aneh seperti itu sehingga saya pikir ada beberapa alasan konkret untuk itu.
Blair Fonville
1
@ BlairFonville Mungkin Anda sudah tahu ini, tetapi ARM bekerja persis seperti yang Anda gambarkan (dengan bitmasks).
Chi

Jawaban:

7

Kode paling sederhana berakhir dengan sedikit pergeseran bit. Mengapa bit didefinisikan sebagai posisi bit alih-alih topeng.

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".

  • Jadi tidak hanya berfungsi, itu tidak membuat lebih banyak ukuran kode.
  • 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).

Dadih
sumber
1
Aku mengerti itu. Saya tidak mempertanyakan apakah itu berhasil atau tidak. Saya tahu itu. Saya bertanya mengapa definisi ditulis sebagaimana adanya. Bagi saya itu akan sangat meningkatkan keterbacaan kode jika mereka didefinisikan sebagai topeng daripada posisi bit.
Blair Fonville
5
Saya pikir jawaban ini tidak tepat. Dia tidak pernah berbicara tentang efisiensi atau kompiler kode. Ini semua tentang kekacauan kode sumber .
pipa
4
@ Blair Fonville: tidak ada cara mudah untuk mendefinisikan makro seperti itu. Diperlukan untuk menghitung logaritma ke basis 2. Tidak ada fungsi preprosesor yang menghitung logaritma. Yaitu hanya bisa dilakukan dengan menggunakan tabel dan itu, saya pikir, akan menjadi ide yang sangat buruk.
Curd
2
@pipe: Saya tidak membicarakannya karena saya tidak menganggapnya sebagai "polusi kode" atau "kekacauan kode sumber" (atau apa pun yang Anda ingin menyebutnya). Sebaliknya saya pikir itu bahkan berguna untuk mengingatkan programmer / pembaca bahwa konstanta yang ia gunakan adalah kekuatan dua (dan itu dilakukan dengan menggunakan ekspresi shift).
Curd
1
@RJR, Blair Fonville: tentu saja mudah untuk mendefinisikan makro seperti itu TETAPI saat menggunakan definisi preprocessor sederhana ok saya akan menghindari macro preprocessor (alias preprosessor functios) bila memungkinkan karena mereka dapat melakukan debugging (melangkah melalui kode sumber C dengan debugger) sangat tidak transparan.
Curd
4

Mungkin pergeseran bit bukan satu-satunya kasus penggunaan untuk PB*definisi. Mungkin ada kasus penggunaan lain di mana PB*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 (seperti PB*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.

typedef enum
{
    CHANNEL_XL_X = 0,
    CHANNEL_XL_Y = 1,
    CHANNEL_XL_Z = 2,
    CHANNEL_G_X = 3,
    CHANNEL_G_Y = 4,
    CHANNEL_G_Z = 5,
    CHANNEL_AUX1 = 6,
    CHANNEL_AUX2 = 7
} ChannelNum;

struct MeasurementResult;

void StartMeasurement(uint8_t channel_mask);
MeasurementResult ReadMeasurementResult(ChannelNum channel_num);

main
{
    ...

    StartMeasurement( (1 << CHANNEL_XL_X) | (1 << CHANNEL_XL_Y) | (1 << CHANNEL_XL_Z) );

    meas_result_x = ReadMeasurementResult(CHANNEL_XL_X);
    meas_result_y = ReadMeasurementResult(CHANNEL_XL_Y);
    meas_result_z = ReadMeasurementResult(CHANNEL_XL_Z);
}
krambo
sumber