Newsgroup: it.hobby.elettronica.digitale
Titolo: Leggere un rotary encoder con linux

Ho una scheda ARM9 su cui ci gira un linux. Ho provato a collegare i due 
segnali A e B di un encoder rotativo ad due GPIO e a decodificare le 
rotazioni mediante polling.
 
Fin quando il polling è continuo (mettendolo in un while(1) senza 
attese), riesco correttamente a decodificare tutte le rotazioni, senza 
perderne alcuna e senza interpretazioni errate.  Ma questo mi porta ad 
avere un processo che non va mai in idle e dà fastidio ad altri thread 
in esecuzione.
 
Se nel ciclo di polling dei segnali dell'encoder metto una pausa, anche 
di qualche microsecondo, inizio a perdere i colpi dell'encoder e ad 
avere interpretazioni errate.
 
L'ideale sarebbe gestire il polling in modo real-time all'interno di un 
interrupt di un timer oppure collegare i due segnali ad due GPIO con 
interrupt e gestire le rotazioni nell'ISR.  Purtroppo in entrambi i casi 
dovrei scendere ad un livello troppo basso e cioè a livello kernel e 
device drivers, cosa che mi sembra estremamente complicata (anche perchè 
non ho purtroppo i sorgenti esatti del kernel che gira su questa scheda).
 
Avevo pensato di sfruttare una UART libera del microprocessore che è 
ovviamente gestita, dal relativo device driver di linux, ad interrupt e 
che, quindi, non perdere alcun carattere.  In pratica pensavo di 
convertire i due segnali A e B in un unico segnale da collegare al pin 
RX della UART.
 
La sequenza dei segnali A/B corrispondente ad una rotazione oraria 
dovrebbe generare una sequenza "UART compatibile", tipo il byte 0xFF che 
è un semplice impulso negativo della durata pari a 1/B (dove B è il 
baudrate della UART).
L'altra sequenza corrispondente ad una rotazione antioraria dovrebbe 
generare un'altra sequenza "UART compatibile", tipo il byte 0xFE che è 
sempre un impulso negativo, ma della durata di 2/B (bit di start, più il 
bit meno significativo di 0xFE).
 
Avete proposte su come trasformare i segnali A/B dell'encoder nel 
segnale RX della UART?
 

 segnale RX della UART?
 
 
Se già prevedi di condizionare i segnali A e B con un minimo di 
elettronica, tanto vale metterci un ATtiny a 8 pin e mandare via seriale 
direttamente il dato.
 
Marco


Lo so, hai ragione da vendere... ma volevo evitare un elemento 
programmabile...
 
 

Il 19/11/2011 16:58, Marco Trapanese ha scritto:

A proposito dell'ATtiny che non conosco, ci sarebbe la possibilità di 
programmarlo all'accensione della macchina via seriale, visto che 
comunque devo collegarlo via UART al microprocessore dove gira Linux? 
Naturalmente intendo senza bootloader, cioè proprio vergine, per evitare 
la fase di programmazione "manuale" del microcontrollore.
 
Conosco dei microcontrollori che hanno dei bootloader rommati che 
leggono il nuovo firmware da seriale, ma non so se è il caso degli ATtiny.

 leggono il nuovo firmware da seriale, ma non so se è il caso degli ATtiny.
 
 
Purtroppo no, gli AVR non hanno bootloader integrato.
Anche a me sarebbe stra-utile, soprattutto per l'smd.
Altri produttori li propongono ma non saprei se anche su quelli da 8-bit.
 
Ciao
Marco
 

 programmabile...
 
 
Prova a guardare gli LS7183-LS7184 della LSI.
A seconda della tua applicazione potrebbero risolverti il problema.
 
Marco

 A seconda della tua applicazione potrebbero risolverti il problema.
 
Esistono chip con i contatori integrati:
 
LS7266R1
HCTL-2001, HCTL-2017, HCTL-2021
 
ma hanno l'interfaccia ad 8 bit il che non è sbagliato vista la
rapidità di acquisizione tipicamente necessaria.
 
Puta caso, sto sviluppando un'interfaccia con AVR per encoder onde
azionare un grosso pantografo con pressa che ho commissionato per usi
alimentari.
Se pozz ha voglia e pazienza, posso estrapolare la parte encoder (con
filtro e contatori su RS232) per impementarla su un piccolo AVR...
Lavora su 4 quadranti.
Si possono implementare anche degli interrupt sul raggiungimento di
certe quote.
 
Piccio.

 certe quote.
 
Grazie comunque della disponibilità.
 
 

Il 19/11/2011 16:58, Marco Trapanese ha scritto:

Mi sa che non esiste nulla a 8 pin con seriale hardware, o sbaglio?
 
 

 Mi sa che non esiste nulla a 8 pin con seriale hardware, o sbaglio?
 
 
Se non ti servono velocità esagerate riesci tranquillamente a usare una 
seriale software.
Ad ogni modo diversi ATtiny (es. ATtiny45) hanno la USI, ovvero un 
supporto hw per la seriale (sincrona e asincrona) pur non essendo una 
UART completa. Ma con poche righe di codice la puoi utilizzare senza 
problemi.
 
Ciao
Marco
 

 orario), altrimenti vuol dire che è cambiato prima A (verso antiorario).
...
 
Io, invece, sto implementando via software il FIDOCAD a seguire.
In realtà il modulo è preceduto da un filtro che rende valido il dato
solo se n-campioni sono stabili.
I due moduli si compongono di poche istruzioni in realtà e prevedo
tempi di risposta nell'ordine dei 10 microsecondi (@ 8MHz)
considerando anche l'incremento dei contatori e la gestione dei
semafori.
In simili condizioni posso lavorare a parecchie decine di KHz di
campionamento con due encoder.
Usando come ingressi degli interrupt, si può alleggerire ulteriormente
il carico della CPU.
Ad ogni cambiamento (toggle) dei clock si genera INT e si fa partire
un timer che campiona successivamente il dato 3 volte.
Finché il dato non cambia la CPU non lavora.
 
 
[FIDOCAD]
TY 121 117 5 3 0 0 0 * D
TY 127 124 5 3 0 0 0 * Ck
RV 120 100 140 130
TY 136 117 5 3 0 0 0 * Q
TY 121 103 5 3 0 0 0 * D
TY 136 103 5 3 0 0 0 * Q
LI 140 120 160 120
MC 170 140 0 0 730
LI 140 105 150 105
MC 170 80 0 0 730
LI 170 90 160 90
LI 160 90 160 120
LI 100 105 100 80
LI 100 120 100 150
LI 100 150 170 150
LI 150 105 150 110
LI 150 110 150 140
LI 150 140 170 140
SA 100 105
SA 100 120
LI 120 105 85 105
LI 120 120 85 120
MC 200 110 0 0 730
LI 195 85 195 110
LI 195 110 200 110
LI 195 145 195 120
LI 195 120 200 120
SA 195 85
SA 195 145
LI 100 80 170 80
TY 66 117 5 3 0 0 0 * D
TY 72 124 5 3 0 0 0 * Ck
RV 65 100 85 130
TY 81 117 5 3 0 0 0 * Q
TY 66 103 5 3 0 0 0 * D
TY 81 103 5 3 0 0 0 * Q
LI 65 105 50 105
LI 65 120 50 120
MC 40 105 0 0 073
MC 40 120 0 0 073
MC 20 108 0 0 075
MC 25 108 0 1 075
MC 30 108 0 0 075
MC 35 108 0 1 075
MC 17 123 0 0 075
MC 22 123 0 1 075
MC 27 123 0 0 075
MC 32 123 0 1 075
LI 195 145 245 145
LI 195 85 245 85
MC 130 185 3 0 073
LI 130 130 130 175
LI 130 170 75 170
SA 130 170
LI 75 170 75 130
LI 130 170 135 170
LI 135 170 190 170
MC 190 170 0 0 680
LI 210 170 215 170
MC 215 180 0 1 075
MC 185 180 0 0 075
MC 245 85 0 0 690
MC 245 135 0 0 690
LI 240 95 245 95
LI 240 95 240 135
LI 240 135 245 135
LI 280 105 270 105
TY 281 117 5 3 0 0 0 * D
TY 287 124 5 3 0 0 0 * Ck
RV 280 100 300 130
TY 296 117 5 3 0 0 0 * Q
TY 281 103 5 3 0 0 0 * D
TY 296 103 5 3 0 0 0 * Q
LI 300 105 310 105
LI 270 120 280 120
LI 300 120 305 120
LI 305 120 310 120
LI 270 105 270 90
LI 270 140 270 120
LI 225 115 240 115
SA 240 115
LI 215 170 290 170
LI 290 170 290 165
LI 290 165 290 130
MC 310 105 0 0 073
MC 310 120 0 0 073
PL 325 105 330 105 1
PL 330 105 330 100 1
PL 332 100 332 105 1
PL 330 100 332 100 1
PL 332 105 344 105 1
PL 340 120 340 115 1
PL 342 115 342 120 1
PL 340 115 342 115 1
PL 325 120 340 120 1
PL 342 120 345 120 1
MC 190 180 0 1 075
MC 220 180 0 0 075
MC 125 195 0 0 075
MC 130 195 0 1 075
MC 135 195 0 0 075
MC 140 195 0 1 075
MC 115 195 0 0 075
MC 120 195 0 1 075
TY 310 125 5 3 0 0 0 * Down
TY 310 95 5 3 0 0 0 * Up
TY 10 115 5 3 0 0 0 * B
TY 10 100 5 3 0 0 0 * A
TY 125 50 10 5 0 0 0 * INTERFACCIA ENCODER 4Q
TY 110 195 5 3 0 0 0 * Ck >> A | B
TY 75 95 5 3 0 0 0 * FF1
TY 130 95 5 3 0 0 0 * FF2
TY 290 95 5 3 0 0 0 * FF3
 
Piccio.

 RX della UART?
 
Non rischi, in questo modo, di aver problemi sul baud rate impostato sulla 
UART?
Per esempio se cambia la velocità di rotazione.
Ci sono i sistemi di auto-baudrate, ma a naso direi che questo sistema 
potrebbe darti un sacco di problemi.
 
Se non vuoi aggiungere qualcosa di programmato, perchè non utilizzare un 
convertitore F/V e dare tutto in pasto ad un canale analogico?
Quello che dovrai fare a livello digitale è solo discriminare il senso di 
rotazione
 
Hope it helps
Ciao
CG 
 
 

 Io, invece, sto implementando via software il FIDOCAD a seguire.
 
A me sembra estremamente più complicato rispetto al mio algoritmo.  Ti 
consiglio di provare il mio suggerimento: è imbarazzante quanto sia 
semplice, ma funziona a meraviglia su due encoder che ho provato.
 
Quando c'è rotazione, si crea necessariamente una finestra temporale in 
cui A e B sono diversi.  E' sufficiente essere sicuri di poter 
campionare questa finestra per stabilire correttamente il verso di 
rotazione.  Con un oscilloscopio e un po' di prove puoi misurare quanto 
può essere piccola questa finestra e tarare di conseguenza la frequenza 
dell'ISR di campionamento del segnale A.  Se noti una durata minima di 
2ms, un campionamento ad 1ms è sufficiente.  Almeno per me 1ms è stato 
sufficiente, ma non ho avuto tempo di fare prove ad oscilloscopio.
 
La cosa incredibile è che funziona bene senza debounce, secondo me 
perché non utilizzo il "classico" metodo dell'interrupt on change dei 
pin di ingresso che scatterebbe parecchie volte per i vari rimbalzi.

 rotazione
 
Vorrei rilevare anche il singolo evento di rotazione....
 

 rotazione
 
Però rilevi solo 2 eventi per ciclo, mentre il mio schemino ne rileva
4.
L'implementazione software, poi, non è per niente onerosa: basta
effettuare lo XOR tra il nuovo campionamento ed il vecchio ma con i
bit invertiti di posto.
Se il risultato è 1 allora incrementa, 2 decrementa, 0 = nessun
evento.
Poi si copia il nuovo campionamento nel vecchio.
 
Piccio.

 Poi si copia il nuovo campionamento nel vecchio.
 
Sì, certo, ci sono una infinità di modi di fare un decodere in quadratura...
 

 singolo passo di rotazione.  Di più non puoi fare.
 
Rilevo ogni singolo fronte di salita/discesa sia di A che di B per cui
nello sviluppo di un periodo completo (da un fronte di salita a quello
successivo, ad es) di A leggo 4 posizioni:
1) fronte di salita di A
2) fronte di salita di B
3) fronte di discesa di A
4) fronte di discesa di B
 
e si ripete il ciclo.
 
Spesso ci si limita a campionare/conteggiare solo sul fronte di salita
di A per cui hai un solo conteggio al posto di 4 possibili riducendo
le possibilità dell'encoder.
 
Piccio.

 le possibilità dell'encoder.
 
Certo, campionare alla ricerca del solo fronte di salita è sbagliato, si 
perde un passo di rotazione.  E' sufficiente campionare ed individuare 
ENRAMBI i fronti (di salita e di discesa).  Cosa che faccio nel metodo 
che ho postato sopra.
 
 

 che ho postato sopra.
 
Sì, ma lo campioni solo su un segnale (A).
Mentre A è alto, B può commutare ed è un segnale utile per
identificare uno step.
Comunque, il più delle volte una simile risoluzione è inutile se non
dannosa.
Ho realizzato un progetto indicatomi da alfio in un mio 3d di marzo
riguardo un azionamento per motore in cc.
 
http://elm-chan.org/works/smc/report_e.html
 
 
Il fatto di avere troppi passi per giro limita le possibilità del
micro di inseguire i toggle dell'encoder.
Comunque il progetto funziona anche se devo fare un meticoloso tuning
dei parametri (sforo il torque e va in servo error).
Il firmware è scritto bene, molto lineare e fornisce il sorgente.
La parte di lettura dell'encoder è originale ed ottimizzata, ma
sfrutta l'encoder ad un quarto delle possibilità.
Il sample per l'encoder è sugli 83 Khz e il micro è clocckato a 16MHz.
Ho realizzato il PCB in monofaccia con un LMD18200 e funziona benino
direi.
Se occorre spedisco il gerber a chi lo richiede.
Utilizza una RS232 ma facilmente cambierò micro e sistema di
comunicazione (I2C) visto che ne devo pilotare 3.
 
Piccio.

 comunicazione (I2C) visto che ne devo pilotare 3.
 
Forse per questo tipo di applicazione puoi accettare di perdere qualche 
step dell'encoder, non saprei.
 
 

 discriminare anche il singolo step.
 
 
IMHO è esattamente il contrario.
Se utilizzi l'encoder come sensore di posizione / conteggio allora *può* 
essere importante intercettare ogni step (ma non sempre, vedi l'esempio 
di Piccio).
 
Nel caso di un'interfaccia utente sarebbe invece scomodo ottenere il 
cambio di voce menu o di un valore numerico ad ogni fronte! Troppo 
sensibile. Infatti di solito gli encoder utilizzati a questo scopo 
vengono addirittura divisi (via hw o via sw) di 2x, 4x o di più.
 
L'utente deve avere la possibilità di gestire comodamente la selezione 
senza dover posizione al decimo di grado l'encoder. Naturalmente tutto 
dipende sempre dal tipo di applicazione, ma parlando in generale non 
concordo sul fatto che discriminare ogni fronte è importante in 
un'interfaccia utente.
 
Ciao!
Marco
 

 discriminare anche il singolo step.
...
 
Ora capisco.
Quei rotary encoder da pannello frontale (tipo monitor o volume
autoradio) hanno mediamente una ventina di step meccanici e ogni step
fa corrispondere sempre la stessa situazione elettrica, ovvero, A e B
sono sempre gli stessi in ogni posizione stabile del pomello.
E' essenziale, quindi, avere un solo incremento per ciclo completo
visto che non è possibile posizionare il pomello stabilmente tra due
step.
Io pensavo ad un utilizzo motion (motori ecc).
 
Piccio.

 Io pensavo ad un utilizzo motion (motori ecc).
 
Lì è diverso?
 

 un'interfaccia utente.
 
Un decimo di grado presuppone 3600 step in una rotazione completa!!!
Io sto parlando di encoder con una ventina di step in un giro, appunto 
quelli utilizzati per applicazioni di interfaccia utente.
 

 quelli utilizzati per applicazioni di interfaccia utente.
 
Ne ho giusto uno smontato da un vecchio monitor che devo utilizzare
proprio per un pannello frontale.
Ogni posizione meccanicamente stabile presenta i contatti aperti.
Passando da uno step a quello successivo ho delle commutazioni del
tutto identiche a quelle degli encoder ottici per uso motion.
In pratica è sufficiente che rilevi un solo tipo di fronte, ad esmpio
quello di salita di A e corrispondentemente leggi B per determinare il
verso.
All'inizio pensavo ad applicazioni di posizionamento, condizionato dal
fatto che sto realizzando appunto un azionamento per motore cc con un
encoder da 400 step che, sfruttato al meglio, ne può fornire 1600.
La frequenza di campionamento è di 83KHz ma nel caso della manopola
basta qualche centinaio di hertz.
Credo sia importante nella tua situazione creare una routine
antirimbalzo visto che di solito quegli encoder sono a contatti
striscianti.
Ne ho sviluppata una bellilla bellilla per AVR.
 
Piccio.

 quelli utilizzati per applicazioni di interfaccia utente.
 
 
Eh, ma non era chiaro o almeno io non avevo capito.
 
Ciao!
Marco
 

 Eh, ma non era chiaro o almeno io non avevo capito.
 
Non l'ho specificato, lo davo per scontato... forse era meglio 
specificarlo :-)

 Ma questi encoder vengono usati anche per interfaccia utente?
 
 
Di certo non ha molto senso, ma mi è capitato di vederne...
 
Marco
 

 specificarlo :-)
 
Ho fatto un po' di reverse-eng sull'assembly AVR del progetto smc3 di
cui ho precedentemente fornito il link.
E' molto interessante il modo in cui l'autore legge l'encoder perché è
particolarmente sicuro ed efficiente.
A 16MHz il tempo di aggiornamento della posizione non supera i 2
microsecondi.
Trasforma le combinazioni date dalle fasi A e B in una sequenza
numerica progressiva tramite l'inversione della fase B condizionata
dallo stato della A, dopodiché effettua una semplice sottrazione.
La mia è più lunga di un paio di istruzioni ma non identifica in modo
così efficiente eventuali missing-code.
 
http://elm-chan.org/works/smc/smc3.zip
 
 
file "smc3.asm", alla label "background:"
 
Piccio.