Rotary encoders – Experiments

These articles will contain information about the tests that I‘ve done on using of multiple rotary encoders in Arduino and beyond.

Finally we will develop a module based on Altera CPLD that will allow simultaneous reading up to 32 encoders.

Encoders that we used in these experiments are the type EC12 and EC11.

RotaryEncoders – EC12 and EC11

There are many methods, projects and libraries that aim to manage rotary encoders in Arduino enviroment.

These can be categorized into two major classes:

  • Polling methods
  • Interrupts methods

Polling methods

The microcontroller continuously monitors the input from the rotary encoder(s). The main drawback of this method is that microcontroller have to check very fast for pulses so don’t miss it. And the microcontroller does not have time to perform other tasks.

Interrupts methods

One or two pins of the rotary encoder(s) are connected at interrupt pins of microcontroller. The main drawback of this method is that external interrupts in microcontrollers are limited resources.

For example ATmega8 has two external interrupts: INT0 and INT1. Atmega32 has three: INT0, INT1, INT2. Atmega328 has INT0, INT1, PCI0, PCI1 and PCI2.

The correct method to read rotary encoders:

Before speaking of the “correct method” and why it iscorrect”, we should see how it works effectively these encoders. These encoders (with which we will work further) are actually “incremental rotary encoders”. This type of encoders provides sequential output at pins A and B when the encoder is rotated.


There are 3 pins on on EC12, two for the Channel A and Channel B outputs and Common (which usually is connected to GND). EC11 has 2 extra pins that we will ignore for now (they correspond to a simple push button).

EC12 has 24 fixed position “detents” on 360º rotation. EC11 has 20 detents.


As we can see from the image above, in fixed positions(detents) both switches are open.We make a convention:

  • MSB (most significant bit) = Channel A
  • LSB (less significant bit) = Channel B
  • OPEN = 0
  • CLOSE = 1.

Transition from one detent to next detent will follow sequence:

  • CW (clockwise): 00→10→11→01→00
  • CCW(counterclockwise):00→01→11→10→00

If we try to implement this convention(which is very intuitive) we have the following (big)problem:

We need to connect common pin to VCC (high level signal). And we need pull-down resistors because we can’t leave input pins of microcontroller floating.

But AVR microcontrollers have already internal pull-up resistors. We want to use this and not add external pull-down resistors. We will connect common pin to ground and we will change our convention in according to this. These are values ​​that we will work further:

  • MSB = Channel A
  • LSB  = Channel B
  • OPEN = 1 (pull-up resistor will keep level high when switch is open)
  • CLOSE = 0 (when switch is closed, input pin of microcontroller will be connected to GND)

Our transitions will change as follows:

  • CW (clockwise): 11→01→00→10→11
  • CCW(counterclockwise) 11→10→00→01→11

 As we can see for each state of the encoder there are only two possible values ​​that can change its current state.

RotaryEncoderStateChanges Because we want to detect CCW or CW movements we consider not 4 but 7 states.


CCWFinal← CCWSt3 ← CCWSt2 ← CCWSt1 ← START CWSt1 CWSt2 CWSt3 CWFinal

CCWFinal = START = CWFinal

The following diagram will clarify this more (or not?).

FSM_EncoderAlthough the diagram looks a bit non intuitive because we expect to have four states, if we analyze each of the states we see that it takes into consideration every possible movement, including incomplete movements. CW_Flag=0 and CCW_Flag=0 are default for all transitions less those marked with RED.


Next we move on to the actual implementation of the algorithm that results from the diagram / table .


We have 7 states: START, CW_Step1, CW_Step2, CW_Step3, CCW_Step1, CCW_Step2, CCW_Step3. We put these states intro arbitrary order, but we will take care to maintain this order in the table which we will build.

The columns will be placed in ascending order so that we can directly use as a index in the table. Since all possible values ​​of our inputs are increasing sequence starting with 0 (00,01,10,11) we do not need to map these inputs to another index.

Our table is incomplete at this time because we not taken into account the most important thing CW_Flag and CCW_Flag. Usually we would have needed another table (or another method to select corresponding outputs)7 states occupy only 3bits. In each cell, we have 5 bits available to store additional data. For our flags (CW_Flag and CCW_Flag) we need 2 bits.We define these flags using the most significant bits (arbitrary choice).

Our table will take this final form:

Updating current state is done as follows:

Example of using this in interrupt routine:

Here is a complete example in Arduino using Atmega32, which will put it all together.

This article will not stop here. Other sections will follow:

  • Reading multiple rotary encoders with Arduino.(ATMEGA1284 DIP40).
  • Verilog design for multiple rotary encoders with Altera FPGA development board.
  • Downgrade to CPLD and PCB design for final project.

This sections are not ready yet for reasons of logistics.

I have already ordered 3 pieces ATMEGA1284 … about 1 week to delivery …arrived

Ordered ALTERA FPGA Development Board … about 3 weeks to delivery…arrived

Ordered TQFP100 carrier board for ALTERA CPLD.(Already have MAX II Cpld EPM570T100C5N) about 3 weeks to delivery. …arrived

If you’re interested in the evolution of the project,  check for updates once a week

Later edit (25.feb.2015):

Part 2 it’s now ready Rotary encoders experiments – part 2.

Updated: February 25, 2015 — 6:40 pm


Add a Comment
  1. I wonder if this could be decoded with a more simplistic state machine.

    Referring to this diagram×483.jpg

    Whenever the readout is 00 (at a dent) you just have to know the state S before.
    If S was 01 the the encoder was turned CW and if it was 10 then it was turned CCW.

    You could put the XOR of channel A and B on an interrupt to get all events you need.

    Am I wrong?

    1. It depends… 🙂
      I guess It will work correctly in 99% of cases.
      But it will not “catch” correctly cases when the user “changes his mind” from “intermediate positions”. So … if you do not mind this behavior… It will work like you said.
      Also, this (complicated) version will filter more better some noises.

    2. There is a way using a Half-Step table that only produces values in 00 state (or 11, but not both).
      This code using the FSM is the best for dealing with rotary encoders, but is which uses the most memory. It works for polling or interrupt without loosing any position and fully debounces them.

      Anyway if interrupts are used i recommend using some capacitors to filter the encoder outputs. It reduces the bouncing enough to not interrupt the microcontroller too much when it’s switching. A RC filter is recomended instead only a C but it works (i tested personally).

      The idea is to reduce the bouncing, not remove it completely. You don’t want to trigger the interrupts 100 times before the encoder sets (that takes CPU time), when with a simple filter you can reduce them to a minimum (or even eliminate all). If there is any bouncing remaining, the FSM will managate it.

      Important: RC values for the low-pass filter are critical. In general R=10K and C=1nF are fine for the most of designs.

      1. At first, thank you for the feedback! I must say that I gave up deliberately with hardware debouncing… because my intention is to read large numbers of rotary encoders… and I relate to this MCU as a dedicated MCU.

        Consider it as a specialized MCU (even is not) that have one purpose… to read this encoders and put values on other MCU. In that way, this (pseudo)specialized MCU gave me opportunity to get rid of these extra components… and greatly simplified the PCB.

        I did a project on a test board with 15 rotary encoders… it involves 30 capacitors and 30 resistors … I was glad to get rid of them… soldering them in addition to what already was there… to much mess on that board.

        But you have a point! And what a on test board seemed difficult, on a PCB it’s not a big deal… So yes, I will redesign a the PCB that I made a some time ago…

        I must say that this algorithm work flawless without RC filter… trust me… 🙂 I have done real tests …
        But RC filter likely will optimize the use of MCU, especially if this MCU also has something else to do.

        Hence in the end, thanks for your suggestion! I will incorporate it into my project. Luckily I did not get to order PCBs yet. I’ll do after redesign.

Leave a Reply

Your email address will not be published. Required fields are marked *

OpenHardware.Ro © 2017 Frontier Theme