avr-chat
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[avr-chat] Using Rotary Encoders...


From: Vincent Trouilliez
Subject: [avr-chat] Using Rotary Encoders...
Date: Wed, 21 Sep 2005 07:00:30 +0200

Hi gents,


I would like your opinions/experiences about using rotary encoders. I
have planned on using one for my project, so ordered one to experiment
with (never used them before).
It's a mechanical one, with the usual 2-bit gray code output.

I was expecting loads of bounces, but got only a few glitches, so was
impressed by the quality of the unit despite the affordable price (under
6 Euros). Wanting to get rid of these few glitches and make it perfect,
I used a timer to allow 50ms for bounces to go away, plenty enough
according to the datasheet which stated 5ms max, and my observations on
the scope showing that pulses were mostly about 10 to 15ms wide max,
under normal conditions (ie, not turning it too slowly). So, 15+5 = 20,
50ms should cover any amount of bouncing.

However this is where the problem start : even with this huge delay, I
still get these glitches. Sometimes it will count 2 pulses instead of
one, or the count won't move, or it will decrement instead of
increment...

So since I have zero experience with these things, I don't really know
what to think:


1) The encoder works perfectly most of the time, and the glitches appear
even when I am turning the shaft slowly, one step at a time. So the
encoder must be okay, and my software has a bug.

2) The other way around : my programme is so simple, it can't go wrong,
and since the encoder has mechanical contacts, it's most likely at
fault, you get what you pay for, go fork out the cash for an optical
encoder.

3) The program is fine, the encoder "should" be glitch free, but you
just happen to have a faulty unit (I noticed that the shaft has too much
radial play to my taste, but then what do I know... ), just get another
one and you will be fine.


I pasted the relevant bits of my program below. I wired channel B on
INT0 and set it to trigger on a rising edge. Then in the ISR I increment
or decrement the counter, depending on the logic level of channel A
(going CW or CCW).
That worked fine (bar the random glitches). Then I added the 50ms delay
to see if that would improve things, but no :o(
I turn an LED on when the pulse/rising edge has been detected, and I
turn it off once the 50ms delay has elapsed. This produces a short flash
which I can about see. I just noticed, however, that whenever a glitch
happens, I get a BIG flash from the LED ! Now what does that mean, I
don't know exactly, but at least it gives me something to work on...


Any ideas/comments, are most welcome :-)


--
Vince


SIGNAL (SIG_INTERRUPT0)
{

        PORTD = 0xFF;           //turn LED on when the pulse has been detected
        if ( PIND & _BV(PD6) )  //if Channel A is high
                encoder_count++;        //then the knob has been turned 
clockwise
        else
                encoder_count--;        //if not... then must have been CCW !! 
;-)
        
        GICR &= 0xBF;           // disable encoder interrupt to avoid counting 
bounces
        TCNT0 = 0;              //reset debounce timer
        TIMSK |= _BV( OCIE0 );  //enable timer interrupt

}

SIGNAL (SIG_OUTPUT_COMPARE0)
{

        PORTD = 0x00;   //50ms debounce delay is over, turn LED off
        TIMSK &= ! _BV( OCIE0 );        //disable timer interrupt
        GICR |= _BV( INT0 );    //enable encoder interrupt again
        
}


void ioinit (void) {

        DDRD    = 0x20; //outputs : PD5 (LED), Inputs : PD6 (Encoder 'A' 
channel)
        //set up timer0
        OCR0    = 50;   // about 50ms delay
        TCCR0   = 0x1D; // compare mode mode, prescaler = 1024
        //set interrupts
        GICR    = 0x40; //enable INT0 interrupt (Encoder 'B' channel).
        MCUCR   = 0x03; //set INTO to trigger on rising edge
        sei();          //enable global interrupts
}

int main (void){

        ioinit ();
        lcd_init();     

        while(TRUE){

                lcd_goto_rc(1,1);       //print counter value in top left 
corner of the LCD

                GICR &= ~( _BV( INT0 ) ); //disable INT0 interrupt while 
reading counter contents
                dot_two(encoder_count); // display counter content on the LCD
                GICR |= ( _BV( INT0 ) ); //re-enable interrupt 

                _delay_ms(100); //simulate a busy main loop

        }

}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]