Nella programmazione AVR, i bit di registro sono invariabilmente impostati spostando a sinistra 1
nella posizione dei bit appropriata - e vengono cancellati da un complemento di uno degli stessi.
Esempio: per un ATtiny85, potrei impostare PORTB, b 4 in questo modo:
PORTB |= (1<<PB4);
o cancellalo in questo modo:
PORTB &= ~(1<<PB4);
La mia domanda è: perché è fatto in questo modo? Il codice più semplice finisce per essere un casino di bit-shift. Perché i bit sono definiti come posizioni dei bit anziché come maschere.
Ad esempio, l'intestazione IO per ATtiny85 include questo:
#define PORTB _SFR_IO8(0x18)
#define PB5 5
#define PB4 4
#define PB3 3
#define PB2 2
#define PB1 1
#define PB0 0
Per me, sarebbe molto più logico definire i bit come maschere (in questo modo):
#define PORTB _SFR_IO8(0x18)
#define PB5 0x20
#define PB4 0x10
#define PB3 0x08
#define PB2 0x04
#define PB1 0x02
#define PB0 0x01
Quindi potremmo fare qualcosa del genere:
// as bitmasks
PORTB |= PB5 | PB3 | PB0;
PORTB &= ~PB5 & ~PB3 & ~PB0;
per attivare e disattivare i bit b 5 , b 3 e b 0 , rispettivamente. Al contrario di:
// as bit-fields
PORTB |= (1<<PB5) | (1<<PB3) | (1<<PB0);
PORTB &= ~(1<<PB5) & ~(1<<PB3) & ~(1<<PB0);
Il codice maschera di bit legge molto più chiaramente: imposta bit PB5
,PB3
e PB0
. Inoltre, sembrerebbe salvare le operazioni poiché i bit non devono più essere spostati.
Ho pensato che forse è stato fatto in questo modo per preservare la generalità al fine di consentire il porting del codice da un AVR n -bit a un m -bit (esempio da 8 bit a 32 bit). Ma questo non sembra essere il caso, poiché si #include <avr/io.h>
risolve in file di definizione specifici per il microcontrollore di destinazione. Anche cambiando gli obiettivi da un 8-bit ATtiny a un 8-bit Atmega (dove definizioni dei bit cambiano sintatticamente da PBx
a PORTBx
, per esempio), richiede modifiche al codice.
_BV(b)
invece di(1<<b)
rendere le cose inutilmente disordinate. In genere definisco bit mnemonici con_BV()
, ad es#define ACK _BV(1)
.