Na programação do AVR, os bits de registro são invariavelmente configurados deslocando-se a esquerda 1
para a posição de bit apropriada - e são limpos pelo complemento do mesmo.
Exemplo: para um ATtiny85, eu poderia definir PORTB, b 4 como este:
PORTB |= (1<<PB4);
ou limpe assim:
PORTB &= ~(1<<PB4);
Minha pergunta é: Por que isso é feito dessa maneira? O código mais simples acaba sendo uma bagunça de mudanças de bits. Por que os bits são definidos como posições de bits em vez de máscaras.
Por exemplo, o cabeçalho de E / S para o ATtiny85 inclui isso:
#define PORTB _SFR_IO8(0x18)
#define PB5 5
#define PB4 4
#define PB3 3
#define PB2 2
#define PB1 1
#define PB0 0
Para mim, seria muito mais lógico definir os bits como máscaras (assim):
#define PORTB _SFR_IO8(0x18)
#define PB5 0x20
#define PB4 0x10
#define PB3 0x08
#define PB2 0x04
#define PB1 0x02
#define PB0 0x01
Então, poderíamos fazer algo assim:
// as bitmasks
PORTB |= PB5 | PB3 | PB0;
PORTB &= ~PB5 & ~PB3 & ~PB0;
para ativar e desativar os bits b 5 , b 3 e b 0 , respectivamente. Ao contrário de:
// as bit-fields
PORTB |= (1<<PB5) | (1<<PB3) | (1<<PB0);
PORTB &= ~(1<<PB5) & ~(1<<PB3) & ~(1<<PB0);
O código bitmask lê muito mais claramente: bits definidos PB5
, PB3
e PB0
. Além disso, parece salvar operações, já que os bits não precisam mais ser trocados.
Pensei que talvez tivesse sido feito dessa maneira para preservar a generalidade, a fim de permitir a transferência de código de um AVR de n bits para um m -bit (exemplo de 8 bits para 32 bits). Mas isso não parece ser o caso, pois #include <avr/io.h>
resolve os arquivos de definição específicos para o microcontrolador de destino. Mesmo alterar alvos de um ATtiny de 8 bits para um Atmega de 8 bits (onde as definições de bits mudam sintaticamente de PBx
para PORTBx
, por exemplo), requer alterações de código.
_BV(b)
vez de(1<<b)
torna as coisas desnecessariamente confusas. Eu normalmente defino mnemônicos de bits com_BV()
, por exemplo#define ACK _BV(1)
.