Domanda

Ho difficoltà a far funzionare correttamente un codificatore rotativo con i microcontrollori AVR.L'encoder è meccanico Codificatore ALPS, e sto usando Atmega168.

Una precisazione

Ho provato a utilizzare un interrupt esterno per ascoltare i pin, ma sembra che sia troppo lento.Quando il Pin A diventa alto, inizia la procedura di interruzione e quindi controlla se il Pin B è alto.L'idea è che se il Pin B è alto nel momento in cui il Pin A è andato alto, allora sta ruotando in senso antiorario.Se il Pin B è basso, ruota in senso orario.Ma sembra che l'AVR impieghi troppo tempo per controllare il Pin B, quindi viene sempre letto come alto.

Ho anche provato a creare un programma che si blocchi semplicemente finché non cambia il Pin B o il Pin A.Ma potrebbe darsi che ci sia troppo rumore quando si ruota l'encoder, perché neanche questo funziona.Il mio ultimo tentativo è stato quello di avere un timer che memorizzi gli ultimi 8 valori in un buffer e controlli se passa da basso ad alto.Anche questo non ha funzionato.

Ho provato a individuare l'encoder e sembra che utilizzino tra 2 e 4 ms dalla prima modifica del Pin fino alla modifica dell'altro Pin.

È stato utile?

Soluzione

Ho una pagina web su codificatori rotativi e come usarli, che potresti trovare utile.

Sfortunatamente senza ulteriori informazioni non posso risolvere il tuo problema particolare.

Quali pin del microcontrollore sono collegati all'encoder e qual è il codice che stai attualmente utilizzando per decodificare gli impulsi?

Ok, hai a che fare con alcuni problemi diversi, il primo problema è che si tratta di un codificatore meccanico, quindi devi affrontare il rumore dell'interruttore (rimbalzo, chiacchiere).IL scheda dati indica che potrebbero essere necessari fino a 3 ms affinché le parti smettano di rimbalzare e creare false uscite.

È necessario creare una routine di antirimbalzo.Il più semplice dei quali è controllare continuamente se A sale.In tal caso, avviare un timer e controllarlo di nuovo dopo 3 ms.Se è ancora alto, puoi controllare B; se non è alto, ignora l'impulso spurio e continua a cercare A alto.Quando controlli B, lo guardi, avvii un timer per 3 ms e poi guardi di nuovo B.Se era lo stesso entrambe le volte, puoi usare quel valore: se cambia entro 3 ms, devi farlo di nuovo (leggi B, attendi 3 ms, quindi rileggilo e vedi se corrisponde).

L'atmega è abbastanza veloce da non doverti preoccupare che questi controlli vadano lentamente, a meno che tu non stia utilizzando anche una velocità di clock lenta.

Una volta che hai a che fare con il rumore meccanico, allora vuoi guardare una corretta routine del codice grigio: l'algoritmo che stai seguendo non funzionerà a meno che non diminuisca anche se A è alto quando B diventa basso.Generalmente le persone memorizzano l'ultimo valore dei due input, quindi lo confrontano con il nuovo valore dei due input e utilizzano una piccola funzione per incrementare o diminuire in base a quello.(Consulta la rubrica "lettura ad alta risoluzione" sul sito web di cui ho parlato sopra per la tabella).Combino le due letture in un numero a quattro bit e utilizzo un semplice array per dirmi se incremento o decremento il contatore, ma esistono soluzioni ancora più avanzate e ottimizzate per dimensione del codice, velocità o facilità di manutenzione del codice.

Altri suggerimenti

L'aggiunta di un filtro passa-basso analogico migliora notevolmente il segnale.Con il filtro passa-basso, il codice sull'AVR era davvero semplice.

       _________
        |         |
        | Encoder |
        |_________|
          |  |  |
          |  |  |
     100n |  O  | 100n  
 GND O-||-+ GND +-||-O GND
          |     | 
          \     /
      3K3 /     \ 3K3
          \     /
          |     |    
VCC O-/\/-+     +-\/\-O VCC
     15K  |     |  15K
          |     |
          O     O
          A     B

Ah, le meraviglie dell'arte ASCII: p

Ecco il programma sull'AVR.Collega A e B all'ingresso PORTB sull'avr:

#include <avr/io.h>

#define PIN_A (PINB&1)
#define PIN_B ((PINB>>1)&1)

int main(void){
    uint8_t st0 = 0;
    uint8_t st1 = 0;
    uint8_t dir = 0;
    uint8_t temp = 0;
    uint8_t counter = 0;
    DDRD = 0xFF;
    DDRB = 0;
    while(1){   
    if(dir == 0){
        if(PIN_A & (!PIN_B)){
            dir = 2;
        }else if(PIN_B & (!PIN_A)){
            dir = 4;
        }else{
            dir = 0;
        }
    }else if(dir == 2){
        if(PIN_A & (!PIN_B)){
            dir = 2;
        }else if((!PIN_A) & (!PIN_B)){
            counter--;
            dir = 0;
        }else{
            dir = 0;
        }
    }else if(dir == 4){
        if(PIN_B & (!PIN_A)){
            dir = 4;
        }else if((!PIN_A) & (!PIN_B)){
            counter++;
            dir = 0;
        }else{
            dir = 0;
        }
    }else if(PIN_B & PIN_A){
        dir = 0;
    }
        PORTD = ~counter;
    }
    return 0;
}

Questo codice funziona a meno che non si ruoti l'encoder molto velocemente.Quindi potrebbe perdere uno o due passaggi, ma non è importante, poiché la persona che utilizza il codificatore non saprà quanti passaggi lo ha eseguito.

La velocità non dovrebbe essere un problema.Quasi tutti gli interruttori meccanici necessitano di routine di antirimbalzo.Se vuoi farlo con gli interrupt, disattiva l'interruzione quando si attiva, avvia un timer che lo riaccenderà dopo un paio di ms.Manterrà il tuo programma libero da polling >:)

Con cosa esattamente hai problemi?Presumo che tu sia riuscito a collegare i pin dell'encoder al tuo PIC secondo le specifiche tecniche collegate alla pagina Farnell che hai fornito, quindi il problema è con la lettura dei dati?Non ricevi nessun dato dall'encoder?Non sai come interpretare i dati che ricevi?

/* into 0 service rutine */
if(CHB)
{
  if(flagB)
   Count++;
  FlagB=0;
}
else
{
  if(FlagB)
   count--:
  FlagB=0:
}

/* into 1 service rutine */
FlagB=1;

/* make this give to you a windows time of 1/4 of T of the encoder resolution
   that is in angle term: 360/ (4*resolution)
 */
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top