A Look Into the ISR
The initial ISR code deals with receiving serial data in the
background and buffering it. After that code is done, the
first thing that is checked is the scaling factor. If the scaling
factor is 0, then nothing needs to be done and we just exit
the ISR. Otherwise, we call the code to check and process
the encoders. There are a couple of important points here.
First, whenever you put code within an ISR routine you
must make sure there is enough time to execute the code.
It must all run within the ISR interval allocated to the ISR
routine. If you have too many instructions in there, you’ll
have to find ways to optimize your code or make the interval
longer so that you can execute more instructions in the ISR.
In the program presented here, it may seem odd to see
a GOSUB within the ISR. The code that is called still
executes within the ISR but needs to be moved to another
bank of memory due to the way SX/B structures the
compiled program. By moving things around, it resolved an
SX/B error on Pass 2. “Address xxx is not within lower half
of memory page.” I’ve had good results with SX/B so far,
and when errors like this crop up they can often be resolved
by re-organizing some of your program.
The code to process the encoder starts out by reading the
current value of the encoder port and masking off the bits so
that the current states of Channel A and Channel B on the
first encoder are used. Then, this value is XOR’d with the
previous encoder state. If there is no change in state, then the
value will be zero and we end up dropping down to check
the next encoder. If there was a change of state, then we
need to figure out what the direction of the change was.
This is again accomplished with our friendly XOR instruction.
In this case, we shift over the old state by one bit before
performing the XOR. We just need to compare bit 0 of one
channel to bit 1 of the other. It doesn’t matter which one you
use as the base, as long as you understand that using the
opposite set of bits will switch the direction of the encoder.
In the original program, it would see if the result from the
XOR operation was 1 which would specify to decrement
the counter. A 0 would then mean the counter should be
incremented. Instead of using a hard coded value, it compares
the XOR’d value with a DIP switch. This way, if the encoder
is going the wrong way, a flip of a switch will correct it! No
wiring changes or coding changes will be required.
Awh, You Count Funny
Whenever we decrement our counter so it rolls over or
increment it past our scaling factor, we need to update our
virtual encoder values on the output. When I did this, it was
late and I just updated a simple counter from 0-3 and sent
that value out as the scaled value. When I tested it, the
encoder output would only change by one up or down but
that was it. Upon looking at the code, I realized what I had
done. I forgot about counting funny. At least that is what my
kids would say. Instead of counting 0, 1, 2, 3, the proper
count was more like 0, 1, 3, 2, etc. This was due to the
gray encoding where only one bit can change status at a
time. This was easily fixed by using my counter as the index
for a LOOKUP command. This would then take the value that
was looked up and present that at the output. This results in
the correct gray code as the output for our virtual encoder.
The processing of the second encoder is identical. It
starts out slightly different, however, because the second
encoder is on a different set of pins. To accommodate that,
it has a slightly different mask of %00001100 to isolate the
two bits we need. Those two bits are then shifted right two
bits which will put them in the proper position so all the
calculations are the same as for the first encoder. The result
is then presented to the host controller on a separate set of
pins for the second virtual encoder.
The Extras
High resolution encoder disk.
HP encoder assembly.
Whenever I have leftover pins available on a microcontroller, I always seem to find a use for them. One of the
features I wanted to add is the ability to alter the scaling
factor on the fly. To accommodate this, one pin is used to
receive serial data from the host controller. The command
structure follows the AppMod style for compatibility with
other modules. The header is “!ES” for Encoder Scaler
followed by an address 0-3 and then the command. The
address must match those selected
by a pair of jumpers connected to
RC6 and RC7. If the address
doesn’t match, it will ignore the
command and wait until something
is directed toward it. The exception
is an address of 255 which would
broadcast out to all the modules of
this type, regardless of their configured address. At the moment, there
are only two commands supported.
The command “S” is a scale value
followed by a binary number 0-63.
Anything higher than this gets
limited to the highest scaling factor
42 SERVO 07.2008