Transmit:
ASM
BANK serial
TEST txCount
JZ TX_Done
DEC txDivide
JNZ TX_Done
MOV txDivide, #Baud1x0
STC
RR txHi
RR txLo
DEC txCount
MOVB TX, txLo.6
TX_Done:
BANK 0
ENDASM
You can see that the transmit code is somewhat simpler
that the receive code — the reason is that it’s not waiting on
anything, it’s just doing something. To transmit a byte, we’ll
load the byte to send into txHi (the output buffer) and set
the bit count, txCount, to 10; we have one start bit, eight
data bits, and one stop bit. Of course, we don’t want
to try to send a byte when we’re in the middle of another,
so we’ll check to make sure txCount is zero before sending
a new byte.
After enabling the transmitter, a start bit is placed on
the line and then the bits are sent out, at the specified
baud rate, LSB to MSB. You can see the RR (rotate
right) instructions in the code that shift the bits of the
transmitted byte out one at a time. Note that the STC (set
carry) instruction precedes the buffer rotation; what this
does is pad the end of the shifted data byte with 1s so that
we have a valid stop bit. If, for example, we wanted to have
two stop bits, the only change we’d need to make is to set
txCount to 11.
What may not be apparent is that the program can
receive and transmit bytes at the same time because our
circuit uses separate RX and TX pins. Yes, you can use the
same pin, but you’ll have to add a bit of check logic in
the TX_BYTE subroutine to make sure that we’re not in
the middle of receiving a byte when we want to transmit
one — other than the RX/TX pin definition(s), the ISR code
remains the same.
Okay, now for the really fun bit: the code that handles
the servo control. First things first: We have to divide the ISR
period by three to get the base servo timing. It’s easier than
you think:
Test_Servo_Tix:
ASM
BANK svoData
INC svoTix
CJB svoTix, #3, ISR_Exit
CLR svoTix
All we have to do is count up to three and execute
the rest of the servo management code when we get
there, otherwise we jump to the exit point of the ISR.
Let me share a secret with you ... this bit of code started as
STAMP APPLICATIONS
high-level SX/B that looked like this:
Test_Servo_Tix:
INC svoTix
IF svoTix < 3 THEN ISR_Exit
svoTix = 0
So why bother converting to assembly? Because I
want to learn and get comfortable with assembly, and the
best way is by exploring good examples — starting with
simple stuff. If you look at the list file output of one of
your programs (press Ctrl-L in the SX-Key IDE), you will
see how your SX/B code is translated into assembly. In
fact, the entire servo processing section started that way;
I took the assembly output from the compiler, trimmed
the few extra bits (that the compiler uses for safety), and
used it in the program. Doing this forced me to crack
open the SX book and look up instructions so that I could
really understand what’s going on. There will come a time
when we have really critical timing requirements and
using assembly is going to be the way to get there.
The reason SX/B provides such a clean output is so that
folks like us can learn from it, and for me that promise is
holding true.
Okay, back to the servo stuff ...
Here’s how the servo processing works. At the
expiration of a 20 millisecond timer, the first servo,
svoFrame, is started and the pulse timer, svoTimer, is
loaded with the servo value. On subsequent passes
through the code, the servo pulse timer is decremented.
When that timer expires, the next servo is started and the
servo timer value is reset to the appropriate value. After
the eighth servo is finished, nothing happens until the 20
millisecond frame timer expires and the process starts
again. Figure 4 shows what the servo outputs look like in
action and in relationship to the frame timer. As you can
see, only one output is on at a time — this means we only
need one active servo timer, so this significantly simplifies
the program.
Okay, here’s the bit that handles the frame timer; in this
code, you can see that when it expires the first servo port
is enabled and the servo timer is loaded with the Servo 1
timing value.
Check_Frame_Timer:
CJNE svoFrame_LSB, #0, Update_Frame_Timer
CJNE svoFrame_MSB, #0, Update_Frame_Timer
MOV svoFrame_LSB, #2048 & 255
MOV svoFrame_MSB, #2048 >> 8
MOV svoPin, #%00000001
CLR svoIdx
MOV FSR, #pos
MOV svoTimer, IND
JMP Refesh_Servo_Outs
Update_Frame_Timer:
SUB svoFrame_LSB, #1
SUBB svoFrame_MSB, /C
May 2007 21