Pergunta

Estou tendo problemas para fazer um codificador rotativo funcionar corretamente com microcontroladores AVR.O codificador é um mecânico Codificador ALPS, e estou usando Atmega168.

Esclarecimento

Tentei usar uma interrupção externa para ouvir os pinos, mas parece que é muito lento.Quando o Pin A fica alto, o procedimento de interrupção é iniciado e então verifica se o Pin B está alto.A ideia é que se o pino B estiver alto no momento em que o pino A ficou alto, ele estará girando no sentido anti-horário.Se o pino B estiver baixo, ele estará girando no sentido horário.Mas parece que o AVR demora muito para verificar o Pin B, então ele é sempre lido como alto.

Também tentei criar um programa que simplesmente bloqueia até que o Pin B ou o Pin A seja alterado.Mas pode ser que haja muito ruído quando o codificador é girado, porque isso também não funciona.Minha última tentativa foi ter um timer que armazenasse os últimos 8 valores em um buffer e verificasse se estava indo de baixo para alto.isso também não funcionou.

Eu tentei definir o escopo do codificador e ele parece usar entre 2 e 4 ms desde as primeiras alterações de Pin até as outras alterações de Pin.

Foi útil?

Solução

Eu tenho uma página sobre codificadores rotativos e como usá-los, que você pode achar útil.

Infelizmente, sem mais informações, não posso solucionar seu problema específico.

Quais pinos do microcontrolador estão conectados ao codificador e qual é o código que você está usando atualmente para decodificar os pulsos?

Ok, você está lidando com alguns problemas diferentes, o primeiro problema é que este é um codificador mecânico, então você tem que lidar com o ruído do switch (ressalto, vibração).O Ficha de dados indica que pode levar até 3mS para que as peças parem de saltar e criar saídas falsas.

Você precisa criar uma rotina de rejeição.A mais simples delas é verificar continuamente se A sobe.Se isso acontecer, inicie um cronômetro e verifique novamente em 3 ms.Se ainda estiver alto, você pode verificar B; se não estiver alto, você ignora o pulso espúrio e continua procurando por A alto.Ao verificar B, você olha para ele, inicia um cronômetro de 3 ms e depois olha para B novamente.Se for o mesmo nas duas vezes, você poderá usar esse valor - se ele mudar em 3 ms, será necessário fazê-lo novamente (leia B, espere 3 ms, depois leia novamente e veja se corresponde).

O atmega é rápido o suficiente para que você não precise se preocupar com a lentidão dessas verificações, a menos que você também esteja executando uma velocidade de clock lenta.

Depois de lidar com o ruído mecânico, você deseja examinar uma rotina de código cinza adequada - o algoritmo que você está seguindo não funcionará, a menos que você também diminua se A estiver alto quando B estiver baixo.Geralmente as pessoas armazenam o último valor das duas entradas e, em seguida, comparam-no com o novo valor das duas entradas e usam uma pequena função para aumentar ou diminuir com base nisso.(Confira o título “leitura em alta resolução” no site que mencionei acima para ver a tabela).Eu combino as duas leituras em um número de quatro bits e uso uma matriz simples para me dizer se incremento ou decremento o contador, mas existem soluções que são ainda mais avançadas e otimizam o tamanho do código, a velocidade ou a facilidade de manutenção do código.

Outras dicas

Adicionar um filtro passa-baixo analógico melhora muito o sinal.Com o filtro passa-baixo, o código no AVR era muito simples.

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

Ah, as maravilhas da arte ASCII :p

Aqui está o programa no AVR.Conecte A e B à entrada PORTB no 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;
}

Este código funciona a menos que você gire o codificador muito rápido.Então ele pode perder um ou dois passos, mas isso não é importante, pois a pessoa que usa o codificador não saberá quantos passos o girou.

A velocidade não deve ser um problema.Quase todos os interruptores mecânicos precisam de rotinas de rejeição.Se você quiser fazer isso com interrupções, desligue a interrupção quando ela for acionada, inicie um cronômetro que a ligará novamente após alguns ms.Manterá seu programa livre de pesquisas > :)

Com o que exatamente você está tendo problemas?Presumo que você tenha conseguido conectar os pinos do codificador ao seu PIC de acordo com as especificações técnicas vinculadas na página Farnell que você forneceu, então o problema é com a leitura dos dados?Você não obtém nenhum dado do codificador?Você não sabe como interpretar os dados que está recebendo?

/* 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)
 */
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top