Rotary encoder hardware debounced with PinChangeInterrupt.h

Thanks Jim P. This is working for me. If useful, consider it. Even with imperfections, works fine. I ran the knob as faster as my fingers could, with no fails. I found few examples in the web. Sorry if this is not the place to publish.

// This code is for a debounced hardware, checking debounce/timing fails
// Hardware:
// 10k pullUp, then 1k series, then 100nF (RC = 0.0011s), then ports of any 
schmitt-trigger IC
// davidllpz - 01/26

#include <PinChangeInterrupt.h> //
#define CLK_A 5
#define DT_B  6
#define SW_PushB  7 
volatile int counter = 0;
int CLK_status;
int lastCounter;

void setup() {
  Serial.begin(9600);
  pinMode(CLK_A, INPUT_PULLUP);
  pinMode(DT_B, INPUT_PULLUP);
  pinMode(SW_PushB, INPUT_PULLUP); 
  attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(CLK_A), getCLK_A, RISING);
}

void loop() {
  // Your stuff here
}
// (ISR)
void getCLK_A() {
  CLK_status = digitalRead(CLK_A);
  if (digitalRead(DT_B) != CLK_status) {
    counter++; // Clockwise
    if (counter != lastCounter + 1) {
      Serial.println ("Failed!");
    }
    lastCounter = counter;
  } else {
    counter--; // Counter-clockwise
    if (counter != lastCounter - 1) {
      Serial.println ("Failed!");
    }
    lastCounter = counter;
  }
  Serial.print("Counter: ");
  Serial.println(counter);
}

Serial.print() should not be in an ISR.

Flag the interrupt in the ISR and exit.

Outside the ISR, do the incrementing/decrementing and printing.

  • Please reword your question.

  • Always format your sketch before you post it.

// This code is for a debounced hardware, checking debounce/timing fails
// Hardware:
// 10k pullUp, then 1k series, then 100nF (RC = 0.0011s), then ports of any schmitt-trigger IC

#include <PinChangeInterrupt.h>

#define CLK_A 5
#define DT_B  6
#define SW_PushB  7

volatile int counter = 0;
int CLK_status;
int lastCounter;

void setup()
{
  Serial.begin(9600);
  pinMode(CLK_A, INPUT_PULLUP);
  pinMode(DT_B, INPUT_PULLUP);
  pinMode(SW_PushB, INPUT_PULLUP);
  attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(CLK_A), getCLK_A, RISING);
}

void loop()
{
  // Your stuff here
}

// (ISR)
void getCLK_A()
{
  CLK_status = digitalRead(CLK_A);

  if (digitalRead(DT_B) != CLK_status)
  {
    counter++; // Clockwise
    if (counter != lastCounter + 1)
    {
      Serial.println ("Failed!");
    }
    lastCounter = counter;
  }
  
  else
  {
    counter--; // Counter-clockwise
    if (counter != lastCounter - 1)
    {
      Serial.println ("Failed!");
    }
    lastCounter = counter;
  }
  
  Serial.print("Counter: ");
  Serial.println(counter);
}

Is there a question? The title insinuates there might be, but the post says it works.

It works for me. I am only sharing if others want it. That’s it. Thanks for corrections.

Thanks. Even ‘lastCounte = counter’ must be out of ‘else’, and other stuffs that can be simplified. What matters is that it works. I speeded up my fingers and don’t fails. Matters the hardware too.

I'll assume UNO R3, until otherwise mentioned.

10k pull-up. OK.
1k / 100nF hardware debounce. OK.
Schmitt trigger IC. Not necessary.
INPUT_PULLUP. Not necessary.
Ports of ATmega328P contain Schmitt triggers.

You increment a number and immediately check if it has incremented.
It may miss pulses, count ghost pulses, but will never print "failed" (unless CPU is really broken).

Aren’t rotaries supposed to not bounce?

Turns out nope but you can hardware debounce or even software debounce…

I look at Gray Code and see that only 1 bit changes per step;

A B

0 0

1 0

1 1

0 1

0 0

In a state machine same direction movement could advance state on the first change or maybe 1ms later (debounce switch usually takes 2+ms) watch the other pin while the first finishes debouncing… or something like that, this is just a notion to speed the potential turn speed.

Change direction, same bit changes… gotta watch them both for ‘the ringer’, already complicates the above but what I see is that while either bit bounces, there is a window to decide&act take advantage of. Turn fast enough, both could be bouncing at the same x-many reads but since the 1st transition that starts the bounce sets the read state instead of waiting for stability, we get moar cyclez though we still have to catch end of bounce before the next click transition. I assume both pins would be read on the same port giving 2 bits at high frequency.

There are encoders with more than 2 data pins AFAIK.

Mine bounced. Maybe I bought cheap KY-040.

The next line says Nope as in they don’t always.

And people often hardware debounce rotaries if it ain’t built-in so sometime rotaries don’t bounce, a cap eats it.

cheap KY-040… seems redundant, c| ; ^ )

Some encoders have sliding contacts. Bouncing is likely.
Some encoders have optical discs. No bounce unless shaft is vibrating.
Some even have an analog pick-up and convert it to the outputs.

But consider the effect of bouncing:

You are wright. Thanks. But I have Rotary => 30cm flat cable => MCU into a noisy environment, handling a 15A dc motor. I preffer exagerate…

You should change the Title of your topic. "fails checker" makes it sound like you have a problem.

Thanks, Jim P. May be this is not the wright place to publish. I suffered with this device. So, I thought to share with others.

Fine where it is, everyone on the forum will see i.

‘In general’, it is recommended less instruction into ISR. In this case, prints once when interrupts occur. Otherwise, any loop will print. You choose. But not necessarily rules are broken…

I was not trying to say "no printing inside an ISR is a law that you must obey", but it is a rule of best practice to return control to the mcu for time-sensitive tasks. Printing takes "forever" so inc/dec/flag in the ISR and exit, then take care of those events in "the loops."

It's like a clutch in a car. You can shift a car by using the accelerator or the brake and coming to a stop. That's a "rule" that can be broken, but effects the power train and your driving experience, and using the "rule" of the clutch makes it better. You are "right" in whatever decision you make. I just won't fly on any of your airplanes.