Using a rotary encoder with AVR Micro controller

后端 未结 5 1204
小鲜肉
小鲜肉 2021-02-10 06:48

I\'m having trouble getting a rotary encoder to work properly with AVR micro controllers. The encoder is a mechanical ALPS encoder, and I\'m using Atmega168.

Cla

相关标签:
5条回答
  • 2021-02-10 06:57

    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 >:)

    0 讨论(0)
  • 2021-02-10 07:04

    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.

    0 讨论(0)
  • 2021-02-10 07:09

    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?

    0 讨论(0)
  • 2021-02-10 07:10
    /* 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)
     */
    
    0 讨论(0)
  • 2021-02-10 07:15

    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.

    0 讨论(0)
提交回复
热议问题