Trong lập trình AVR, các bit đăng ký luôn được đặt bằng cách dịch chuyển trái sang 1
vị trí bit thích hợp - và chúng bị xóa bởi phần bổ sung của cùng một vị trí.
Ví dụ: đối với ATtiny85, tôi có thể đặt PORTB, b 4 như thế này:
PORTB |= (1<<PB4);
hoặc xóa nó như thế này:
PORTB &= ~(1<<PB4);
Câu hỏi của tôi là: Tại sao nó được thực hiện theo cách này? Mã đơn giản nhất kết thúc là một mớ hỗn độn của bit-shift. Tại sao các bit được định nghĩa là vị trí bit thay vì mặt nạ.
Chẳng hạn, tiêu đề IO cho ATtiny85 bao gồm:
#define PORTB _SFR_IO8(0x18)
#define PB5 5
#define PB4 4
#define PB3 3
#define PB2 2
#define PB1 1
#define PB0 0
Đối với tôi, sẽ hợp lý hơn nhiều khi định nghĩa các bit là mặt nạ thay thế (như thế này):
#define PORTB _SFR_IO8(0x18)
#define PB5 0x20
#define PB4 0x10
#define PB3 0x08
#define PB2 0x04
#define PB1 0x02
#define PB0 0x01
Vì vậy, chúng tôi có thể làm một cái gì đó như thế này:
// as bitmasks
PORTB |= PB5 | PB3 | PB0;
PORTB &= ~PB5 & ~PB3 & ~PB0;
để bật và tắt các bit b 5 , b 3 và b 0 tương ứng. Như trái ngược với:
// as bit-fields
PORTB |= (1<<PB5) | (1<<PB3) | (1<<PB0);
PORTB &= ~(1<<PB5) & ~(1<<PB3) & ~(1<<PB0);
Mã bitmask đọc rõ ràng hơn: set bit PB5
, PB3
và PB0
. Hơn nữa, nó dường như sẽ lưu các hoạt động vì các bit không còn cần phải thay đổi.
Tôi nghĩ có lẽ nó đã được thực hiện theo cách này để bảo toàn tính tổng quát để cho phép chuyển mã từ một n -bit AVR sang m -bit (ví dụ 8 bit đến 32 bit). Nhưng điều này dường như không xảy ra, vì nó #include <avr/io.h>
giải quyết các tệp định nghĩa cụ thể cho vi điều khiển đích. Ngay cả việc thay đổi mục tiêu từ ATtiny 8 bit sang Atmega 8 bit ( ví dụ, trong đó định nghĩa bit thay đổi cú pháp từ PBx
sang PORTBx
, chẳng hạn), yêu cầu thay đổi mã.
_BV(b)
thay vì(1<<b)
làm cho mọi thứ trở nên lộn xộn không cần thiết. Tôi thường định nghĩa bit mnemonics với_BV()
, ví dụ#define ACK _BV(1)
.