En la programación AVR, los bits de registro se establecen invariablemente desplazando 1
a la izquierda a la posición de bit adecuada, y se borran con el complemento de uno de los mismos.
Ejemplo: para un ATtiny85, podría configurar PORTB, b 4 así:
PORTB |= (1<<PB4);
o despejarlo así:
PORTB &= ~(1<<PB4);
Mi pregunta es: ¿por qué se hace de esta manera? El código más simple termina siendo un desastre de cambios de bits. ¿Por qué los bits se definen como posiciones de bits en lugar de máscaras?
Por ejemplo, el encabezado IO para el ATtiny85 incluye esto:
#define PORTB _SFR_IO8(0x18)
#define PB5 5
#define PB4 4
#define PB3 3
#define PB2 2
#define PB1 1
#define PB0 0
Para mí, sería mucho más lógico definir los bits como máscaras (así):
#define PORTB _SFR_IO8(0x18)
#define PB5 0x20
#define PB4 0x10
#define PB3 0x08
#define PB2 0x04
#define PB1 0x02
#define PB0 0x01
Entonces podríamos hacer algo como esto:
// as bitmasks
PORTB |= PB5 | PB3 | PB0;
PORTB &= ~PB5 & ~PB3 & ~PB0;
para activar y desactivar los bits b 5 , b 3 y b 0 , respectivamente. Opuesto a:
// as bit-fields
PORTB |= (1<<PB5) | (1<<PB3) | (1<<PB0);
PORTB &= ~(1<<PB5) & ~(1<<PB3) & ~(1<<PB0);
El código de máscara de bits lee mucho más claramente: bits fijados PB5
, PB3
y PB0
. Además, parecería guardar operaciones ya que los bits ya no necesitan ser desplazados.
Pensé que tal vez se hizo de esta manera para preservar la generalidad con el fin de permitir el código de portabilidad de un AVR de n bits a un m- bit (por ejemplo, 8 bits a 32 bits). Pero este no parece ser el caso, ya que #include <avr/io.h>
resuelve los archivos de definición específicos del microcontrolador de destino. Incluso el cambio de objetivos de un ATtiny de 8 bits a un Atmega de 8 bits (donde las definiciones de bits cambian sintácticamente de PBx
a PORTBx
, por ejemplo), requiere cambios de código.
_BV(b)
lugar de(1<<b)
hacer las cosas innecesariamente desordenadas. Normalmente defino la mnemotecnia de bits con_BV()
, por ejemplo#define ACK _BV(1)
.