В программировании AVR биты регистров всегда устанавливаются смещением влево 1
в соответствующую позицию бита, и они очищаются с помощью одного и того же дополнения.
Пример: для ATtiny85 я мог бы установить PORTB, b 4 следующим образом:
PORTB |= (1<<PB4);
или очистить это так:
PORTB &= ~(1<<PB4);
У меня вопрос: почему это так? Самый простой код заканчивает тем, что был беспорядочным сдвигом. Почему биты определяются как битовые позиции вместо масок.
Например, заголовок ввода-вывода для ATtiny85 включает в себя следующее:
#define PORTB _SFR_IO8(0x18)
#define PB5 5
#define PB4 4
#define PB3 3
#define PB2 2
#define PB1 1
#define PB0 0
Для меня было бы намного логичнее вместо этого определять биты как маски (как это):
#define PORTB _SFR_IO8(0x18)
#define PB5 0x20
#define PB4 0x10
#define PB3 0x08
#define PB2 0x04
#define PB1 0x02
#define PB0 0x01
Таким образом, мы могли бы сделать что-то вроде этого:
// as bitmasks
PORTB |= PB5 | PB3 | PB0;
PORTB &= ~PB5 & ~PB3 & ~PB0;
включить или выключить биты b 5 , b 3 и b 0 соответственно. В отличие от:
// as bit-fields
PORTB |= (1<<PB5) | (1<<PB3) | (1<<PB0);
PORTB &= ~(1<<PB5) & ~(1<<PB3) & ~(1<<PB0);
Код битовый читает гораздо более четко: набор бит PB5
, PB3
и PB0
. Кроме того, казалось бы, чтобы сохранить операции, так как биты больше не должны быть сдвинуты.
Я подумал, может быть, это было сделано таким образом, чтобы сохранить общность, чтобы позволить переносить код с n -битного AVR на m -бит (например, 8-битный или 32-битный). Но, похоже, это не так, поскольку он #include <avr/io.h>
разрешает файлы определения, специфичные для целевого микроконтроллера. Даже изменяя цели из 8-битного Attiny к 8-битовой Atmega (где изменить битое определение синтаксический от PBx
до PORTBx
, к примеру), требует изменений коды.
_BV(b)
вместо того, чтобы(1<<b)
излишне запутывать вещи. Я обычно определяю битовую мнемонику с помощью_BV()
, например#define ACK _BV(1)
.