Manipolazione bit dei registri con Arduino
Impariamo a gestire input/output agendo direttamente sui bit dei registri DDRx e PORTx
Premessa: con la seguente metodologia si potrà ottimizzare il firmware a livello maniacale ma si andrà a perdere totalmente la portabilità dello stesso su schede aventi configurazione hardware diversa da quella di partenza.
Per prima cosa è necessario fare chiarezza su come sono raggruppati i pin del microcontrollore e per semplicità ci riferiremo ad un ATMEGA328 (ovvero il cuore della scheda ArduinoUNO). Come viene mostrato dalla seguente immagine i pin sono raggruppati in gruppi facilmente distinguibili:
PD0, PD1, PD2, PD3, PD4, PD5, PD6 e PD7 –> input/output digitali
PB0, PB1, PB2, PB3, PB4, PB5, PB6 e PB7 –> input/output digitali
PC0, PC1, PC2, PC3, PC4, PC5, PC6 –> input/output analogici
In questa guida trattiamo la gestione per bit solo dei primi due gruppi, ovvero degli input/output digitali.
Il microcontrollore lavora sempre su due registri: quello riguardante la topologia/dichiarazione della natura del pin e quello riguardante lo stato logico.
Individuiamo con DDRx (dove x sarà B o D) il registro riguardante la topologia del pin. Ci permetterà di settare un determinato terminale come input o come output digitale. Il registro è formato da 1 byte, ovvero 8 bit corrispondenti alle relative porte. Se il bit ha valore 1 significa che il terminale è un pin di output, se ha valore 0 si tratta di input.
Ad esempio dichiarare DDRD = B11111111 significa dichiarare tutti i terminali PDx ( PD0, PD1, PD2, PD3, PD4, PD5, PD6 e PD7 ) come output. La dicitura DDRD = B00000000 significa invece che tutte le porte PDx sono input.
NB. il bit più a sinistra corrisponde a PD7, mentre quello più a destra corrisponde a PD0.
Il secondo registro che ci serve è PORTx (dove x sarà B o D) e permette di impostare lo stato logico del terminale. Se il pin è stato precedentemente battezzato come input (nel registro DDRx) allora nel registro PORTx il valore 0 corrisponde a ingresso semplice mentre il valore 1 attiva la resistenza di PULLUP.
Se il pin è stato precedentemente batezzato come output (nel registro DDRx) allora nel registro PORTx il valore 0 corrisponde a stato logico low, mentre il valore 1 a stato logico high.
Per esempio abbiamo le due seguenti istruzioni:
DDRB = B11100111
PORTB = B11011101
si deduce che:
PB7 è un output con stato logico high,
PB6 è un output con stato logico high,
PB5 è un output con stato logico low,
PB4 è un imput con resistenza di pullup attiva,
PB3 è un input con resistenza di pullup attiva,
PB2 è un output con stato logico high,
PB1 è un output con stato logico low,
PB0 è un output con stato logico high.
L’errore da non fare è pensare di avere vantaggi a definire l’intero byte del registro rispetto ai comandi arduino pinMode o digitalWrite. I vantaggi si ottengono solo se lavoriamo a singolo bit attraverso procedure legate ad operatori logici AND e OR. Quindi i casi si riducono a due: la necessità di mettere un determinato bit a valore 0 o a valore 1.
La dicitura seguente prende il byte del registro desiderato e semplicemente mette 1 alla posizione x del byte (la posizone piu a sinistra è 7, quella più a destra è 0).
REGISTRO |= (1 << x);
Quindi, avendo PORTD=B00000000, ed eseguendo la seguente direttiva, vado a mettere 1 in sesta posizione ottenendo quindi PORTD=B01000000
PORTD |= (1 << 6);
Se invece volessimo mettere un bit a 0 dovremo utilizzare il comando (dove x è la posizione del bit all’interno del byte).
REGISTRO &= ~(1 << x);
quindi, se abbiamo per esempio PORTD = B11111111 e sfruttiamo il seguente comando porteremo uno zero in sesta posizione ottenendo PORTD = 10111111.
PORTD &= ~(1 << 6);