# Rotary Encoder

Written on 30 September 2019

My 40m homebrew transceiver is controlled by an Arduino Nano clone. The tuning knob is a rotary encoder which has three pins - ground, A and B. A and B are connected to digital inputs with an internal pull-up resistor, so when the pin goes active a logical zero is read. Rotating the control produces two square waves in quadrature. For example, a series of counter-clockwise clicks produces the following:

This diagram is from Wikipedia.

Reading the state of A and B on each transition gives the following for clockwise and counter-clockwise:

CW
A 0 1 1 0 0
B 0 0 1 1 0
CCW
A 0 0 1 1 0
B 0 1 1 0 0

My first code to handle this was simple. I noticed that A is 1 for two transitions and on the second the state of B indicates whether movemement is CW or CCW. This led to the following code snippet:

``````if( (bRotaryA != prevbRotaryA) || (bRotaryB != prevbRotaryB) )
{
if( prevbRotaryA && bRotaryA )
{
if( !prevbRotaryB && bRotaryB )
{
*pbCW = true;
}
else if( prevbRotaryB && !bRotaryB )
{
*pbCCW = true;
}
}
prevbRotaryA = bRotaryA;
prevbRotaryB = bRotaryB;
}

``````

This worked well but I began to notice some strange effects. When rotating the control quickly clockwise to increase the VFO frequency, it would occasionally go backwards, to a lower frequency. Making seemingly unconnected changes to my code, or changing compiler optimisation level, affected the prevalence of the problem. It was obviously a timing problem. Clearly my logic needed to be cleverer and do some debouncing.

The solution was to count both A and B pulses and only register movement when there were two pulses on each:

``````// Only do something with the rotary control if there is a change
if( (bRotaryA != prevbRotaryA) ||
(bRotaryB != prevbRotaryB) )
{
// We need to debounce the control
// The two outputs run in a set sequence so we ensure this is
// followed.
// Count the A and B pulses
if( bRotaryA )
{
countRotaryA++;
}
if( bRotaryB )
{
countRotaryB++;
}

// If we have had 2 pulses from A and B then we are happy
// we have properly rotated
if( (countRotaryA == 2) && (countRotaryB == 2) )
{
// Whichever line is high tells us whether we are going CW or CCW
if( bRotaryA )
{
*pbCCW = true;
}
else if( bRotaryB )
{
*pbCW = true;
}
}
// If the control is back to its idle position then reset the counts
if( !bRotaryA && !bRotaryB )
{
// Reset the counts
countRotaryA = countRotaryB = 0;
}

prevbRotaryA = bRotaryA;
prevbRotaryB = bRotaryB;
}
``````

Now the control works perfectly.