Reading Rotary Encoders
Rotary encoders, which are used in adjustment knobs and for rotation detection, are devices that are prone to issues such as “values skipping despite turning” or “occasionally moving in the opposite direction” if the implementation is too simple. This article explains everything from the mechanism of Phase A and Phase B to the implementation of a state transition table method that is highly resistant to false counts, as well as precautions unique to mechanical (contact-type) knobs.
- How Phase A and Phase B Work: Direction Can Be Determined by 2-bit Gray Code Transitions
- Why Simple Implementations Malfunction
- Standard Practice: State Transition Table Method
- Relationship Between Detents and Counts
- Read Intervals and Implementation Methods
- Hardware Precautions
- Summary
How Phase A and Phase B Work: Direction Can Be Determined by 2-bit Gray Code Transitions
Incremental rotary encoders output two pulses, Phase A and Phase B, in response to rotation. These two phases have a 90° electrical phase difference, and the direction of rotation can be determined by which phase changes first. Whether Phase A or Phase B leads in the defined clockwise (CW) direction depends on the model, wiring, and mounting reference, so be sure to rotate the actual device to check, using the procedure described later. Some models also have a Phase Z (origin signal) that outputs a pulse only once per rotation.
The 2-bit combination of Phase A and Phase B transitions in a sequence where only 1 bit changes at a time (Gray code), such as “00 → 01 → 11 → 10 → 00...”. This property is the key to the false count prevention measures described later (this example is a sequence where Phase B changes first. For encoders where Phase A leads, the sequence is reversed, so this is handled by inverting the signs in the table described later). In terms of implementation, the standard practice is to treat this as a transition of the 2-bit state (00/01/11/10) of Phase A and Phase B, and to regard only normal transitions (where only 1 bit changes) as valid.
Why Simple Implementations Malfunction
A common implementation is the method of “checking the level of Phase B at an interrupt triggered by the rising edge of Phase A.” This method has two problems. While it works assuming ideal waveforms, the contacts of mechanical encoders generate multiple edges in a short period due to chattering (contact bounce). As a result, (1) multiple counts may occur when the knob is turned by just one click (detent), or (2) the value of Phase B may become unstable at the moment of the edge, leading to incorrect direction detection. Since filtering with capacitors alone can cause missed counts, delays, or residual bounce depending on the conditions, software measures are also used in combination.
Standard Practice: State Transition Table Method
The standard practice for robust implementation is to use a table to determine all 16 possible “previous value → current value” transitions of the two-bit Phase A/Phase B state. Only correct transitions along the Gray code sequence are counted as +1 or -1, and transitions where 2 bits change simultaneously are discarded as invalid because they do not occur in ideal quadrature detection. Although such transitions may be observed momentarily in mechanical types, most are due to bounce or momentary sampling fluctuations, and discarding them helps suppress false counts. The back-and-forth transitions caused by chattering are canceled out by +1 and -1, which also serves as debouncing (chattering removal). The following code example assumes that read_pin_a() and read_pin_b() return the level of each phase as 0 or 1.
#include <stdint.h>
/* The state is a 2-bit value: (A<<1)|B. The index is (previous<<2)|current. */
static const int8_t enc_table[16] = {
0, +1, -1, 0,
-1, 0, 0, +1,
+1, 0, 0, -1,
0, -1, +1, 0
};
static uint8_t prev_state;
static volatile int32_t enc_count;
void encoder_poll(void) /* Called every 1 ms */
{
uint8_t state = (uint8_t)((read_pin_a() << 1) | read_pin_b());
static uint8_t inited = 0u;
if (!inited) { prev_state = state; inited = 1u; return; } /* Initialize with the current value on the first call */
enc_count += enc_table[(prev_state << 2) | state];
prev_state = state;
}
Whether the + direction of the count is clockwise depends on the wiring and bit assignment of Phase A and Phase B. Be sure to rotate the actual device to check, and if it is reversed, swap the signs.
Relationship Between Detents and Counts
For mechanical knobs with tactile clicks, many models advance Phase A and Phase B by 4 states (changing by 1 cycle) during 1 click, in which case the count divided by 4 becomes the “number of clicks.” Some models have 2 states per click, and some models have click positions that do not align with the electrical cycle, so check the datasheet or rotate 1 click on the actual device to see the count value and decide. In addition, stability varies depending on the input circuit, such as the presence of pull-ups, Schmitt trigger inputs, and external RC filters. If possible, consider using Schmitt trigger inputs or digital filters in combination. To prevent fractional remainders, implementing a design that finalizes the count at the click boundary (the state where both phases are stable) stabilizes the feel of operation.
Read Intervals and Implementation Methods
- Knobs turned by hand: Periodic polling of about 1 ms is sufficient. At the speeds at which people operate them, missed states are rarely an issue in most applications (detailed guidelines for sampling frequency are described later). Call encoder_poll() from a timer interrupt or a periodic task of an RTOS.
- High-speed rotation, such as that of motor shafts: Since polling will miss states, use the encoder mode (phase counting function) of the microcontroller’s timer or a hardware counter. The role of the software is to periodically read the counter and calculate the difference.
- Sampling frequency guideline: A practical guideline is to ensure a sampling frequency of 4 to 10 times or more the maximum edge frequency of Phase A and Phase B (theoretically, the lower limit is 2 times according to the sampling theorem (Nyquist theorem) to avoid missing changes in each phase, but a margin is provided considering timing jitter and interrupt latency). The specific value is determined by calculating the maximum edge frequency from the resolution, 4× decoding, and maximum rotation speed.
- When using the interrupt method: Trigger interrupts on both edges of both phases, and keep the processing inside the handler to a minimum, consisting only of table lookup and counting. Note that on 8-bit or 16-bit microcontrollers, reading a 32-bit variable may not be completed in a single instruction, so the volatile qualifier alone does not guarantee mutual exclusion. When reading enc_count in the main loop, copy it to a local variable within a critical section, such as by disabling interrupts, before use.
Hardware Precautions
- Pull-up: Since the mechanical type only opens and closes contacts, a pull-up resistor (including one built into the microcontroller) is required.
- Filter: Slowing the edges with a small-capacity capacitor (about several nF) combined with the table method provides even greater stability. However, be careful of waveform rounding in high-speed rotation applications.
- Stopping between detents: If the knob stops at an intermediate position, vibration may cause it to go back and forth by 1 bit. With the table method, this is canceled out without causing false counts.
Summary
It is standard practice to read rotary encoders using a state transition table, taking advantage of their Gray code property with a 90° phase difference. If you are struggling with instability using edge interrupts and level detection, simply switching to a table-based method will make it remarkably stable.

