Rotary Encoder: Immediately Tame your
Noisy Encoder! Find out how to
Instantly Stop Switch Bounce using one of 2 software methods. Easily get Reliable Operation from your Encoder.
Arduino rotary encoders: Are you struggling to get one reasonable output
from your encoder? Encoders are notoriously difficult to get a single
output change because
of switch bounce. What tends to happen is that multiple increments or
decrements occur.
Find out two reliable methods to tame your noisy encoder.
Get consistent output from an encoder.
Use them to ensure one position move results on only one change in output.
In this page the Keys KY-040 encoder is used throughout.
A rotary encoder is an input device that
you can rotate in either direction continuously. As you turn the device it
generates digital pulses to show the direction of rotation using two phased
output signals. These two outputs also indicate single position movements, so
you can use them in control panels to increment or decrement parameters.
Hair Saved! Neil Gleaden
: Been pulling my hair out trying to debounce a KY-040 on the STM32, Your
"Robust Rotary encoder reading" worked perfectly. You've saved me from
having to wear a hat, Many thanks. Facebook comment on page.
The Scope Lies! (or needs a closer look;) Jessie Kropp : Fantastic. Thank you. I was about to give up on coding for this rotary.
On the scope it all looked good but it's amazing how quickly the MCU can
read and mis-interpret things (or my bad code I should say). Facebook comment on page.
The type of encoder used below for demonstration is also known as an
incremental rotary encoder since it generates pulses indicating single step
changes. Other types generate an absolute output i.e. the same output number (4
or more bits depending on the accuracy required) is generated for a specific
position of the encoder and you would use these in robotics applications.
Note: Rotary encoders produce extremely noisy output
oscillations due to switch bounce and the information on this page gives you
two techniques to eliminate that noise. The first is a simple
filter method and the second method uses table decoding to get really good
output from low quality devices.
The purpose of this tutorial is to provide an example for the Arduino of a
simple rotary encoder implementation.
Rotary Encoders allow you to easily increase or decrease parameters by a single
value.
As well as generating directional information and step change pulses the
device has a physical feedback mechanism that lets you feel when you move from
one position to the next. These points are known as detents and range from 12
to 24 positions within a 360º rotation. For the device used here there are 20
detents.
Unlike a potentiometer the rotary encoder
has no end stops so you can use one to continually increase or decrease a
parameter (once decoded by the microcontroller) and there is no need to set the
control position back to a start point (there is none).
They also often have a push button switch built into the shaft which is
useful for menu selection etc.
You can use them for many applications ranging from:
Volume control
Illumination level control
Parameter control e.g. for a process e.g. speed, height, temperature
etc.
Menu selection (the push button is useful here).
Since the outputs are digital signals you can process them using a
microcontroller and use the result in any way you want i.e. to change the value
of a variable representing a system parameter.
In fact rotary encoders look simple, but there is quite a lot going on
inside these small devices (~11mm x ~13mm ).
Here's what is going on:
Two outputs providing quadrature coded signals.
Physical position feedback and bump stop (known as detents) - for this
device there are 20 detents.
Main shaft push button (a push to make switch).
As you turn the control knob you can feel the each of the 'detent' position
stops, so you know when you have turned the device by exactly one position.
This provides fine grained physical feedback allowing exact parameter changing.
This is very different to using a potentiometer to set the volume level etc.
where there is no physical feedback.
Quadrature Phase Shift
Encoding
This rather technical sounding encoding method is in fact very simple. all
it means is that two signals are offset from one another by a quarter of a
period (or phase shifted by 90º). It also falls out in the wash that the
signals generated are grey coded which just means that no two signals edges are
aligned i.e. signal outputs do not change state at the same time.
Gray coding is useful for electro-mechanical devices to generate signals
that are unambiguous. For example if the output was binary coded then at the
point of transition (due to small delays in signal paths) you might decode a
completely erroneous value i.e. at the point of transition any codes could be
generated.
This could be a problem especially if only combinatorial logic is used as
the decoder. Gray code stops that from happening (although it does not stop
switch bounce).
The following diagram shows the rotary encoder waveform output on pins A and
B (CLK) and (DT) respectively.
[Source PEC11L datasheet]
Note: The D in the diagram above shows where the detent
position is located. In fact this is where the outputs are not connected to
ground hence they are pulled high by the 10k resistors on the breakout
board.
Inside the Rotary Encoder
The following diagram shows the inner workings of the rotary encoder. Each
of the three connections 8A, 8B and 8C is formed of a spring arm that pushes
down on the substrate.
There are three signals, one connected to the metal substrate (Ground) and
two others that move over the alternating substrate pattern. So the outputs are
shorted to ground as the device is rotated and then are left floating
(unconnected) when the contact is in the substrate gap.
Note how the spring arm contacts are physically offset by a quarter of the
period (defined by the physical substrate) - contacts 8B and 8C in the diagram
below - this is how the quadrature encoded outputs are generated.
Source: Expired Patent (Now in the public
domain).
Note: The contacts 8A, 8B, 8C are springs that bounce on and
off the contact substrate causing the output signals to bounce between high and
low i.e. switch bounce.
However spring contact 8A, connecting to pin 8D, won't bounce as it is
on a substrate with no breaks. This should be used as ground with 8E and
8F connected to pullup resistors.
Types of Rotary Encoder
Contacting Incremental
Rotary Encoders
This is the type of device used in the demonstration on this page. At each
detent position two quadrature signals are generated indicating a single
position change and showing the direction of rotation.
This particular device has quite a high rotational life - 100k rotations
(see datasheet) - but since there is physical contact, the device will
eventually wear out. In the Bourns catalogue other physical devices range from 15k to 200k
maximum rotations.
Optical Encoder
The PEC11Lhas a maximum RPM of 60RPM whereas optical encoders, in that
catalogue, have a 10 million revolution life and can operate at 3000rpm - these
are the types you could use for measurements in high speed machinery but see
magnetic encoder below which has an even higher life and of course a higher
cost!
Magnetic Encoder
For even higher rotational life, a magnetic encoder offers the best choice
(since there is no physical contact within the device) the only part that will
wear out is the shaft bearings. These offer a rotational life of 100 million
revolutions!
These devices come in 4 different flavours:
Incremental Quadrature (same as the PEC11L used here).
Direction/Step encoder - offers better resolution (up to 512 pulses per
revolution).
Absolute encoder - allows the absolute position detection of the encoder
(1024 codes define the position).
PWM Encoder - generates a PWM output from 1us to 1024us width -
advantages claimed, are noise immunity and faster data acquisition.
Measurements: Using Rotary Incremental
Encoders
The following examples cover the following measurements:
Velocity
logarithmic change
Velocity
You might want to measure velocity to use as a parameter in your code e.g.
if you turn the wheel faster then perform a different action e.g. change the
parameter at a different rate.
Logarithmic
This is a parameter adjustment that measures the rotation speed and if found
to be constant periodically increases the parameter. This is very useful for
devices that have a large range of control e.g. a DDS (Direct Digital
Synthesis) that can output frequency form 1 to 10MHz. You really don't want to
sit there turning the knob by 1Hz periods to get to 10MHz!
Decoding Methods
There are two ways to decode rotary encoder outputs:
Polling
Interrupts
On the KY-040 there are two signals labelled DT and CLK meaning CLOCK and
DATA. If you look at the timing diagrams for these signals it seems obvious to
use the CLOCK as a clock and read the DATA input on the rising edge of the
clock. However this ignores the fact that the signals bounce all over the
place.
If you use the CLK signal as an interrupt you will get into deep trouble as
the random bouncing of the input will trigger interrupts all the time (and not
at the time when you want to read the data signal) so you will get incorrect
data.
There are polling methods to decode the grey coded signals using a state
machine so that bouncing signals are ignored i.e. error states are ignored.
These are quite complex and sometimes get out of sync.
The way I use these devices is a combination of a small amount of smoothing
capacitor and a simple digital rotary switch debounce algorithm. (See code below).
This provides easy to understand code (also small code size) and works to
accurately obtain individual detent position information and also accurate
directional rotation information.
Sometimes, though, you may have a very poor quality encoder and that
needs more effort to decode and in that case you will need to look at
the more complex robust decoder code here.
Device Decoding Techniques
You can use many clever ways to decode the outputs involving complex state
machines and gray decoding algorithms. Some use interrupts, and most use
polling.
The problem with connecting the outputs to an interrupt pin is that
you have no control over the bounce that you may experience and the processor
could be interrupted too much to do any useful work (and may even hang) and
will get the wrong value from the data input anyway.
Warning: Rotary encoders are extremely noisy because of switch
bounce due to the internal construction of the device (using physical contact
springs that bounce over the substrate connections). This makes it extremely
difficult to accurately decode the device outputs.
However see my new technique - the last code example code.
Switch bounce occurs as the contacts are springs that bounce on and off a
substrate contact - even the data sheet indicates the switch bounce you can get
for each turn is up to 10ms (Bourns PEC11L).
[Source PEC11L datasheet]
You can see that the signals labelled A and B can be changed to CLK (clock)
and DT (data) and that a rising edge on a clock signal (A) will give a a logic
low on DT (B) if turned clockwise, and a logic high if turned anti-clockwise
(when turned in the opposite direction the falling edge becomes the rising
edge!).
Capacitor Smoothing
Adding a huge smoothing capacitor (and resistor see diagram below and
replace the 0.01uF with 470nF as an example of a "too big" capacitor" - which
is suggested by some people) to stop the bounce will stop the bounce but also
slows the input signal level to the point where it will go through the
undefined logic input level (below the top threshold VIH and above
the lower threshold VIL) of the microcontroller. In this input
region noise on that input could (and often does!) trigger the input to high or
low causing oscillation i.e. making more bounce signals and not solving the
problem at all.
Note: Some microcontroller inputs have built in Schmitt trigger inputs.
You can get around this by using a schmitt trigger device such as a 74HC14
to create the correct fast edge signals but you may alter the timing too much
to get a useful output signal.
RC pair and digital Filter
One way I have found is to use a small smoothing capacitor resistor pair
along with a digital debounce filter. This allows individual detent positions
to be accurately identified (slow turning of the control shaft is accurately
decoded). At faster revolutions codes are missed but the real point of the
rotary encoder is to allow accurate individual detent (and direction)
detection. You don't need to know exact detent stops for fast revolutions - all
you need is to know that the user wants to increase the parameter faster.
Digital Debounce Filter
The digital filter is made up of a single 16 bit integer variable into which
you shift the current state of the input pin:
state=(state<<1) | digitalRead(CLK_PIN) |
0xe000;
This is a very compact filter - each time round the loop a new bit is
shifted left (at bit 0). The "or" action with 0xe000 defines the number of
iterations i.e. the top 3 bits are blocked off leaving the rest as useful
inputs. The idea is that you test for the state 0xf000 which can only occur if
there was a sequence of 1 0000 0000 0000 inputs meaning that the signal has
been stable for 12 iterations around the loop i.e. not bouncing around.
Arduino Rotary Encoder
Datasheet
The rotary encoder used in the KY-040 looks like a Bourns PEC11L device -
you can download that rotary encoder datasheet from the link below. All that
the breakout board does is add two 10k pullup resistors (R2 and R3) while the
space for the switch pullup has been left blank.
IDE Version Used : 1.6.4 Board used : Arduino Uno R3
Rotary Encoder Hardware Setup
Device Used : KY-040 (breakout board)
Other components 10k resistor and 10nF capacitor - only for the clock
signal, connected as shown below:
[Source PEC11L datasheet]
Note: 10k and 10n are extra to the breakout board (A and B have the 10k
pullups on the board). Only add them to the clock signal (A).
Example Rotary Encoder Code:
This is a an arduino ky-040 rotary encoder example that
shows you how to decode the 20 turn encoder by removing switch bounce. using a
digital filter technique. The operation of this digital filter is discussed here.
Note: The quality of the encoder will affect the output signal
(one of mine skips codes whereas a higher quality one does not!).
The keyes-040 encoder can be very noisy due to switch bounce and you may
need to use a much more robust way of decoding it - I have one that is fairly
well behaved and one that is massively noisy.
The following example uses a table decode method which requires more code
than in the previous example but is capable of reading rotary encoders without
needing any debounce capacitors at all. (However check that this works with
your own hardware to make sure of this).
The way it works is to encode the outputs of the decoder as a binary number.
To do this you can define the CLK as an LSB binary digit and DATA as the MSB
binary digit.
Then observe the valid states that the outputs can occupy i.e. the ones
shown in the diagram below by dotted lines.
Because the outputs are in quadrature and because this results in a gray
code output no output changes state at the same time as another. What this
means is that only one of the two outputs will be bouncing around at any
transition edge. This means bouncing signals can be easily ignored because the
bouncing mostly produces invalid encoder states.
If you look at the diagram above you can see that there are four states (11,
10, 00, 01). In addition to that, there are only 8 ways that you can move from
one state to the next including going backwards (Anti Clockwise).
For clockwise motion you can only perform the following actions:
(11 > 10), (10 > 00), (00 > 01) and (01
>11)
Similarly only the following encoder output transitions are valid for
Anti-Clockwise rotation:
(01 > 00), (00 > 10), (10 > 11), and (11
> 01)
You can find other rotary decoding methods (including this one) here.
The idea behind the table method is that you store the previous state and
current state and set them out as a binary code. In this way the table directly
encodes the transition of valid outputs - the main purpose behind the technique
is that invalid ones caused by switch bounce are discarded.
Valid Code Output
So for the clockwise direction above, there are four valid outputs (where
the 2 MSBits are the previous state and the 2 LSBits are the current state):
1110
1000
0001
0111
Only these are valid states. In theory only these should be output by the
rotary encoder but in practice switch bounce generates other codes.
For the opposite direction (Anti Clockwise) the following codes are
valid:
0100
0010
1011
1101
To allow the microcontroller to check for valid codes and ignore invalid
ones a table is needed (with the 4 bit PSNS - Previous State Next State - code
as the input):
PSNS (Prev State, Next State)
Valid code
Direction
0000
X
X
0001
Valid
CW
0010
Valid
CCW
0011
X
X
0100
Valid
CCW
0101
X
X
0110
X
X
0111
Valid
CW
1000
Valid
CW
1001
X
X
1010
X
X
1011
Valid
CCW
1100
X
X
1101
Valid
CCW
1110
Valid
CW
1111
X
X
Coding this into a C table and replacing CW with 1 and CCW with -1 and
invalid as 0 results in the following:
You can find code that uses this method elsewhere on the web ( I may have
swapped -1 for 1 - does not really matter just swap SIG A and SIG B) but that
method takes any CW or CCW valid output as a real transition and so for a
"detent" to "detent" movement four CWs or four CCW states are returned (the
detent position is shown in the diagram below). The problem is that bouncing
may cause the a state change backwards until the switch settles and goes
forwards again.
Improved Table Decode Method
By using the following code you can see the outputs generated between each
detent. The code simply generates a newline when it finds either 7 or 0xB.
These are the last codes generated when performing a detent to detent
rotation.
Rotary Encoder Quality Test
Program
Use the following program to see how good/bad your encoder is (observe
typical results below).
Using the program above I pushed the rotary encoder pushbutton to generate
the text "Next Detent" before turning the encoder to the next detent position.
This allows you to see all the codes that were generated during one position
change.
You can see that some of the rotations caused a lot of codes but the codes
were only able to go back by one state before returning to the correct one.
More importantly you can see that the final 2 codes are always the same
matching the 2 last nibbles for complete rotation sequences : D42B and E817.
For the "bad" rotary encoder the following output was generated:
Poor Quality Rotary Encoder
KY-040 Quality test:
D 4 2 8 2 B eleven
Next Detent
D 4 2 8 2 B eleven
Next Detent
D 4 1 4 2 B eleven
Next Detent
E 8 2 8 2 8 2 8 1 4 1 7 seven
D 7 seven
Next Detent
E B eleven
E 8 2 8 2 8 1 7 seven
Next Detent
E 8 2 8 2 8 2 8 1 4 1 7 seven
Next Detent
E B eleven
E 8 2 8 2 8 1 4 1 4 1 7 seven
Next Detent
E 8 1 4 1 4 1 4 1 4 1 4 1 7 seven
Next Detent
E 8 1 4 1 7 seven
Next Detent
E 8 1 7 seven
Next Detent
E B eleven
E B eleven
E B eleven
E B eleven
E B eleven
E B eleven
E B eleven
E 8 2 8 2 8 1 4 1 7 seven
Next Detent
For the good quality encoder the following output was generated:
Rotary Encoder test better quality encoder.
KY-040 Quality test:
E 8 1 7 seven
Next Detent
E 8 1 7 seven
Next Detent
E 8 1 7 seven
Next Detent
E 8 1 7 seven
Next Detent
E 8 1 7 seven
E B eleven
Next Detent
E B eleven
E B eleven
E B eleven
E B eleven
E B eleven
E B eleven
E B eleven
E B eleven
E B eleven
E 8 1 7 seven
Next Detent
D 4 2 B eleven
D 7 seven
D 7 seven
Next Detent
D 4 2 B eleven
Next Detent
D 4 2 B eleven
Next Detent
You can see that there is quite a difference between the two with the 1st
one generating far more code outputs (due to switch bouncing). The actual codes
that indicate a single detent-to-detent movement are E817 and D42B which are
the same values (shown in the discussion on valid binary codes above) for the
"valid" prevstate,nextstate coding.
You can see that there is a lot of bouncing while the switch is between
detents but not when it reaches the end. All the code outputs start with the
correct code either E or D then bounce around a lot and then end with the final
two codes.
Operation of Improved
Table Decode Code
The code below looks for the last two states to indicate a valid rotary code
output ( 0x2b and 0x17 ). This gives a double processing debounce - the first
being a "valid" output and the second being a "valid rotation". This works very
well and even allows the rotary encoder to return to its original position
(rotate CW 20 positions then rotate CCW 20 positions) with no missing codes -
even for an extremely noisy rotary encoder.
You may be able to use the full 16 bit hex code for less noisy ones (or high
quality ones). Your results may vary.
Note that this is with direct connection to the encoder - no debounce
resistors or capacitors (only the 10k resistor pull ups on the breakout
board).
Code For Improved Table
Decode
// Robust Rotary encoder reading
//
// Copyright John Main - best-microcontroller-projects.com
//
#defineCLK2
#defineDATA7
voidsetup(){
pinMode(CLK,INPUT);
pinMode(CLK,INPUT_PULLUP);
pinMode(DATA,INPUT);
pinMode(DATA,INPUT_PULLUP);
Serial.begin(115200);
Serial.println("KY-040 Start:");
}
staticuint8_tprevNextCode=0;
staticuint16_tstore=0;
voidloop(){
staticint8_tc,val;
if(val=read_rotary()){
c+=val;
Serial.print(c);Serial.print(" ");
if(prevNextCode==0x0b){
Serial.print("eleven ");Serial.println(store,HEX);}
if(prevNextCode==0x07){Serial.print("seven ");Serial.println(store,HEX);}
}
}
// A vald CW or CCW move returns 1, invalid returns 0.
int8_tread_rotary(){
staticint8_trot_enc_table[]={0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0};
prevNextCode<<=2;
if(digitalRead(DATA))prevNextCode|=0x02;
if(digitalRead(CLK))prevNextCode|=0x01;
prevNextCode&=0x0f;
// If valid then store as 16 bit data.
if(rot_enc_table[prevNextCode]){
store<<=4;
store|=prevNextCode;
//if (store==0xd42b) return 1;
//if (store==0xe817) return -1;
if((store&0xff)==0x2b)return-1;
if((store&0xff)==0x17)return1;
}
return0;
}
How to get accurate DHT22 digital humidity sensor readings with an Arduino. Did you know it also measures temperature as Well? Find out why, in this page...
A PIR sensor lets your Arduino sense movement without contact. This tutorial covers PIR sensor basics, connecting one to an Arduino board and coding a motion detector.
Arduino Hall Effect Sensor: Add magnetic sensing superpowers to your Arduino projects with an easy-to-use hall effect sensor. With full code and layout...
Comments
Have your say about what you just read! Leave me a comment in the box below.
Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.