Corso programmazione PICMicro in C – Approfondimenti – Come utilizzare un encoder in quadratura per l’immissione dei dati
Ho preferito inserire questo articolo nel corso di programmazione come un’appendice e non come un capitolo a sè stante in quanto non tratta strettamente dell’utilizzo di un particolare circuito integrato o di una particolare periferica del picmicro ma piuttosto dell’utilizzo alternativo di altre tecniche già apprese fin’ora (gli interrupt e l’utilizzo dei pulsanti con l’antirimbalzo nella fattispecie). Per tale motivo, per quanto mi sia possibile, ogni tanto inserirò in questo piccolo corso anche qualche “appendice” o se preferite “appunti di utilizzo” per spiegare come fare questa o quell’altra cosa sfruttando cose che già abbiamo imparato.
In quest’articolo impareremo a conoscere uno strumento semplice e molto interessante con cui abbiamo a che fare spesso nella vita di ogni giorno e lo impareremo ad utilizzare come metodo alternativo per l’inserimento o la modifica di variabili al posto dei classici pulsanti. In più sfrutteremo in contemporanea 2 sorgenti di interrupt le quali si aiuteranno a vicenda nello svolgimento di un compito.
Molti di voi hanno un’autoradio in macchina, alcuni autoradio (soprattutto modelli di marche famose e di fascia medio/alta) hanno, per la regolazione del volume e dei toni, una manopola che ruota. I più attenti già sanno che quella manopola non è assolutamente il classico potenziometro (altrimenti come mai gira di continuo, senza blocchi? Ok qualcuno potrebbe pure obiettare che esistono i potenziometri a rotazione continua ma dal momento che costano un a cifra non si tratta assolutamente di questo caso). Quella manopola è un congegno davvero semplice ma geniale per alcuni aspetti: si tratta di un encoder rotativo (Rotary Encoder). Una “manopola” del genere la troviamo comunque un po’ dappertutto: ce l’hanno anche alcuni forni a microonde, anche se il suo utilizzo più “in voga” è attualmente quello della regolazione del volume audio negli impianti hi-fi.
Sappiamo bene che un encoder, in senso generale, è un dispositivo che serve per codificare un tipo di segnale in un’altra “forma”. Un encoder rotativo è appunto un dispositivo che permette di codificare la rotazione di un albero in un segnale elettrico.
Gli encoder rotativi vengono utilizzati, soprattutto in ambito industriale, per il computo delle velocità degli alberi di rotazione. Erano usati nei vecchi mouse con la “pallina” per far muovere il puntatore, sono utilizzatissimi anche in robotica per determinare le velocità delle ruote dei robottini e quindi correggerne la traiettoria, e come abbiamo appena detto vi è anche questo utilizzo più “ludico” per il controllo del volume su un impianto audio o per impostare la temperatura del forno a microonde.
Un encoder rotativo altro non è che una serie di “contatti” (azionati in vari modi: meccanicamente, tramite infrarossi o sfruttando l’effetto hall) che chiudono ciclicamente un contatto quando l’albero ad essi collegato effettua una rotazione. A seconda del numero di contatti e del modo in cui questi vengono chiusi, abbiamo vari tipi di encoder. In questo articolo ci occuperemo unicamente dei cosiddetti encoder in quadratura.
Per capire come è fatto un encoder in quadratura possiamo anche aprire un vecchio mouse di quelli con la pallina: per ogni asse (X e Y) la pallina mette in rotazione una ruota con tante fessure, da un lato di ciascuna ruota ci sono due diodi infrarossi e dall’altro due ricevitori (in genere accorpati in un unico blocco):
<img class="aligncenter size-full wp-image-2168" title="MouseEncoder" src="https://www.settorezero.com/wordpress/contents/2010/02/MouseEncoder.jpg" alt="MouseEncoder" width="482" height="457"> Immagine prelevata da <a href="http://www.eehomepage.com/report.php?report=20080225" target="_blank" rel="noopener">www.eehomepage.com/report.php?report=20080225</a> |
Quando di fronte ad un diodo viene a trovarsi la finestra aperta, il raggio passa mandando in conduzione il ricevitore che gli sta di fronte il quale “chiude” un contatto, quando invece tra la coppia trasmettitore/ricevitore si trova la parte di ruota “piena”, il contatto rimane aperto: all’uscita del sensore avremo quindi un segnale ad onda quadra che è indicativo del movimento che sta effettuando la ruota.
Questo particolare tipo di encoder viene definito “encoder in quadratura” perchè possiede 2 contatti che forniscono due segnali sfalsati di 90°. Ma cosa significa? Cerco di farvelo capire con delle immagini, perchè il concetto è apparentemente complicato ma nella pratica è davvero semplice. Guardiamo questa immagine stilizzata:
Abbiamo la ruota sulla quale sono disegnati dei settori: i settori neri fanno rimanere il contatto aperto, i settori bianchi fanno chiudere il contatto, i due quadratini grigi sono i due contatti dell’encoder. Come vedete i due contatti si trovano sulla ruota posizionati su due rette, passanti per il centro dell’asse di rotazione, inclinate tra loro di 90°. Quando la ruota gira, ogni contatto produrrà un’onda quadra (transizioni tra contatto chiuso e contatto aperto). Avendo i due contatti posizionati come in figura, le due onde quadre prodotte saranno anch’esse sfalsate di 90° in questo modo:
Ok, ma a cosa serve mettere due contatti quando per calcolare unicamente la velocità di rotazione di un albero basterebbe un unico contatto? Avete ragione: con un unico contatto sappiamo ogni quanti gradi di rotazione il contatto viene chiuso, e quindi quante volte viene chiuso per un giro di completo di 360°: calcolando il tempo impiegato per effettuare il giro completo, possiamo trovare la velocità di rotazione dell’albero. Se ci serve unicamente sapere la velocità di rotazione un unico contatto potrebbe anche bastare.
Ma… La manopola del volume dello stereo? Non serve certo a calcolare la velocità con la quale la stiamo ruotando! Ha difatti ben altra funzione: se la giriamo in un senso il volume aumenta, se la giriamo nell’altro il volume diminuisce, quindi c’è qualcosa in quella manopola che capisce in quale senso la stiamo ruotando.
E difatti è proprio così. Quella manopola mette in rotazione un albero sul quale è appunto montato un encoder in quadratura come quello che abbiamo visto nell’immagine. La produzione di due segnali sfalsati di 90° permette appunto, oltre che di calcolare la velocità di rotazione (che ovviamente nel caso del volume dello stereo non viene affatto calcolata perchè non serve a nulla), di stabilire anche il verso in cui l’albero sta ruotando. Come?
Innanzitutto diamo un nome ai segnali che escono dai due contatti dell’encoder: come il buon Guido Ottaviani mi ha insegnato, chiamerò un segnale PULSE e l’altro DIR, il perchè della scelta di questi due nomi, al posto dei classici A e B che in genere si trovano in giro, sarà presto chiaro.
Supponiamo di collegare questi due segnali a due ingressi di un picmicro. Collegherò un segnale ad un ingresso che mi genera un interrupt (ad esempio al pin RB0 oppure ad uno dei pin da RB4 a RB7) e questo segnale sarà quello che chiamo PULSE, e colleghiamo l’altro segnale, DIR, ad un ingresso che non genera interrupt, quindi ad un ingresso qualunque.
Ruotiamo l’albero. Ad un certo punto scatterà un interrupt perchè il contatto che fornisce il segnale PULSE passa da aperto a chiuso (o viceversa, insomma: cambia stato logico). A questo punto intercettiamo l’interrupt e confrontiamo il livello logico attuale del segnale PULSE con il livello logico attuale del segnale DIR: sta qui la “magia”!
Se entrambi i livelli logici sono uguali (entrambi alti o entrambi bassi), possiamo dire che l’albero sta ruotando in un senso (possiamo dire che l’albero sta ruotando in senso orario oppure che il nostro robottino si sta muovendo in avanti), se invece i due livelli logici sono differenti, allora l’encoder sta ruotando in senso antiorario (o il nostro robottino sta camminando all’indietro).
Un’immagine dovrebbe far capire cosa accade nella pratica:
Ho evidenziato in viola l’istante in cui scatta l’interrupt, ovvero l’istante in cui il segnale PULSE effettua il cambio di livello logico. Notiamo inoltre come i due segnali DIR e PULSE siano sfalsati tra loro di 90° come detto in precedenza. Ruotando in avanti, al verificarsi dell’interrupt, i due segnali hanno sempre il livello logico uguale tra loro, ruotando all’indietro, invece, i livelli logici sono sempre opposti. Chiamo appunto PULSE il segnale che mi genera una “pulsazione”, ovvero l’interrupt, e DIR l’altro che mi serve per poterlo confrontare e stabilire quindi la DIRezione di rotazione.
Bene, passiamo ora ad un’applicazione pratica: sfruttiamo un encoder per incrementare/decrementare una variabile. Questo sistema lo potete utilizzare per qualsiasi applicazione che richieda due pulsanti up/down: l’utilizzo di un encoder è sicuramente molto più professionale: un controllo di volume, la regolazione di un livello, di una temperatura, di un tempo, il posizionamento di un servocomando…
Per questo “esperimento” ho utilizzato un encoder rotativo meccanico di fascia economica ma che fa in maniera eccellente il suo lavoro. Tale encoder esternamente si presenta come un comune potenziometro ed in più ha anche un contatto che si chiude quando l’alberino viene premuto verso il basso:
Questo tipo di encoder nell’immagine è davvero economico (l’ho trovato su Ebay in Inghilterra per meno di 3 euro, ma se lo acquistate in Cina, e soprattutto se avete la pazienza di aspettare un mese, ne potete portare a casa ben 10, completi di manopole, per circa 10/12 euro). Come potete vedere è davvero molto piccolo, per tale motivo l’ho messo a confronto con la moneta da 2 euro, difatti quando mi è arrivato a casa sono rimasto un po’ smarrito perchè mi aspettavo qualcosa di un po’ più grande, almeno quanto un potenziometro.
Vi sono due ganci laterali per l’ancoraggio allo stampato, i tre pin vicini sono quelli dell’encoder (il centrale è il comune e i laterali sono i due contatti), gli altri due pin di fronte a quelli dell’encoder sono quelli del pulsante normalmente aperto, che si chiude premendo l’alberino. Ho visto che la nota marca PIHER (che produce potenziometri, trimmer, switch ecc) ha in catalogo un encoder molto simile a questo, lo potete vedere in questa pagina. Quello della Piher ha la stessa, identica, piedinatura e forma (non ho verificato le dimensioni).
Dovremo trattare i segnali dei due contatti dell’encoder come se fossero un normale pulsante. Trattandosi di un’encoder meccanico, la chiusura dei contatti non è pulita per cui dovremo tener conto di dover attuare anche qualche meccanismo di antirimbalzo. Utilizzando invece encoder ottici o magnetici la chiusura del contatto è pulita e quindi non dovremo preoccuparci dei rimbalzi.
Faremo arrivare il segnale di massa al comune e i due segnali forniti dai contatti saranno applicati a due ingressi del picmicro tramite le solite resistenze di pull-up. Ovviamente dobbiamo ricordarci di collegare un segnale ad un pin che produce interrupt (lo collegheremo ad RB0) e l’altro ad un pin qualsiasi: questo concetto è essenziale. Ho montato da subito l’encoder su un pezzetto di millefori collegandovi sopra tutto l’occorrente: uno strip di contatti a 90° per la connessione con una breadboard o con il circuito di utilizzo, le resistenze di pullup (anche per il pulsante, che però in questa prova non userò) e un piccolo condensatore sull’alimentazione:
Sul datasheet della Piher è consigliato l’utilizzo di due piccoli condensatori da 10nF sui due terminali dei contatti, verso massa. Per questa prova personalmente non li ho montati, anche perchè l’encoder della piher l’ho scoperto solo dopo aver fatto le prove, se fate questo circuito, montateli.
Lo schemino del circuito in foto è molto semplice:
Anche se penso di essere stato abbastanza chiaro, vi espongo comunque il layout dell’encoder (prelevato dal datasheet della Piher e uguale a quello comprato su Ebay) per maggior completezza:
Quindi ricapitolando: i due segnali che dovranno arrivare al pic sono quelli uscenti dai pin contrassegnati come A e B sullo schema dell’encoder, quello che collegate al pin RB0 (è indifferente: potete collegare o l’A o il B) lo chiameremo PULSE e l’altro lo collegheremo al pin RB1, quest’ultimo non scatena interrupt e ci serve per poterlo confrontare con l’altro segnale e poter quindi capire la direzione di rotazione, per cui lo chiameremo DIR. Il pulsante che si aziona premendo l’alberino, presente sui contatti E e D, non verrà usato in questa prova. Se voi lo volete usare, sapete come fare.
Il software che ho scritto esegue la funzione di base di un encoder in quadratura: la rotazione in senso orario incrementerà una variabile, la rotazione in senso antiorario invece la decrementerà, per dimostrarne il funzionamento il valore della variabile sarà mostrato su un display LCD compatibile Hitachi HD44780. Il circuito che ho utilizzato è lo stesso della lezione 7, al quale ho ovviamente tolto i 4 led e i 3 pulsanti (insomma tutta la parte destra del circuito), rimangono quindi di quello schema solo il display e il cicalino.
Dal momento che ho utilizzato la demoboard Freedom 2, nel codice troverete un define diverso per il cicalino e la presenza di un define per abilitare la retroilluminazione dell’LCD: se usate la Freedom 2 non dovete fare modifiche al codice, altrimenti basta cambiare un paio di define del file settings.h
Lato software, dal momento che abbiamo capito come funziona un encoder in quadratura, non dovremo far altro che intercettare l’interrupt su RBO e fare le opportune considerazioni: confrontare il segnale che ha scatenato l’interrupt con l’altro: se i due segnali hanno lo stesso livello logico, mi setterò una variabile che nel main mi produrrà un incremento e viceversa:
void interrupt ISR (void) { if (INTF) // L'interrupt è stato causato da un cambio di stato di RB0/INT ? { if (ENCODER_PULSE==ENCODER_DIR) // rotazione oraria { INTE=0; // disattivo l'interrupt su RB0/INT mode=INCR; // modalità incremento ABcounter=0; // resetto contatore } else // rotazione antioraria { INTE=0; // disattivo l'interrupt su RB0/INT mode=DECR; // modalità decremento ABcounter=0; // resetto contatore } INTF=0; // Resetto il flag interrupt su RB0/INT } // fine che interrupt verificatosi su RB0/INT |
In realtà avrei potuto gestire l’incremento e il decremento della variabile anche nella stessa routine di interrupt, ma come vedete non l’ho fatto per vari motivi:
- Le routine di interrupt, come ripetuto tantissime volte, devono essere le più snelle possibile.
- Stiamo gestendo un encoder meccanico, per cui dobbiamo inventarci un sistema per effettuare un antirimbalzo via software. Dal momento che l’antirimbalzo via software prevede un ritardo, non possiamo assolutamente gestirlo nella routine di interrupt.
Per tale motivo ho previsto un counter (ABcounter) che viene incrementato dall’interrupt sul Timer0 e che mi serve a creare un ritardo nel main. Appena scatta l’interrupt sul pin RB0/INT, disattivo subito l’interrupt (INTE=0) per non intercettarne due successivi (come detto prima: l’encoder usato è molto economico!), mi setto una variabile (mode) che nel main mi attiverà una routine per incrementare o decrementare il valore della variabile, e azzero il contatore (ABcounter) che mi blocca il controllo del successivo interrupt per un tempo stabilito nel main. Spulciatevi il codice per vedere cosa accade. Suppongo che questa non sia la migliore gestione dell’antibounce, ma mi è venuta al volo funzionando in maniera abbastanza soddisfacente: senza essa, difatti, l’encoder produceva degli incrementi/decrementi a volte esagerati. Penso anche che l’utilizzo dei due condensatori sui terminali di uscita dell’encoder migliori ulteriormente la situazione.
Consigli e suggerimenti
Nel caso in cui si voglia collegare più di un encoder (ad esempio: un robottino per il quale dobbiamo verificare la velocità e il verso di rotazione delle due ruote), dovremo fare affidamento al cambio di interrupt sulle porte RB4/RB7, abilitando il flag RBIF. Possiamo collegare un segnale pulse ad RB0 e quello dell’altra ruota ad uno dei pin RB4-RB7 o entrambi a due pin di RB4-RB7. In quest’ultimo caso dovremo predisporre altre variabili per memorizzare lo stato precedente dei segnali per poter capire quale dei due encoder ha scatenato l’interrupt dal momento che uno qualsiasi dei pin RB4-RB7 scatena l’interrupt.
Se volete realizzare un encoder per le ruote del vostro robottino potete anche utilizzare il sensore P5587 prodotto dalla Hammamatsu, che è molto piccolo ed adatto appunto a fare questo lavoro, ovviamente sulla ruota dovrete predisporre un disco con settori bianchi e neri in quanto tale sensore lavora per riflessione. Per realizzare un encoder in quadratura avrete bisogno di due sensori per ruota.
Se volete approfondire il discorso sugli encoder, la rete è piena di esempi appunto perchè in robotica gli encoder sono uno strumento davvero necessario. Posso consigliarvi di cominciare da questa voce su wikipedia in inglese (la versione in italiano è piuttosto scarna).
Links
Su Settorezero ci sono tanti altri progetti che utilizzano gli encoders:
- Esempio di menu su display LCD con encoder e PIC16F15376
- Una tastiera per media player con Arduino che utilizza l’encoder
- Utilizzare 4 encoder con Arduino sfruttando solo 3 IO
- Timer per bromografo con LCD, Encoder e PIC16F876A
- Generatore di onde quadre da 1Hz a 16MHz con Encoder e PIC16F15376