I'm trying to write an interrupt service routine for an Arduino Mega 2560 to decode a quadrature rotary encoder. I've got an ISR (Interrupt Service Routine) that responds to the falling edge of digital pin 2.

I then need to look at the value of pin 3 and see if it is high or low to tell which direction the encoder is being turned. I've built a simple debouncing circuit using a couple of resistors and a .01 μF capacitor per line from the encoder, so those signals are clean now.

Based on the reading I've done, it's best to read the pin states directly rather than using digitalRead in an ISR since the digitalRead() function is rather slow.

I am finding it hard to find and figure out the port mappings for the port registers to read the pins.

I believe that I should use

(PIND & (1 << 3) != 0 to read digital pin 3, since I think pins 2-9 are on register PIND?

I found this chart https://www.arduino.cc/en/Hacking/PinMapping2560 that lists the pin mappings for the Mega 2560, but I'm not sure how to read it.

If I use bool pinBState = digitalRead(rotaryPinB) == LOW in my ISR it works, but it seems to miss state changes sometimes.

If instead I use the code `bool pinBState = (PIND & (1 << 3)) == 0;

The value of pinBState never changes.

Can somebody tell me what I'm doing wrong?

See the self-answer below. It seems that digital pins 0-7 are read from PINE, not PIND.

However, now I have a new problem:

With the code shown in my answer I am getting readings that increase for clockwise rotation, and decrease for counterclockwise rotation, as expected:

If I rotate it very slowly through a full revolution, I can usually get exactly 12 steps in a rotation. If I rotate my encoder rapidly, or through several full turns, it gives me values that aren't exact multiples of 12, and rotating it several turns clockwise, and then the same number of turns counterclockwise does not return the value to 0 like it should.

I'm using a simple debouncing circuit as recommended from the spec sheet for the encoder:


The circuit looks like this:

enter image description here

Shouldn't that circuit work? I wired it up on a breadboard, and have pretty long leads, and didn't cut the wires on the resistors or capacitors since this is still a test circuit. I wouldn't think this would be sensitive to RF noise though. The only other thing I've got on the project is an I2C LCD display. I've triple-checked my wiring and don't think I've made any mistakes.

I'm stumped as to why an interrupt-driven rotary encoder, with the recommended debouncing circuit, that reads port registers to read the data from it's PIN B doesn't give reliable results for normal rates of turning on the control.

  • Are you looking for a complete list? forum.arduino.cc/index.php?topic=52534.0
    – VE7JRO
    Feb 21 '19 at 1:55
  • No, I saw that page but can't make any sense of it. I need to know what code to use to read the value of digital pin 2 on my Mega 2560. Some coaching on how to read those tables would be a nice plus, but right now my focus is very narrow: Getting this single read to work.
    – Duncan C
    Feb 21 '19 at 1:56
  • Digital pin 2 is PE4 ... that is the 5th bit of port E
    – jsotola
    Feb 21 '19 at 2:30
  • How is this different than what you asked here? electronics.stackexchange.com/questions/423688/…
    – CrossRoads
    Feb 22 '19 at 1:37
  • Digital pins 2 and 4, both have hardware interrupts on them. Why not use both interrupts? It's kind of overkill, but the mega has them, so why not use them. As to your current problem; You are missing steps, so either your code is too slow, or the debounce filter is to strong. The latter could easily be tested by using smaller capacitors. If rotaryValue and int? Is it defined as volatile?
    – Gerben
    Feb 22 '19 at 16:25

Ok, it looks like digital pins 0-7 are read from the PINE (That's PIN E) register, not PIND.

I am setting up my pins as input using normal Arduino code:

#define rotaryPinA 2
#define rotaryPinB 3

void setup() {
  attachInterrupt(digitalPinToInterrupt(rotaryPinA), encode, FALLING);

  pinMode(rotaryPinA, INPUT);
  pinMode(rotaryPinB, INPUT);
  //The rest of the setup function

And then my encode method:

void encode() {
  rotaryValueChanged = true;
  //Read my PinB (Digital Pin 3) using port register PINE, 5th bit
  bool pinBState = (PINE & (1 << 5)) == 0;
  //Here is the equivalent code using digitalRead:
  //bool  pinBState =  digitalRead(rotaryPinB) == LOW;
  rotaryValue +=  pinBState ? -1 : 1;

That code seems to work, but the value I'm getting from my rotary encoder drifts.


Have you tried some hardware denouncers - like MC14490?
Using Interrupts you can then detect all four phases of each encoder step.

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.