MP Electronic Devices: Yet another algorithm for rotary encoder control

This is a part of MP Electronic Devices site.

Principles of operation

Rotary encoders are devices that typically convert the angular motion of a shaft into digital output signals. There are many web resources that explain the mechanisms of these devices and how to connecting them to a microcomputer, but this page is not intended to be a complete overview. The idea is to present another algorithm to properly read the output of mechanical rotary encoders.

[Rotary encoder signal]

In principle, common rotary encoders generate the signal by alternately closing two switches, as shown in the figure above. Depending on the connection, the neutral state can be either High (1) or Low (0). We assume here the most common connection where the neutral state is High. For clockwise movement, the action on the first switch precedes the action on the second switch, while this is reversed for counterclockwise movement. If we denote each state as a binary number with two digits representing the states of the two switches, the neutral state is represented as 11, clockwise rotation is represented by the chain of states 1101001011 and counterclockwise rotation is represented by the chain of states 1110000111.

Switch bouncing

[Switch bouncing]

The problem is that mechanical electrical switches are not perfect, so the phenomenon switch bouncing occurs. When a switch is toggled, the contacts must physically move from one position to another. As the components of the switch settle into their new position, they bounce mechanically, causing the underlying circuit to open and close several times. The signal obtained is shown in the figure above.

This problem can be fixed either by hardware or software. In the case of software, this involves a certain subjectively determined hold time during which the activity of the switch is ignored. However, in case of the rotary switch we can avoid the hold time altogether. Since the pair of switches passes through four successive cycle states, we can in principle define exactly when the operation is completed.

Four state algorithm

[ADC Differential Pi]

The microprocessor can read out a total of four different states, and two successive readouts give a total of sixteen possible transitions, as shown in the figure above. Four transitions represent no movement, four transitions represent a quarter movement to the right, four transitions represent a quarter movement to the left, and four transitions represent theoretically impossible transitions.

Let's define a full cycle as a process that begins and ends in the neutral state. By assigning 0 for no movement, +1 for a quarter cycle movement to the right and −1 for a quarter cycle movement to the left, we get the aggregate of +4 for a full cycle to the right, the aggregate of −4 for a full cycle to the left and the aggregate of 0 for no movement. In particular, each individual switch bouncing, whether to the left or to the right, totals 0 and has no influence to the result. Also, the modulo of the aggregate and 4 gives us the exact state. For example, if the modulo is zero, the current state is the neutral state.

Special care must be taken with "impossible" transitions. Due to technical inadequacies, they are indeed possible. If we assign the value 0 to the impossible transitions, the aggregate of a full cycle with exactly one impossible transition is −6, −2, +2 or +6. To recognise that the full cycle contained one (or more) impossible transitions, we must assign a number larger than 10 to the impossible transitions, so that the aggregate of the full cycle will be larger than 4. However, if we assign the number 14, we not only recognise the existence of the impossible transition(s), but also the modulo of the aggregate and 4 is zero again when and only when the neutral state is reached.

Implementation of four state algorithm

All these considerations taken together can be observed in the C code for for Arduino linked below. The rotary function first updates lrmem, a binary variable with four digits, where the first two digits represent the previous state and the second two digits represent the current state, which is essentially the last transformation. The list TRANS contains the values for each transformation, as shown in the following table.

lrmemTransformationTRANSComment
0b000000000no movement
0b00010001−1movement to the left
0b00100010+1movement to the right
0b00110011+14impossible movement
0b01000100+1movement to the right
0b010101010no movement
0b01100110+14impossible movement
0b01110111−1movement to the left
0b10001000−1movement to the left
0b10011001+14impossible movement
0b101010100no movement
0b10111011+1movement to the right
0b11001100+14impossible movement
0b11011101+1movement to the right
0b11101110−1movement to the left
0b111111110no movement

The rotary function then updates lrsum, the variable containing the current aggregate. When the neutral state is reached, the function acts accordingly.

To reduce the computational load, the code linked below checks the current state only when the switch jump is detected by the interrupt. (No movement elements in the table are therefore no longer used.) Furthermore, the code can be used for rotary encoders, regardless of whether their neutral (initial) state is 11 as in the description above or 00. The initial state of the rotary encoder is read when the code starts. Finally, the code also provides for a push-button rotary encoder, whose state is also read when the code starts.

Four state C code for Arduino with interrupts

Note that no additional electronic components are necessary, if the code uses built-in Arduino pull-up resistors.

Two state algorithm

There is an alternative algorithm, which is not as compact but which further reduces the computational load.

In this algorithm, we are only interested in two half cycle states, the neutral state 11 and the inverted state 00. Since we no longer keep track of the intervening quarter cycle states, we now need to know which switch jumped to the neutral and inverted states last. If the second switch is the last to jump to the inverted state and then also the last to jump to the neutral state, we have one clockwise rotation. If the first switch is the last to jump to the inverted state and then also the last to jump to the neutral state, we have one counterclockwise rotation. In all other cases, we have a faulty cycle that is ignored.

The hidden genius of this seemingly straightforward algorithm is that the switch that jumped last is not necessarily the switch that jumped second. In fact, both switches can alternately jump several times before they reach the end of the half cycle. The usual cause of these additional jumps is the switch bouncing at the end of the previous half cycle or the change of rotation direction. Additional spurious jumps can also be caused by the intervening quarter cycle movements back and forth. All these jumps are ignored, as only the penultimate and the last jump to one of the neutral or inverted states are taken into account to determine the direction of rotation.

The coded algorithm requires fewer interrupt calls on switch bouncing, as we can only look for falling edges if we start in state 11 and only for rising edges if we start in state 00. The number of interrupt calls can be reduced even further: After, starting from one of the states 00 and 11, one switch has jumped, we only have to watch out for the jumps of the other switch, as both switches jump alternately. In this case, we achieve the absolute minimum required number of interrupt calls, shown in red in the figure below for both clockwise and counterclockwise rotation.

[Minimum number of interrupt calls on switch bouncing]

The code below can be used for rotary encoders, regardless of whether their neutral (initial) state is 11 as in the description above or 00. The initial state of the rotary encoder is read when the code starts. The code also provides for a push-button rotary encoder, whose state is also read when the code starts.

Two state C code for Arduino with interrupts

Note that no additional electronic components are necessary, if the code uses built-in Arduino pull-up resistors.

Updates

On request, the code was updated in June 2024 to provide for a rotary encoder whose neutral (initial) state is either 11 or 00.

The C code for Arduino was upgraded with interrupts in December 2024, inspired by the work of Ralph Bacon.

The two state algorithm was added in December 2024.

Links to other useful resources

Below are two links to similar algorithms that were developed independently of my four state algorithm.

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

Rotary Encoder : How to use the Keys KY-040 Encoder on the Arduino

Created by Marko Pinteric. Please contact me for suggestions, new tricks, etc.

Updated . Web page has been read by visitors since March 2021.