Archive
Arduino Rotary Encoder: One Line Debounce Code
When using a rotary encoder, I like to use an interrupt line to detect that it is being used. Using this approach, one can write the debounce code with a simple delay. In the code below, the interrupt routine simply sets a flag. Then the code waits for the “bouncing” to finish. Empirically this takes about one millisecond. I used 2 milliseconds and I’ve tested the code with a rotary encoder WITHOUT any kind of hardware debouncing.
// Rotary encoder interrupt service routine ... static boolean rotating=false; void rotEncoder() { rotating=true; // If motion is detected in the rotary encoder, // set the flag to true } ... void setup() { // Attach Interrupts attachInterrupt(0, rotEncoder, CHANGE); // ISR for rotary encoder ... ... } void loop() { ... while(rotating) { delay(2); // debounce by waiting 2 milliseconds // (Just one line of code for debouncing) if (digitalRead(4) == digitalRead(2)) // CCW ... else // If not CCW, then it is CW ... rotating=false; // Reset the flag } } Note: The small Arduinos (Duemilanove, Uno, etc) only have two interrupt lines: pin 2 and pin 3
Rotary Encoder: H/W, S/W or No Debounce?
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
Arduino UNO, Free Shipping…
Amazon is selling the Arduino UNO with free shipping. The Duemilanove is also free shipping but it is below the $25 threshold. You can save $5 with the Duemilanove, but the UNO gives you a cleaner, lower noise 3.3v which is good for audio applications
Modding Arduino UNO for Lower Noise
One of the main differences between the Arduino UNO and previous implementations of Arduino, is the 3.3V supply. In UNO, it is provided by a low noise, low drop regulator. In previous implementations it is taken from the internal 3.3V regulator of the USB FTDI chip. There is no specification on the 3.3V supply for the FTDI chip, but the specification for the LP2985 shows that the noise level is in the 30 uV RMS if proper capacitors are used. Another big difference is the supply current: 50 mA for the old boards, 150 mA for the new boards (even though the Arduino UNO specification says 50 mA)
When dealing with audio applications it is good to lower the noise of all the the power supplies and devices involved in the system. In the case of Arduino, it is connected to the DAC through the two I2C lines and the GND connection. For the implementation described here, the I2C lines are enabled by the level converter which is connected (on the low voltage side) to the Arduino UNO 3.3V regulator.
If we compare the implementation of the LP2985 regulator in the Arduino UNO schematic with the recommended implementation is the LP2985 data sheet, we find that the bypass capacitor is missing and the output capacitor is smaller than the size recommended for low noise operation. The diagram below shows these differences. (See data sheet on type of capacitors recommended)
Reading Buffalo II SPDIF Sample Rate
I have implemented reading the DPLL register in Buffalo II DAC. Shown in the photo is the sample rate in Hz for a 44.1K SPDIF signal. Previously I had used TPA’s AC-1 prototype to read the sample rate as shown in this post. As expected the results are exactly the same.
In this implementation the sample rate is read/updated once every two seconds, but this is configurable in the code. The new version of the code has been posted here.
Arduino Code for Buffalo II DAC: LCD Connection
(Click diagram for larger view)
For a good tutorial in hooking up a “standard” LCD to Arduino and use the standard LCD library, see: LadyAda Tutorial on LCDs
The diagram shows the basic connections with a “standard” LCD that is also compatible with the current Arduino LCD library. Adjustment for the contrast is manual with a pot. As shown, there is no adjustment for brightness. The LCD will always be at full brightness.
You can adjust the brightness by limiting the current through the GND pin of the LCD (pin 16) with the following circuit. Adjusting the pot will increase or decrease the brightness of the LCD.
Arduino for Buffalo II DAC
I’ve started writing the code for Arduino for the Buffalo II DAC. I am reusing the code I wrote for the Wolfson 8741 (TPA OPUS DAC), but I’ve decided to make some changes to make it easier for others to implement.
Hardware components:
- Arduino UNO (Although any Arduino clone will work)
- “Standard” Hitachi-HD44780-compatible LCD (20×4). This device has native support from the Arduino standard installation. In my previous code, I used a serial LCD implementation from a single supplier with custom libraries. The “standard” LCD is available everywhere. I got mine from SureElectronics on ebay
- “El cheapo” rotary encoder, debounced with capacitors. More info here and here
- 5V to 3.3V level converter for the I2C lines. Even though it has been reported that the Saber DAC is 5V tolerant at the inputs of the I2C, I’m using the level converter just in case. More info here.
The initial release consists of basic volume control with a rotary encoder and display of volume level from -99db to 0db.
The code is available here: Arduino code for Buffalo II DAC. Copy the text and paste it into an Arduino blank sketch; verify the code and upload it into the Arduino board. There are many good tutorials on how to get started with Arduino. Here is one: http://www.ladyada.net/learn/arduino/













Latest Comments