[C] problem with rottary encoder

Thread Starter

rafan

Joined Oct 18, 2012
1
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

Rich (BB code):
#include <stdlib.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "lcd.h"


#define F_CPU 16000000UL

// Deklarace promennych
char str[4];
int enc = 0;
unsigned int timercounter = 0;

// deklarace timerů
void initInterrupts(void);
void TimerInit(void);

//Preruseni INT1
unsigned char dir = 0;
char a,b;
ISR(INT1_vect)
{
//pokud dojde k preruseni, probehna kontrola pinu PD5 k urceni smeru
//a podle toho pricteni nebo odecteni
    if (PIND & (1 << PD5)) {
    if (PIND & (1 << PD3)) {
        enc--;
        } else {
        enc++;
        }
    // change to falling detection
    MCUCR |= ((0 << ISC00) | (1 << ISC01));
    } else {
        if (PIND & (1 << PD3)) {
        enc++;
        } else {
        enc--;
    }
    // change to rising detection
    MCUCR |= ((1 << ISC00) | (1 << ISC11));
    }

//timercounter = 0;
} 

// preruseni kazdyhc 10ms
ISR(TIMER1_COMPA_vect)
{
     // Reset timeru
     TCNT1 = 0;
}

// Inicializace INT1
void initInterrupts(void)
{
     // nastaveni PD3 a PD5 jako vstupu
     DDRD &= ~(1<<PD5);
     DDRD &= ~(1<<PD3);

     // povoleni pull-up odporu
     PORTD |= (1<<PD5)|(1<<PD3);

     // Preruseni na sestupnou hranu na INT1
     MCUCR |= (1<<ISC11);
 
     // povoleni INT1
     GICR |= (1<<INT1);
}

void TimerInit(void)
{
 
     // nastaveni delicky (clk/64)
     TCCR1B |= _BV(CS10);
     TCCR1B |= _BV(CS11);

     // 250 pulzu -> kazdou 1 ms test
     OCR1A = 250;

     // smazani OCF1A flag
     TIFR |= _BV(OCF1A);

     // Povoleni Timeru1 v rezimu COMPARE
     TIMSK |= _BV(OCIE1A);
}

// Hlavni fce
int main(void)
{
     // Inicializace IO a casovacu
     initInterrupts();
     TimerInit();

     // povoleni global. preruseni
     sei();

     // Inicializace LCDcka
     lcd_init(LCD_DISP_ON);
      // napis na prvnim radku LCDcku
     lcd_gotoxy(0,0);  
     lcd_puts("Encoder test");


 // nekonečník
 while (1)
 {
   // zapis na LCDcko na pozici 0,1 (znak,radek)
   lcd_gotoxy(0,1);  
   lcd_puts(itoa(enc, str, 10));
 
   // kdyby nahodou prodleva
   _delay_ms(10);
 }
}
 

JohnInTX

Joined Jun 26, 2012
4,787
If i remember correctly the phase of which the two outputs change gives you the direction.
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..
 

THE_RB

Joined Feb 11, 2008
5,438
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;
Rich (BB code):
// rotary encoder decoding
oldPINS = PORTB;        // test encoder pins

if(oldPINS != newPINS)  // if encoder has changed!
{
  if(newPINS.F0 != oldPINS.F1) count++;	// find which direction it rotated
  else                         count--;

  oldPINS = newPINS;    // save pins for testing next time
}
 

IC-Man

Joined Jan 3, 2012
26
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!
 
Top