Using a rotary encoder with AVR Micro controller

泄露秘密 提交于 2019-12-03 09:07:38
Adam Davis

I have a webpage about rotary encoders and how to use them, which you might find useful.

Unfortunately without more information I can't troubleshoot your particular problem.

Which microcontroller pins are connected to the encoder, and what is the code you're currently using to decode the pulses?

Ok, you're dealing with a few different issues, the first issue is that this is a mechanical encoder, so you have to deal with switch noise (bounce, chatter). The data sheet indicates that it may take up to 3mS for the parts to stop bouncing and creating false outputs.

You need to create a debounce routine. The simplest of which is to continuously check to see if A goes high. If it does, start a timer and check it again in 3 ms. If it's still high, then you can check B - if it's not high then you ignore the spurious pulse and continue looking for A high. When you check B, you look at it, start a timer for 3 ms, and then look at B again. If it was the same both times, then you can use that value - if it changes within 3 ms then you have to do it again (read B, wait 3 ms, then read it again and see if it matches).

The atmega is fast enough that you shouldn't have to worry about these checks going slowly, unless you're also running a slow clock speed.

Once you deal with the mechanical noise, then you want to look at a proper gray code routine - the algorithm you're following won't work unless you also decrement if A is high when B goes low. Generally people store the last value of the two inputs, and then compare it to the new value of the two inputs and use a small function to increment or decrement based on that. (Check out the heading "high resolution reading" on the website I mentioned above for the table). I combine the two readings into a four bit number and use a simple array to tell me whether I increment or decrement the counter, but there are solutions that are even more advanced, and optimize for code size, speed, or ease of code maintenance.

Adding an analog lowpass filter greatly improves the signal. With the lowpass filter, the code on the AVR was really simple.

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

Ah, the wonders of ASCII art :p

Here is the program on the AVR. Connect A and B to input PORTB on the 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;
}

This code works unless you rotate the encoder really fast. Then it might miss a step or two, but that is not important, as the person using the encoder won't know how many steps they have turned it.

Speed should not be a problem. Mostly all mechanical switches need debounce routines. If you wanna do this with interrupts turn off the interrupt when it triggers, start a timer that will turn it back on after a couple of ms. Will keep your program polling-free >:)

What exactly are you having problems with? I assume you've been able to hook the pins of the encoder to your PIC as per the technical specifications linked on the Farnell page you gave, so is the problem with reading the data? Do you not get any data from the encoder? Do you not know how to interpret the data you're getting back?

/* 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)
 */
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!