Position Encoders
Encoders transmit position or frequency info to the computer.
Here's a few ways to make life with them easier.
Published in Embedded Systems Programming, February 1991
 |
For hints, tricks and ideas about better ways to build embedded systems, subscribe to The Embedded Muse, a free biweekly e-newsletter. No hype, just down to earth embedded talk. 23,000 other engineers subscribe. It takes just a few seconds (all we need is your email address, which is shared with absolutely no one) to subscribe to the Embedded Muse. |
One of the most exciting embedded systems I worked on was a huge
guage designed to measure the thickness of metal in a steel mill.
A frighteningly radioactive cesium source, encased in a ton of
lead, shot gamma rays through up to 4 inches of steel. An ion
chamber measured just how much of the radiation made it through
the steel, providing the raw material of a thickness calculation.
Two embedded PDP-11 minicomputers and a handful of Z80s drove
the 7 ton sensor assembly back and forth on a railroad track,
so the guage could read the steel's thickness at any point across
the plate's 14 foot width. Without question, debugging the code
that ran the sensor back and forth on the track was the most fun
of the project! One software bug sent the monstrous assembly through
an electronics cabinet, causing no end of recriminations...
A problem we faced on this project was feeding the sensor's position
on the railroad track back into the computer. Inaccurate positional
information would invalidate all of the thickness data. The end-customer
was forking over better than $2 million for the system; they expected
correct data all of the time. In this case the solution was fairly
simple: we put a shaft encoder on one of the wheels. The encoder
transmitted a 12 bit binary code representing position back to
the computer.
Measuring position is important in most factory control environments,
the home of a lot of embedded systems. More often than not some
sort of encoder provides all of the location data to a computer.
An encoder is a mechanical device coupled to a rotating shaft
that provides a digital representation of the shaft's position.
Modern encoders almost exclusively use optical techniques to convert
the shaft's angle to digital form. A beam of light shines though
a disk fixed to the spindle. Photocells detect marks on the disk.
Depending on the type of encoder, these marks will represent either
an absolute or relative position.
Shaft Encoders
Shaft encoders convert position information into an absolute binary
code. A 12 bit encoder will output 0000 with the shaft at zero
degrees. At 90 degrees the code will be one quarter of the full
scale reading, or 400 hex. 180 degrees is 800h, until just before
0 degrees FFFh is output. It's pretty easy to compute the shaft's
angular position via a formula or lookup table given only the
number of encoder bits.
Sometimes binary is less than ideal. The science of Information
Theory teaches us that straight binary eats up a lot of "channel
capacity". As the encoder slowly rotates the binary value
increases monotonically, thankfully keeping the software that
reads it simple. Unfortunately, with each code change only one
bit might be different (say, from 000 to 001), or many bits might
change (from 7FF to 800). When lots of bits change at once trouble
can result. For example, many switching lines can cause crosstalk
in the cable.
Gray code is a variation of binary that eliminates much of the
problem. A Gray code encoder will change only one bit at a time
as the shaft rotates. Table 1 shows the relationship between Gray
and binary. Note that for each sequential entry in the table,
the Gray code changes only by a single bit. This reduces the demands
made on the transmission channel, whether they are simply wires
or a radio link.
(As an aside, Information Theory underlies much of the computer
world. It's interesting philosophically as well, as the Theory
relates how entropy, the amount of disorder in the universe, effects
just how fast one can send data over a communications link. See
"Information Theory" in the December 1987 Byte for a
quick summary of the subject.)
While Gray code might be ideal for an encoder's output, it's pretty
hard to use in internal computations. Standard practice is to
convert Gray to binary using a table translation scheme.
Computing absolute position is easy if we know the encoder's resolution
(in distance per revolution). One unknown still exists: what does
the "zero" position correspond to?
The "parked" or "zero" position of any mechanical
system is when it is all the way at one extreme or the other.
One crude way to calibrate the encoder is to have a technician
manually rotate it to give a reading of 000 when the system is
parked. Then, the computer can figure distance just in terms of
offset from the 000 position.
If anything in the mechanical part of the beast slips the position
data will contain errors. It's far better to add a limit switch
that is asserted when the sensor is parked. Then the software
can read the encoder whenever the limit is detected, and apply
this reading as an offset to all position calculations. It saves
the tedious calibration step, and reduces errors.
Have the computer regularly park the sensor and re-read the encoder
offset if things are prone to mechanical slipping. If the application
can stand having it offline once in a while, this will remove
all doubt about the positional accuracy.
A shaft encoder implicitly contains direction information. The
software will know if the shaft is rotating clockwise or counterclockwise
by examining the direction of the code change. This is important
in bidirectional systems, particularly when the shaft might be
controlled by external forces other than the computer.
Whenever moving parts are involved be wary of backlash. High quality
encoders themselves have no inherent backlash. In other words,
if the direction of rotation changes there will be no count uncertainty
due to mechanical play in the unit. Unfortunately, a perfect encoder
might still see backlash from play in the rest of the mechanics.
When a motor starts spinning, play in the gearing might make the
encoder not see the first few millimeters of travel. Where accurate
positioning is important the software might have to make the system
always approach a final position from the same direction, thus
always working with constant backlash errors. It's better to use
low backlash gears if you can convince the mechanical group to
go along with the extra cost.
Never put an encoder on a powered wheel. If the motor's startup
makes the wheel slip for an instant before it grabs the track,
then the encoder position will be wrong. Always connect the encoder
to an unpowered, coasting wheel.
Toothed Encoders
Another type of encoder gives a pulse stream as the shaft rotates.
No absolute position information is conveyed. The software must
count the number of pulses and infer position indirectly.
Before today's scribed glass disks, encoders looked rather like
toothed gears. The beam of light was interrupted by the rotating
teeth, giving rise to the name "toothed encoders". The
name stuck even as the technology passed the concept by.
Shaft encoders with binary codes are ideal for some systems, but
have a number of inherent problems. Their resolution is limited.
It's difficult to make an accurate encoder with more than 12 bits
of resolution - 4096 counts per revolution. Sometimes this is
just not enough. A toothed encoder can generate tens of thousands
of pulses per revolution.
In other cases the encoder is used not so much to indicate position
as to command the software to read an I/O port. A simple analogy
is the distributor in a car. A each of 4 positions per revolution
the distributor causes a contact to close, firing off a spark
plug. In the embedded world things are a bit more complex. A scanning
colorimeter might use a rotating diffraction grating to sweep
thousands of colors of light across a sample. The software must
read reflected energy at each color. If the grating's shaft is
connected to a toothed encoder, then each of the thousands of
pulses can interrupt the CPU and make it read the reflected light.
In this case we don't care about the shaft's absolute position
so much as we need a "read data now" interrupt from
the moving pieces.
Perhaps another example is in order. Remember the bad old days
of punched cards? Some readers had a toothed encoder coupled to
the shaft that moved cards through the scanner. One revolution
of the shaft corresponded to the length of the whole card. 80
separate pulses per revolution came from the encoder. Each one
came as a set of character holes were in position, and meant "read
a character now".
Most toothed encoders come with twin outputs. One is the pulse
stream indicating relative position. The other is a "zero"
pulse that is asserted only once per revolution, indicating the
start of a rotation. Using the zero and count pulses the program
can indeed come up with the same kind of absolute position information
output from a shaft encoder. If the encoder is calibrated just
like a shaft encoder, then the cheaper toothed version will give
accurate position information.
One downside of computing position this way is that a toothed
encoder gives no information about the direction of rotation of
the shaft. The pulse stream looks the same either way. Further,
if the mechanical assembly is moved when the computer is turned
off then the position will be incorrect. Unless, of course, the
computer recalibrates everything on power up.
Fast Reads
Most of the systems I've worked on required very fast response
to each encoder pulse. Only rarely can one afford the luxury of
polling a port to find that a pulse is asserted.
All polled loops are subject to varying degrees of latency. Consider
the following polling code:
loop:
in a,port ; read pulse port
and a,80 ; isolate pulse bit
jz loop ; jump if no pulse
The loop will fall through immediately if the bit becomes asserted
just before the input instruction is executed. If it goes high
just AFTER this same instruction, then it will execute the entire
loop again, doubling the detection time. This variable latency
is sometimes deadly.
Again consider the case of a car's distributor. Variable latency
will make the engine run rough, since the time of each spark plug
ignition will dither. In the case of a typical instrument collecting
data every 50 microseconds (say), a 5 microsecond dither represents
10% acquisition uncertainty.
In a high speed encoder system minimizing latency becomes a sort
of search for the holy Grail. I've spent weeks yanking just a
few microseconds out of the code to control the dither.
One obvious solution is to connect the pulse stream to the processor's
interrupt input. As we all know, an interrupt will immediately
stop the CPU and vector off to the interrupt service routine (ISR).
Actually, "immediately" is not quite true. Different
instructions take different amounts of time to execute. Sometimes
the range is several orders of magnitude, particularly when a
MULTIPLY instruction is compared to a NOP. Conventional interrupt
handlers are no better than a polled loop in minimizing latency.
Worse, interrupts are slow. When handling very fast data the interrupt
structure just might not be able to keep up. After all, a vectored
interrupt requires an acknowledge cycle to get the interrupt source,
several PUSHes to stack a return address and other context information,
and an indirect read from memory. All of this takes time - sometimes
quite a few microseconds.
Some processors support several types of interrupts. While the
Z80 is usually used in its most useful vectored configuration,
it does have an oddball mode left over from its 8008 heritage.
On an interrupt, external hardware can jam an instruction into
the execution stream. This mode bypasses all conventional interrupt
processing.
The following code takes advantage of a jammed NOP instruction.
loop:
halt ; wait for interrupt
<process interrupt>
jmp loop
The interrupt does nothing but exit the halt condition and execute
a NOP. Latency is just the processor's raw interrupt latency,
which is usually only one or two machine cycles. With no interrupt
servicing overhead, the code runs about as fast as possible.
The technique can be improved a bit where speed is a real problem.
Probably the interrupt processing code will maintain a count of
the number of pulses received, and will exit the loop when the
count is exceeded. Keep the count in the HL register pair. Then,
jam an INC HL instruction instead of NOP. This single byte opcode
will perform some useful work in the code, slightly increasing
the system's performance.
Hitachi's 64180 (called the Z180 by Zilog) has an even better
way to process fast interrupts. This is a CMOS processor that
minimizes power consumption. It has a very low power mode which
is initiated by executing the SLP (sleep) instruction. SLP is
rather like HALT, except for the lower current drain.
SLP has an unusual mode that is but poorly documented deep in
the chip's hardware description. Like HALT, only an interrupt
will exit a sleep condition. Like HALT, SLP will initiate conventional
interrupt servicing if the processor is in vectored interrupt
mode. However, if the global interrupts are disabled (via a DI
instruction), but an individual peripheral interrupt enable bit
is on, then that interrupt will exit sleep mode without starting
an interrupt acknowledge cycle. The code will just flow past the
SLP instruction into the next opcode, effecting just the sort
of fast response we need without having extra hardware to jam
a special instruction.
Most processors are not so well endowed. The 80x88 family, for
example, can only handle an interrupt with the conventional slow
service routine. Still, options exist even in these cases. Connect
external hardware that drives the processor into a permanent WAIT
condition when a particular I/O cycle occurs. Then, have the encoder
release the WAIT. The code will look like:
loop:
out al,dx ; assert a WAIT
<process data> ; come here after encoder pulse
jmp loop
Again, the latency will be minimal and execution speed fast.
Non-linear Encoders
We've been discussing conventional toothed encoders where each
pulse occurs exactly so much of a revolution after the previous
one. When rotated at a constant speed, the pulse output will represent
one constant frequency.
On one project we rotated a diffraction grating to produce a linear
sweep of colors. Unfortunately, the angle versus frequency of
a grating is related to the sine-squared of the grating's angular
position. For a while we used a hideously expensive and unreliable
assembly of specially shaped cams to rotate it at the trigonometric
rates, keeping a linear color output. The grating's irregular
motion and constant accelerations burned up bearings in days.
Then someone had a brilliant idea: why not make a special encoder
to linearize the data?
A mechanical engineer removed the cams so the grating would rotate
at a constant rate. Then, we had a special encoder made that gave
pulses at different rates depending on the position of the shaft.
These pulses were carefully spaced to compensate for the grating's
sine-squared frequency characteristic. By taking data whenever
a pulse arrived, the computer acquired an array which was linear
over the spectrum.
The moral is that in the embedded world we have unique opportunities
to solve complicated problems creatively. That's what makes this
business so much fun.
***********************************************
Gray Code Binary Code
0000 0000
0001 0001
0011 0010
0010 0011
0110 0100
0111 0101
0101 0110
0100 0111
1100 1000
1101 1001
1111 1010
1110 1011
1010 1100
1011 1101
1001 1110
1000 1111
Table 1: Gray and Binary Codes
***********************************************
|