Issue
152 March 2003
Using
Rotary Encoders as Input Devices
TWO-BIT
BINARY ENCODERS
As
the name implies, these devices translate the encoder’s
shaft position into one of four possible states; nonetheless,
these encoders generally have from 16 to 32 detents
or positions per 360° rotation. Therefore, it is the
microcontroller’s job to keep track of those cases in
which the encoder advances from the 3 position back
to the 0 position (analogous to a "carry"
situation in binary math). Equally important, it must
also sense when the encoder is rotating in the opposite
direction and goes from the 0 state to the 3 state (i.e.,
a "borrow").
For
this reason, it’s important that the microcontroller
be constantly aware of any changes in the encoder’s
position. For most applications, this means that both
of the encoder outputs must be connected to interrupt
inputs on the microcontroller. Failing that the least
you have to do to ensure proper operation is configure
the microcontroller to respond to an internal timer
interrupt occurring every 5 ms or so, and let the timer
ISR check the state of the two encoder outputs.
You’re
familiar with the normal binary number sequence, but
the term Gray code may not be familiar to you. Because
2-bit rotary encoders are often configured to produce
Gray code outputs rather than straight binary coding,
I’ll explain the concept. The term Grey code is also
used in the literature. I assume this means that a person
called Gray, or Grey, did not invent the code, because
I would expect the spelling to be consistent if it referred
to a person’s name.
If
you think for a moment about the normal binary sequence,
you’ll notice that, as the sequence progresses, there
are certain transitions during which many of the bits
change at the same time. The most striking example of
this in 8-bit binary code occurs during the transition
from 127 to 128, where the seven LSBs all go from a
one to a zero, and the MSB goes from a zero to a one.
Transitions like 63 to 64 and 31 to 32 are less dramatic,
but still result in a lot of bits changing simultaneously.
Because
rotary encoders are basically mechanical devices subject
to physical tolerances and wear, any multi-bit encoder
would suffer from problems arising because the exact
opening/ closing timing of the many internal switches
wouldn’t be the same. The problem would manifest itself
most blatantly at those positions where the codes changed
drastically.
Rotary
encoders have been around for years and were connected
to discrete logic chips early on. Therefore, during
the brief intervals where the switches were not perfectly
in-sync with each other, there existed the possibility
of numerous false position codes being generated at
the output of the logic devices connected to the encoder.
To
eliminate this problem, an alternate coding scheme—the
Gray code—was devised, in which any transition in a
straight up or down sequence would only differ from
its neighbor by a single bit change. For a rotary encoder,
which only changes in a sequential fashion, this meant
that at no time would more than one switch be opening
or closing. This eliminated the switch synchronization
problems that I mentioned earlier. Table
1 shows the bit patterns for both a standard 2-bit
binary sequence, as well as its Gray code equivalent.
For
the microcontroller to keep track of a 2-bit encoder’s
shaft position, in absolute terms, those carry/borrow
types of transitions must be recognized when they occur.
To achieve this the microcontroller must keep a record
of the encoder’s last 2-bit position, as well as the
current position, when any change occurs. Because there
are four possible output codes and two directions in
which the encoder could have turned in each of them,
eight possibilities have to be evaluated.
This
evaluation can be performed in code using four IF statements,
each of which contains two additional nested IF statements
(i.e., a total of 12 IF statements). The four IF statements
would test for each of the four current positions, and
the two nested IF statements would check to see which
of the two possible positions the encoder is in prior
to the change. This solution results in numerous lines
of code, and its execution time will vary from one case
to another, depending on how far down through the IF
blocks the program must go before it finds the matching
criteria. Later, when I discuss code examples for a
specific encoder, I’ll introduce a more elegant solution.