I bought a 12-step "quadrature" incremental rotary encoder as an input device for a project I'm working on.

When I wrote the code to track "ticks" of rotation, I made it respond to any change in the "A" pin. As a result, I got 24 steps, not 12. They appear to be evenly spaced, and still correctly indicate clockwise or counter-clockwise rotation.

It occurs to me that if I track changes of state in both the "A" and "B" pins, as long as I reverse the logic for identifying clockwize vs. counterclockwise rotation, I could get a 48 step count from the same encoder.

Thus isn't it straightforward to get 2X or 4X the steps from a quadrature rotary encoder simply by interpreting the signals differently?

  • would that still work correctly when starting and stopping? it would seem the direction could be ambiguous if only one pin changed... – dandavis Feb 19 '19 at 19:16
  • No, with quadrature encoding you can tell the direction based on one pin state change and the state of the other pin. – Duncan C Feb 19 '19 at 19:57
  • please clarify what this means got 24 steps – jsotola Feb 19 '19 at 21:06
  • Yes, you are correct. You can track 48 different positions per rotation. – Gerben Feb 20 '19 at 15:02
  • @Gerben, can you submit an answer (with a little more detail) so I can accept it? my naive code using digitalRead of the 2 pins is missing positions unless I rotate the encoder slowly. I found some code online this morning that uses pin interrupts. I gather that's the way to go? – Duncan C Feb 20 '19 at 15:58

Maybe the encoder you got is being incorrectly described? I have a handful of the little thru-hole rotary encoders popular in Arduino module kits and they have 12 detents for one rotation, but they are actually 48-position-per-rotation encoders. When I use them, I have to divide the tick count by 4 to have one physical "click" count as one tick in my code.

For some of these, it's fairly straightforward to open them up and remove the detent, and thus get a higher-resolution, smooth spinning encoder. Unfortunately, others, such as the ones I have, are difficult to open without destroying them, and since they physically snap to the detents, aren't usable at the proper resolution.

The method you note, where you track the high/low change in either direction of both outputs is the normal way to read quadrature encoders.

| improve this answer | |
  • The one I bought is a 12-step encoder, but doesn't have detents. From what I've read the norm is to only track rising edge changes on one of the channels, which would give me 12 steps. Thus your answer confuses me. – Duncan C Feb 19 '19 at 18:42
  • Mine is a Bourns PEC11-4020K-S0012 (Jameco part 2277703) encoder with switch. – Duncan C Feb 19 '19 at 18:44
  • Reading the datasheet shows it's described as "12 pulses per 360deg rotation". Clearly they mean 12 pulses (up, then down, then back up; or down, then up, then back down) on one channel, and not 12 level changes. I suppose what's "normal" depends on a lot of things. Many Arduino scripts use a less useful process of looking for a rising edge on one channel and then checking the other channel to determine the "direction" of rotation. But that wastes so much information! – jose can u c Feb 19 '19 at 18:58
  • "12 pulses" means 12 rising pulses on a single channel in this case. My point is that depending on how you write your code, you can treat that as 12, 24, or 48 steps plus direction information, and without having to divide the tick count by anything. – Duncan C Feb 19 '19 at 19:03

The answer to my question is yes.

Normally you only count the falling edge of one of the outputs of an incremental encoder as a pulse. That gives you the specified number of ticks for that kind of encoder - 12 ticks per turn in the case of my encoder.

If you instead write your code to respond to either rising or falling edge changes on one of the pins, you get 24 ticks per revolution.

If you instead write your code to respond to rising or falling edge changes on either pin, you get 48 ticks per revolution.

The code I came up with looks like this:

void setup() {
  //Use this setup code for 12 ticks/revolution
  attachInterrupt(digitalPinToInterrupt(rotaryPinA), encode, FALLING);

  Use this setup code for 24 ticks/revolution 
  (trigger interrupt on rising and falling edge)
  attachInterrupt(digitalPinToInterrupt(rotaryPinA), encode, CHANGE);

  Use this setup code for 48 ticks/revolution 
  (trigger interrupts on rising and falling edge of either pin)
  attachInterrupt(digitalPinToInterrupt(rotaryPinA), encode, CHANGE);
  attachInterrupt(digitalPinToInterrupt(rotaryPinB), encodePinB, CHANGE);

//The rest of your setup code...

And the interrupt service routines (ISRs):

void encode() {
  rotaryValueChanged = true;
  //Read the PinA & PinB (Digital Pins 2 & 3) using port register PINE, 4th and 5th bit
  //Fast equivalent to pinAState = digitalRead(rotaryPinA) == LOW
  bool pinAState = (PINE & (1 << 4)) == 0;
  bool pinBState = (PINE & (1 << 5)) == 0;
  rotaryValue +=  (pinAState == pinBState) ? -1 : 1;

void encodePinB() {
  rotaryValueChanged = true;
  //Read the PinA & PinB (Digital Pins 2 & 3) using port register PINE, 4th and 5th bit
  bool pinAState = (PINE & (1 << 4)) == 0;
  bool pinBState = (PINE & (1 << 5)) == 0;
  rotaryValue +=  (pinAState != pinBState) ? -1 : 1;

If you only set up one interrupt on rotaryPinA, the 2nd interrupt method never gets called.

The code above is specific to the Arduino Mega 2560 and other boards that have the same pin to register mappings. It uses interrupts so it doesn't miss state changes in the pins, and reads the pin states using port registers for speed.

Note that if you're only adding a falling edge interrupt you can skip the read of pin A (since when the pin state falls it will, by definition be at a logic 0 whenever the ISR gets called.)

To get this to work well I had to build the RC filter circuit described in the spec sheet for my encoder:


The diagram looks like this:

enter image description here

And I had to add Schmitt triggers between the filter outputs shown above and the input lines to the Arduino.

Without the Schmitt triggers it gets jitter when the voltage from the filter doesn't switch between HIGH and LOW values fast enough. The Schmitt triggers exclude spurious readings from those "dead zone" voltages.

With those changes the readings from the encoder are quite good, although it still measure ± 1 tick or so per revolution for some reason.

I wish now I had bought the version of the encoder that has detents so it rotates in discrete clicks.

| improve this answer | |
  • 1
    I find it somewhat surprising that you had to add Schmitt triggers, as the Arduino already has built-in Schmitt triggers on all its digital inputs. – Edgar Bonet Feb 23 '19 at 11:52

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.