[C] problem with rottary encoder

Discussion in 'Embedded Systems and Microcontrollers' started by rafan, Oct 18, 2012.

  1. rafan

    Thread Starter New Member

    Oct 18, 2012
    1
    0
    Hi. I have a big problem with the rotary encoder. I'm trying to find out for using interrupt rotation right / left. Unfortunately unsuccessfully.

    Rotary ekonder is connected to the ATMEGA16
    PD5 is Encoder_B
    PD3 in Encode_A

    Here is my code. I do not know how should I treat INT1. Would someone? thank you

    Code ( (Unknown Language)):
    1.  
    2. #include <stdlib.h>
    3. #include <avr/interrupt.h>
    4. #include <util/delay.h>
    5. #include "lcd.h"
    6.  
    7.  
    8. #define F_CPU 16000000UL
    9.  
    10. // Deklarace promennych
    11. char str[4];
    12. int enc = 0;
    13. unsigned int timercounter = 0;
    14.  
    15. // deklarace timerů
    16. void initInterrupts(void);
    17. void TimerInit(void);
    18.  
    19. //Preruseni INT1
    20. unsigned char dir = 0;
    21. char a,b;
    22. ISR(INT1_vect)
    23. {
    24. //pokud dojde k preruseni, probehna kontrola pinu PD5 k urceni smeru
    25. //a podle toho pricteni nebo odecteni
    26.     if (PIND & (1 << PD5)) {
    27.     if (PIND & (1 << PD3)) {
    28.         enc--;
    29.         } else {
    30.         enc++;
    31.         }
    32.     // change to falling detection
    33.     MCUCR |= ((0 << ISC00) | (1 << ISC01));
    34.     } else {
    35.         if (PIND & (1 << PD3)) {
    36.         enc++;
    37.         } else {
    38.         enc--;
    39.     }
    40.     // change to rising detection
    41.     MCUCR |= ((1 << ISC00) | (1 << ISC11));
    42.     }
    43.  
    44. //timercounter = 0;
    45. }
    46.  
    47. // preruseni kazdyhc 10ms
    48. ISR(TIMER1_COMPA_vect)
    49. {
    50.      // Reset timeru
    51.      TCNT1 = 0;
    52. }
    53.  
    54. // Inicializace INT1
    55. void initInterrupts(void)
    56. {
    57.      // nastaveni PD3 a PD5 jako vstupu
    58.      DDRD &= ~(1<<PD5);
    59.      DDRD &= ~(1<<PD3);
    60.  
    61.      // povoleni pull-up odporu
    62.      PORTD |= (1<<PD5)|(1<<PD3);
    63.  
    64.      // Preruseni na sestupnou hranu na INT1
    65.      MCUCR |= (1<<ISC11);
    66.  
    67.      // povoleni INT1
    68.      GICR |= (1<<INT1);
    69. }
    70.  
    71. void TimerInit(void)
    72. {
    73.  
    74.      // nastaveni delicky (clk/64)
    75.      TCCR1B |= _BV(CS10);
    76.      TCCR1B |= _BV(CS11);
    77.  
    78.      // 250 pulzu -> kazdou 1 ms test
    79.      OCR1A = 250;
    80.  
    81.      // smazani OCF1A flag
    82.      TIFR |= _BV(OCF1A);
    83.  
    84.      // Povoleni Timeru1 v rezimu COMPARE
    85.      TIMSK |= _BV(OCIE1A);
    86. }
    87.  
    88. // Hlavni fce
    89. int main(void)
    90. {
    91.      // Inicializace IO a casovacu
    92.      initInterrupts();
    93.      TimerInit();
    94.  
    95.      // povoleni global. preruseni
    96.      sei();
    97.  
    98.      // Inicializace LCDcka
    99.      lcd_init(LCD_DISP_ON);
    100.       // napis na prvnim radku LCDcku
    101.      lcd_gotoxy(0,0);  
    102.      lcd_puts("Encoder test");
    103.  
    104.  
    105.  // nekonečník
    106.  while (1)
    107.  {
    108.    // zapis na LCDcko na pozici 0,1 (znak,radek)
    109.    lcd_gotoxy(0,1);  
    110.    lcd_puts(itoa(enc, str, 10));
    111.  
    112.    // kdyby nahodou prodleva
    113.    _delay_ms(10);
    114.  }
    115. }
    116.  
    117.  
     
  2. nigelwright7557

    Senior Member

    May 10, 2008
    487
    71
    If i remember correctly the phase of which the two outputs change gives you the direction.
     
  3. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    Yes. One way is to interrupt off one edge of one of the phases. Then inspect the other phase; it will be 1 for one direction and 0 for the other. This method will not give you the maximum resolution however. That comes when you interrupt on each transition of each phase and combine the bit new states read with the previous states of the last IRQ.

    IRQ on each transition of each bit:
    With 2 bits in, you'll have 2 new bits and 2 old bits on each IRQ. Combine these to make a number 0-15 and use that number as an index into a state table. For example, if the original state is 00, on the interrupt if you read:
    01 - the encoder moved one count clockwise. 00<<2+01 = 0001. State 1 is increment count.
    10- the encoder moved one count counterclockwise. 00<<2 + 10 = 0010. State 2 is decrement count.
    (CW and CCW are relative here. I don't know how your encoder is wired).

    For each combination of is-was, you'll have an increment and a decrement.

    You also must consider the error cases i.e. 00->11. Since 2 bits can't change at once, its an error (you probably missed a point). You can ignore it for simple applications (a user knob for example) or flag an error (encoding an XY axis on a machine tool for another example).

    Your is-was table will have 16 entries:
    4 ++
    4 --
    4 do nothing or error 00->00 (3 more with bits the same)
    4 do nothing or error 00->11 (3 more with both bits changed)

    That's the way I do it..
     
  4. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    5,435
    1,305
    You don't need a wave table, you can decode it with a single bit test;
    (two encoder pins A and B)
    (on detecting ANY change of the encoder pins)
    if(newA != oldB) count++;
    else count--;

    it is as simple as that. Of course you need to keep a copy of the old state of the 2 pins, which is just recording a byte;
    oldPINS = newPINS; // record state of the A and B pins

    and of course need to detect that any pins have changed;
    newPINS = PORTB;
    if(newPINS != oldPINS)

    so the whole code becomes something like;
    Code ( (Unknown Language)):
    1.  
    2. // rotary encoder decoding
    3. oldPINS = PORTB;        // test encoder pins
    4.  
    5. if(oldPINS != newPINS)  // if encoder has changed!
    6. {
    7.   if(newPINS.F0 != oldPINS.F1) count++; // find which direction it rotated
    8.   else                         count--;
    9.  
    10.   oldPINS = newPINS;    // save pins for testing next time
    11. }
    12.  
     
  5. IC-Man

    New Member

    Jan 3, 2012
    26
    4
    Hi,

    The AB signals are coded and a phase shift between A and B is a direction change. If you want to do this direction discrimination in SW you need two interupt input which triggers an interupt on a rising or falling edge and then decode in SW. Here you find the diagram for up/down counting on the AB-signal: http://www.ichaus.biz/wp2_simple_measurement . It shows also a hardware solution with the iC-MD containing the disrection discriiminator plus up to three 16 bit position counter.

    Enjoy what ever you do!
     
  6. raychar

    Member

    Nov 8, 2011
    82
    0
    Here is a helpful codes for it, although it is for PIC-u, you can easily fit to other controllers. I tested and suceeded.
     
Loading...