; Program SSC_DEMO.SRC (Mini SSC educational demo).
; Written in Parallax dialect of PIC assembly language.
; This program is a demo version of the firmware for the Mini SSC,
; a device that controls eight radio-control servos in accordance with
; instructions recieved over a 2400- or 9600-baud serial hook-up. The
; commercial Mini SSC also recognizes individual board IDs, allowing up
; to 32 controller (256 servos!) on one serial port. This demo version
; is not addressable, works at a fixed rate of 2400 baud, and controls
; just on e servo. However, it has the same basic structure as the full-up
; program. It is provided for novice PIC users interested in a method for
; Running two time-sensitive processes concurrently.
; Serial data is accepted through ra.3; servo pulses (1 to 2 ms, repeated
; approx. 60 Hz) are output at rb.0. This program runs propertly on a
; PIC16C61, 71, or 84 with an 8 MHz clock. At the time this program was
; written, the 16C61 was new, and PASMX/PEPX did not recognize it as a
; valid device. Fortunately, the '61 programs just find from code generated
; for a 71 or 84 (provided you don't use the special features of those
; devices). If you program a 61, be sure that PEPX is set for a 71 - setting
; it for an 84 will cause programming errors.
device pic16c71, xt_osc, wdt_off, protect_off, pwrt_off
id 'SVDM'
;Symbols (constants) used in the program.
one_ms = 6 ; value to load into RTCC for 1-ms delay
ser_pin = ra.3 ; Serial-input pin
stb24 = 44 ; Start delay for 2400 bps @ 8 Mhz
bit24 = 164 ; Full delay for 2400 bps @ 8 Mhz
; Variable declarations. Variables for this program begin at address
; 0C hex, placing them above the PIC's special-function registers.
org 0Ch
svo_PC ds 1 ; Program segment counter for servo ISR
w_copy ds 1 ; Copy of w register made by ISR
delay_cnt ds 1 ; Counter for serial delay routines.
bit_cnt ds 1 ; Number of received bits.
rcv_byte ds 1 ; The received byte
temp ds 1 ; Reusable temporary storeage
position ds 1 ; Position value.
end_frame ds 1 ; Counter for end of frame delay
; Upon reset the PIC first executes the code located at address 0. In this
; demo, it jumps to the beginning of the foreground program located at
; "start" When an interrupt occurs, the PIC jumps to address 4. This is
; where the interrupt handler is located.
org 0
jmp start ; The reset vector.
org 4 ; The interrupt handler.
; The interrupt-service routine (ISR). When RTCC overflows, the resulting
; interrupt sends the program here. This ISR stores the w and status
; registers, then jumps to the program segment stored in svo_PC.
; The segments load RTCC and start and stop the servo pulses
; based on the data found in the variable position. Once finished, they
; store the address of the next segment to be executed, restore the
; registers, and execute the reti (return from interrupt) instruction.
do_servos
clrb rtif ; Reset timer-interrupt flag.
mov w_copy,w ; Save the status and W registers.
mov w,<>status ; Swap status into w.
mov st_copy,w
mov w,svo_PC ; Move servo program couter to w.
jmp w ; Jump to segment pointed to by svo_PC
; Segment zero starts the servo pulse by weiring a high to rb.0 and
; setting RTCC to interrupt again after a 1-ms delay, it loads segment 1
; into tho "servo program counter" that do_servos used to jump to the
; correct segment of the routine.
seg_0 setb rb.0 ; Initiate pulse
mov rtcc,#one_ms ; Set up 1-ms delay.
mov svo_PC#seg_1 ; Execute next segment at 1-ms timeout
mov w,<>st_copy ; Restore the status register and W.
mov status, w
swap w_copy
mov w,<>w_copy ; Return from interrupt.
reti
seg_1 mov w,/position ; Load the inverse of the position value
mov rtcc,w ; into the rtcc to create delay
mov svo_PC,#seg_2 ; Execute next segment after timeout
mov w,<>st_copy ; Restore the status register and W.
mov status,w
swap w_copy
mov w,<>w_copy
reti ; Return from interrupt
seg_2 clrb rb.0 ; End the pulse.
mov w.position ; Load the non-inverted position value
mov rtcc.w ; into rbcc to create a pad delay
mov svo_PC,#seg_3 ; Execute next segment after time out.
mov end_frame,#14 ; Wait 14 ms before seg_0 (see seg_3)
mov w,<>st_copy ; Restore the status register and W.
mov status,w
swap w_copy
mov w,<>w_copy
reti ; Return from interrupt
seg_3 mov rtcc,#one_ms ; Set up a 1-ms delay
djnz end_frame,:cont ; Repeat this segment untill end_frame=0
mov svo_PC,#seg_0 ; When end_frame runs out, point to seg_0
:cont mov w,<>st_copy ; Restore status register and W.
mov status,w
swap w_copy
mov w,<>w_copy
reti ; Return from interrupt
; Start marks the beginning of the initialization code that sets up the
; program. It configures the RTCC to increment off the system clock
; through a divide-by-8 prescaler. At 8 MHz, that makes one RTCC tick
; equal to 4 microseconds - the desired resolution of our servo-pulse
; generator. Start also sets the servo routine's program counter to
; segment 0.
start mov svo_PC,#seg_0 ; Point to base segment of ISR
setb rp0 ; Switch to register page 1.
clr wdt ; Transfer the prescaler to RTCC.
mov option,#10000010b ; and set it for /8
clrb rp0 ; Back to page 0.
mov intcon,#10100000b ; Turn on RTCC interrupt.
main mov lra,#1000b ; Serial input is ra.3.
mov lrb,#0 ; make rb all outputs.
:start_bit ; Receive serial data.
snb ser_pin ; Look for start bit.
jmp :start_bit ; No start bit? keep watching.
call serl_in ; Get the serial data.
mov delay_cnt,#bit24 ; Stop-bit delay.
call delay ; Wait for stop bit.
mov posiiton,rcv_byte ; Move data to position
goto main ; Endless loop.
; Subroutine to read in serial data from the port bit ser_pin.
; Note that there's no stop-bit delay. Callers may incorporate
; this delay into other operation.
serl_in
mov bit_cnt,#8 ; Eight data bits.
clr rcv_byte ; Get ready for new data.
mov delay_cnt,#stb24 ; Start-bit delay
call delay ; Wait untill first data bit.
:rcv mov delay_cnt,#bit24 ; Full-bit delay.
call delay ; Wait for bit.
movb c,ser_pin ; Put data bit into carry.
rr rcv_byte ; Rotate bit into byte.
djnz bit_cnt,:rcv ; Not full? Get next bit.
ret
; Simple do-nothing loop for variable length dealys. Program must
; logad delay_cnt before calling this routine.
delay jmp $+1
djnz delay_cnt,delay
ret