Home > Arduino, Code > Rotary Encoder: H/W, S/W or No Debounce?

Rotary Encoder: H/W, S/W or No Debounce?

October 20, 2010 Leave a comment Go to comments

A mechanical rotary encoder consist of switches that are opened and closed in a predetermined sequence. During the switch transitions noise is generated, typically lasting from 1 to 5 milliseconds; the switch is “bouncing”. The following photo shows a rotary encoder switching and captures the first millisecond after the swtich event. Notice that for approximate .7 milliseconds the switch is “bouncing”

In order to reliable determine the state or level of the switch, it has to be “de-bounced” either by adding hardware components to attenuate the noise or by waiting in the code sufficient time after the transition has occurred.

Software Debounce

One of the advantages of using Arduino is that the community is constantly producing code that can be shared and reused. The Arduino BOUNCE library has been designed to de-bounce switches and rotary encoders in S/W, without the need to add hardware de-bouncing components like capacitors and specialized chips to these devices.

The library takes into account a variable bounce time (the time after switching where there is noise) so that the code can be adapted to a particular with an optimal bounce delay. The library works by polling a specified pin and determining if a change in state (HIGH to LOW or LOW to HIGH) has occurred. Arduino Bounce is found here: http://www.arduino.cc/playground/Code/Bounce.

The code is very easy to use:

#include <Bounce.h>

// Instantiate a Bounce object with a 5 millisecond debounce time
// The new object in this example is called "rotary"
Bounce rotary = Bounce(pinA,5); 

void setup() {
  //(other code)
void loop() {
 // Update the debouncer
 // Returns true if the pin state changed. False if not.
  value=rotary.update ( );

 //{Other code)

Hardware Debounce

The implementation here uses hardware debouncing for the rotary encoder. In consists of a capacitor across the signal pins (A, B) and GND. The pull up resistor of the Arduino input pins is enabled, and A, B are connected to these input pins.

Bourns, has a recommended implementation published in this technical paper: Signal Conditioning. It consist of an external pullup resistor and the output pins are filtered with an RC filter as shown in the following diagram:

No Debounce Required

Circuits@Home has published some code that will read both PinA and PinB of a rotary encoder and return a valid CW or CCW transition (or no activity/invalid transition). The code is highly optimized and a little hard to understand. The main routine is just 5 lines of code!

/* returns change in encoder state (-1,0,1) */
int8_t read_encoder()
  int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
  static uint8_t old_AB = 0;
  old_AB <<= 2;                   //remember previous state
  old_AB |= ( ENC_PORT & 0x03 );  //add current state
  return ( enc_states[( old_AB & 0x0f )]);

The following tips will help you understand the code:

It uses Arduino Port Manipulation to read multiple inputs at the same time. Because of this, the input pins must be co-located in the same port.

The code reads a current state of the rotary encoder (the values of pinA and pinB)  and appends it to a previous state, creating a 4-bit “transition” value.

This 4-bit transition value is is used as the index (its corresponding decimal value) to the array of all possible transitions and the corresponding value in the array is returned. The code assigns 0 to invalid transitions or no transitions, 1 for CW transitions and -1 to CCW transitions.

The array is constructed by looking at valid and invalid transitions of a rotary encoder. If you look at the wikipedia entry for rotary encoders, you will notice that for CCW direction, moving from phase 1 to phase 2, you will get 00 to 01 in the pins, or a concatenated value of 0001. Phase 2 to phase 3 results in 0111 and so on. If you map all valid transaction into all the possible combination of the 4 bits, you get the following:

Transition Valid? Array Entry Array Index
0000 No 0 0
0001 CCW -1 1
0010 CW 1 2
0011 No 0 3
0100 CW 1 4
0101 No 0 5
0110 No 0 6
0111 CCW -1 7
1000 CCW -1 8
1001 No 0 9
1010 No 0 10
1011 CW 1 11
1100 No 0 12
1101 CW 1 13
1110 CCW -1 14
1111 No 0 15

My Choice

I decided to use hardware debounce because it is good to eliminate the noise at the source. In addition, the s/w debounce code will add a debounce (~5msec) time every time it is invoked whether the volume knob is adjusted or not. Seems like an unnecessary delay.

The no-debounce-required code is indeed very elegant: it is compact, fast and adds no delays in the code and requires no hardware debounce circuitry. However, just like in the case of the s/w debounce code, it will be executing all the time whether the rotary encoder has been turned or not. However, tt may not matter at all that the code is executing or adding delays all the time, after all the Arduino code runs in an infinite loop. The question is whether the extra execution will cause any critical delays to execution of other code.

I have just implemented the configuration as recommended by the BOURNS technical brief described above and it works very well. Because in this configuration the pull up resistors are implemented outside of arduino, the internal pull ups in Arduino are  to be disabled. That is:

digitalWrite(VOLUPPIN, LOW);
digitalWrite(VOLDOWNPIN, LOW);

Note: the code up to v 0.2x still assumes the old debounce circuit, namely a capacitor across pinA -Gnd and PinB-Gnd, but the use can make the changes described above if the new debounce circuit is used.

Update (May, 2011):

The following implementation has proven to be very reliable:

  • No H/W debouncing, the rotary encoder is used as it comes from the factory
  • S/W debouncing with one line of code [link]
  • Single interrupt code for the rotary encoder. This code is the simplest I’ve come up with so far.

H/W debouncing is still recommended. If you don’t want to implement the more sophisticated approach from the Bourns paper, you can just add a couple of capacitors from the signal pins (A, B) to the common pin. Here is a photo of simple H/W debouncing from my earlier project  (Experimentally, when I installed the blue capacitors (0.1uF)  they did not work quite well. Had to install additional .01uF caps (marked 103) in parallel.

And here is the right way to do it (from circuits@home). Notice that they are 104 capacitors – 0.1uF

Photo from circuits@home

  1. May 17, 2011 at 13:21

    I’m using rotary encoders too. I use a timer interrupt instead of a hardware interrupt. I also use the Arduino Bounce library. I don’t think that the new version of the library uses delays to debounce. It uses timers instead. So I wrote a little timer function that tests the logic in 12 microseconds. Take a look at http://practicalusage.com/?p=267 and maybe also http://practicalusage.com/?p=246
    I’m still doing tests for Version 1.1 of the software.

  2. BlogGeanDo
    May 17, 2011 at 16:36

    Hello Robert, that’s one IN-DEPTH study of rotary encoders. Very impressive. I’ll have to look at the code with more time to understand what you are doing. Thanks…

  3. May 22, 2011 at 13:50

    I can confirm that the new Arduino Bounce library doesn’t use delay(). The library relies on millis() instead.

  4. Jeff
    July 15, 2011 at 02:02

    I would prefer not to stall my main loop with a delay(). It’s not a very cooperative addition to the loop, assuming you have other things to do on the micro. Using millis() is the way to go.

  5. BlogGeanDo
    July 15, 2011 at 03:22

    Hello Jeff,

    Thanks for commenting. Yes, in general using millis() is the way to go, but my delay() is only two milliseconds whereas the resolution of millis() is one millisecond. So my worse case is wasting 1 msec 🙂

  6. Wagner
    September 9, 2016 at 18:18

    Hello, thanks for sharing this broader view of the theme, showing different solutions. Since I’m working with a lot of encoders in the.same projects, all of them advising me of the CHANGE via interrupts, I liked most your hardware proposal because that will avoid extra “fake” calls that could reduce the overall performance. But tks again for sharing different ideas that work and suit different needs.

  7. September 14, 2016 at 15:30

    Pageo Jewelers – Passion by Design

  8. September 14, 2016 at 16:57

    New Yorker Online Shop

  9. September 14, 2016 at 17:25

    Career Guide

  10. September 14, 2016 at 17:45

    Kosmetik Tagesdeals bis -85 Rabatt

  11. September 14, 2016 at 17:53

    new treatment vitiligo

  12. September 14, 2016 at 18:43

    sac gucci femme

  13. fds
    February 28, 2018 at 14:12

    the code does not avoids debounce, it still can happen, hw debounce is clearly mandatory and should even be inside the rotary encoder, maybe more expensive models exist

  14. John Dempsey
    November 21, 2019 at 01:00

    The link to the Bourns Signal Conditioning Tech not above is no longer valid. The document is now here: https://www.bourns.com/docs/technical-documents/technical-library/sensors-controls/technical-notes/Bourns_enc_sgnl_cond_technote.pdf?sfvrsn=348415eb_4

  15. John Dempsey
    November 21, 2019 at 01:29

    Also, the circuits@home link in the may 2011 is no longer valid. That domain has been taken over by a “Best of ” commercial hawking site..

  16. Marko
    March 4, 2021 at 16:32

    “No Debounce Required” seems to be intelligent code to read rotary encoder, but does not avoid the necessity of debouncing! By studying the code alone (not real test) one can conclude that it will return multiple events for the signal capture you posted too.

  17. BlgGear
    December 11, 2015 at 06:10

    Thanks for sharing. I will check it out…

  18. Marko
    March 6, 2021 at 13:55

    It’s a pretty clever idea. I built on it by checking four movement aggregates and avoiding the forbidden transfers 0011, 1100, 0110, and 1001 to get a fully safe and fairly short Python code tested on Raspberry Pi 3.

  19. Marko
    March 6, 2021 at 23:22

    I spent some time and created a webpage with the algorithm.
    In case you are interested, here is the address: https://www.pinteric.com/rotary.html

  1. July 20, 2011 at 06:27
  2. November 29, 2015 at 21:44
  3. November 5, 2017 at 18:23

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: