project fits very comfortably on
an ExpressPCB mini-board.
CRACKING THE CODE
The real challenge with this
project is the code, probably the
most sophisticated that I’ve presented in this column. I promise
you, though, that after you’ve
spent a bit of time with it, you
will reach that comfort zone —
something I hit within the last
month or two — where mixing SX
VPs is no longer a big mystery;
it’s simply a bit of work to do like
anything else.
Having spent some time
with other control protocols
lately, specifically LANC and
DMX512, I decided to take a
page from those books and
create a protocol for this project
that uses a break in the control transmission as the
synchronizing point. The idea is dirt simple: synchronization
of the packet is achieved by leaving the receive line into the
controller idle for at least two byte periods. After that, we
will expect a nine byte packet; the first byte contains the
state of the digital outputs and the eight bytes that follow
are the position values for the servos. Figure 3 illustrates
the control packet.
That all sounds pretty easy, right? It is, mostly, but things
get a little tricky when we start to look deeper into the
timing details. The packet structure and code on both ends
is kept clean by using byte values for the servos, suggesting
that we’d use a BS1-compatible, 10-microsecond resolution
for servo position. This means, then, that in order to receive
the packet while maintaining the servo positions we’d
like to have an ISR rate that is 10 microseconds or a nice
fraction thereof.
And there’s the rub ... You see, we really need to put
detail into the bit timing of the virtual UARTs so that we
have reliable communications. I decided on a maximum
data rate of 38.4K baud so that the level shifter circuitry
could be removed and I could control this with a BS2. At
38.4K, the bit time is 26.042 microseconds. But remember,
we need to sample the receive line at least four times per
bit period so that means we need to set the ISR period to
6. 51 microseconds.
You can see the problem: 6. 51 microseconds is quite a
long way from the 10 microsecond unit value we want for
servo control, and doesn’t divide evenly into it. What do we
do? Well, if we divide the bit period by eight, we get 3.255
microseconds, and that multiplied by three is 9.766 microseconds, which is very close to the 10 we’re looking for — we
can live with that. This means, then, that to get the servo
centered (at 1,500 microseconds) we will use a position value
of 154 which actually gives us 1504 microseconds; I’d say
■ FIGURE 1. Processor schematic.
that will work and we know that our serial data is going to
be solid as we’re actually sampling the receive line twice as
frequently as we need to.
In review, we’re going to set the ISR rate to 3.255
microseconds and have the serial routines divide that by
eight for proper bit timing, and the servo routine will divide
the ISR by three to derive its base timing of approximately
10 microseconds. The ISR does a couple other things, too;
here is the list, in order of appearance:
• Process the delay timer (if running).
• Update the packet sync timer.
• Receive a serial byte.
• Transmit a serial byte.
• Refresh the servos.
Since most of the work for this project is done in the
ISR, that’s were we’ll focus our discussion. Here we go ...
‘ ————————————————————————————
INTERRUPT NOPRESERVE 307_200
‘ ————————————————————————————
Update_Delay_Timer:
IF tix <> 0 THEN
DEC tix
ENDIF
Check_Sync_Timer:
INC syncTimer
IF syncTimer > SyncCount THEN
armed = Yes
syncTimer = 0
ENDIF
The interrupt is declared with a rate of 307,200 times
per second; this gives us a period of 3.255 microseconds.
And as the SX/B code used in the interrupt doesn’t use any
May 2007 19