2

I'm using an Arduino Uno and two (3-pin) rotary encoders for a project. Since the loop takes a bit long and accuracy is important, I'd like to use interrupts to read the values from the rotary encoders. Also, pins 2 and 3 are occupied so I can't use hardware interrupts.

I'm using the Rotary library (github link) for the rotary encoders and the EnableInterrupt library (github link) for external interrupts. The Rotary library includes an interrupt example, but I couldn't manage to modify it to work with pin change interrupts. This is my current state (test code):

#include <EnableInterrupt.h>
#include <Rotary.h>

Rotary r = Rotary(2, 3);

void setup() {
  Serial.begin(9600);

  /* Original interrupt setup:
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
  */

  enableInterrupt(2, interruptFunction, CHANGE);
  enableInterrupt(3, interruptFunction, CHANGE);
}

void loop() {

}

void interruptFunction() {

  Serial.println("Int. called");

  unsigned char result = r.process();
  if (result == DIR_NONE) {
    // do nothing
  }
  else if (result == DIR_CW) {
    Serial.println("ClockWise");
  }
  else if (result == DIR_CCW) {
    Serial.println("CounterClockWise");
  }
}

Everytime I turn the encoder for one step, the interruptFunction gets called a bunch of times (about 2~4) but the r.process() call never returns a value.

My current guess is that this has something to do with the rotary encoder outputting gray code, but I'm not quite sure how my approach differs from the Rotary interrupt example using hardware interrupts.

Hope you can help!

  • Avoid Serial in an ISR. Check the online documentation. – Mikael Patel Mar 3 '17 at 17:18
  • I'm aware of that, i just used the provided (and working) example as a base to eliminate possible sources of failure. I will of course change it for my production code. – jfkint Mar 4 '17 at 9:39
  • The function process() of the Rotary Library is not a blocking function and only read the state of the 2 pins and compute an output. How you can say "but the r.process() call never returns a value." ? – J. Piquard Mar 4 '17 at 23:27
  • The best strategy for ISR is do the least possible. You don't need to Serial.print inside the ISR. All you need to do is store result and do the print inside loop. In the ISR change a global variable to signal that you have a new value; reset it in the loop after you do your thing. – user31481 Oct 30 '17 at 13:55
1

Everytime I turn the encoder for one step, the interruptFunction gets called a bunch of times (about 2~4)

This is due to contact bounces. You should either implement a hardware debouncing circuit (a simple R-C filter could be enough. Do not connect instead large capacitors directly in parallel to your contacts, as when the contact closes, the energy stored on the capacitor will damage over time the contacts. Using large capacitances on an input can also lead to problems when power is removed), or better a good software debouncing strategy.

For instance, when the interrupt is called the first time, disable the pin-change interrupts. Then set up a timer, which will trigger an interrupt when it expires (the time should be larger than the bounce time, usually several ms or more). Then in the timer interrupt, first put your code:

  unsigned char result = r.process();
  if (result == DIR_NONE) {
    // do nothing
  }
  else if (result == DIR_CW) {
    Serial.println("ClockWise");
  }
  else if (result == DIR_CCW) {
    Serial.println("CounterClockWise");
  }

end re-enable the pin-change interrupts.

  enableInterrupt(2, interruptFunction, CHANGE);
  enableInterrupt(3, interruptFunction, CHANGE);

but the r.process() call never returns a value.

r.process() will ALWAYS returns a value. Maybe you did mean that it returns DIR_NONE ?

0

I too had this problem. I chased my tail in SW for hours. I was making the ISRs non-reentrant (assumed they accidentally were), I was doing SW debouncing etc. I even looked at the signal on a scope but did not see any significant bounces. I eventually added 0.1uF capacitors to the inputs and that solved all my problems with the ISR. So don't trust the encoder manufacturers, there WILL be signal bounce and your Arduino WILL detect it and throw multiple interrupts. Good luck with your project! LDP

  • 2
    > I too had this problem. it isn't fair to blame a coding issue on this topology. But your point about the beauty of hardware debouncing is still valid, and often ignored by newbies wanting to bet their lives on software debouncing. – dannyf Jun 30 '17 at 15:46
0

check the library to see if it debounces the input.

if not, write your own code. usually state machine-based approaches are very resistant to switch bounces.

-1

The correct solution is to use a debouncing circuit. The MC14490 is a hex debouncing circuit and is perfect for this type of application. No need to block interrupts or set timers. It's available as either SMD or thru hole.

New contributor
ThinkingMetal is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.