Monday, October 3, 2011

Decoding a Rotary Encoders - Cont'd

See previous posts.

Here is my Arduino Encoder Code for my archive, and others to review.

The input circuit is a pull up resistors and a cap to ground on each leg of the Encoder pins, the center Encoder pin is grounded. One pin is configured for interrupt while the other is configured for just input.

One interesting observation, there are two interrupts for each detent of the Encoder, rotating the knob very slowly will show an interrupt half way between the detents. The Encoder doc's do not imply this should be expected. Maybe I have the wrong doc's??

This interrupt handler works, it is my adaption of the published (original) code which is shown further below. I just futzed with the code to remove problems that I observed. I do not understand all that I know, maybe I just need more futzing.


///////////////////////////
// Interrupt Handlers
///////////////////////////
void doEncoder0() {          // This works !!! but not sure why??!!!
    static volatile byte _previous = 0;
    volatile byte _this = 0;

    volatile int junk = 2000, junk2; 
    while (junk--) junk2++;    // Delay for Debounce

    _this = digitalRead(encoder0PinA);

    if (_this != _previous) {
        _previous = _this;  
        if (_this == digitalRead(encoder0PinB)) {  
            encoder0Pos++;
        } 
        else {
            encoder0Pos--;
        }
    }
    return;
}


The following suggested Handler does NOT work for my simple Encoders.

void doEncoderX() {  // This should work but does NOT !! but not sure why??!!!
   
    if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
        encoder0Pos++;
    } 
    else {
        encoder0Pos--;
    }
    return;
}


The Setup is just three lines:

    pinMode(encoder0PinA, INPUT);    
    pinMode(encoder0PinB, INPUT);

    attachInterrupt(INT0, doEncoder0, CHANGE);




The Encoder Interrupts accumulate in "encoder0Pos", The shift (>>1) divides the value by two,  which is necessary as I want to count "detents" and there are two interrupts per detent. Setting "encoder0Pos" to zero, indicates all Encoder Interrupts have been accepted. Note: there is a very narrow window between these statements where a Interrupt could be lost, which is not big deal for my app.


 // Get Encoder input
 if (abs(encoder0Pos) > 5) chr += encoder0Pos; // For Accelerated Action
 chr += encoder0Pos>>1;
 encoder0Pos = 0;



Perhaps I need more desecrate component debounce, i.e., more capacitance on the input port.


--

4 comments:

  1. I think you need to set the encoder0PinB to INPUT as well. And I don't see your attachInterrupt. I was confused by the referenced article talked about rising edge, but attached the interupt to FALLING ?
    Larry

    ReplyDelete
  2. Larry,

    You are correct, I forgot to cut and paste that line, I corrected the blog.

    Eldon

    ReplyDelete
  3. Hello Eldon,
    This page explains why you get two interrupts. Then, it's just a matter of choosing the right one.
    Also, in my code, I use a timer interrupt. You use a direct interrupt. It's a good alternative.
    http://practicalusage.com/?p=246

    ReplyDelete
  4. Hello Eldon,

    The best source I've found for rotary encoder code is: http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino

    This technique (grey code decoding) enables you to skip the debounce code and increase reliability. The OP code is for pins 0 and 1. I (bikedude) posted code in the thread for pins 2 and 3.

    ReplyDelete