Okay, this should about do it for the VSA-compatible programs for VSA; this one gives you 16 servos. If you look at the code it's very modular; what's happening, in fact, is that it's running two "virtual" servo controllers that have eight servos each.
VSA project setup: Channels: 0 to 15
Channels 0 to 15:
* MiniSSC Servo
* +Value = 245
* -Value = 55
* Default = 150
[Edit]: Posted updated version 16 JUN 2009
' =========================================================================
'
' File...... Servo16.SXB
' Purpose...
' Author.... Jon Williams, EFX-TEK
' Copyright (c) 2008-2009 EFX-TEK
' Some Rights Reserved
' -- see http://creativecommons.org/licenses/by/3.0/
' E-mail.... jwilliams@efx-tek.com
' Started...
' Updated... 16 JUN 2009
'
' =========================================================================
' -------------------------------------------------------------------------
' Program Description
' -------------------------------------------------------------------------
'
' Servo/PWM controller designed for use with VSA or other real-time
' serial control programs.
'
' P0 to P15... Servo control
'
' The protocol is identical to the SEETRON (www.seetron.com) MiniSSC:
'
' <sync><channel><value>
'
' sync...... $FF (255)
'
' channel... 0 to 15
'
' value..... 55 to 245 for servos (150 = center)
' -- these values can be adjusted in the code
'
' Default baud rate is 38.4K but can be changed down to as low as 2400.
' -------------------------------------------------------------------------
' Conditional Compilation Symbols
' -------------------------------------------------------------------------
' -------------------------------------------------------------------------
' Device Settings
' -------------------------------------------------------------------------
ID "Servo16"
DEVICE SX28, OSCHS2, TURBO, STACKX, OPTIONX, BOR42
FREQ 50_000_000
' -------------------------------------------------------------------------
' I/O Pins
' -------------------------------------------------------------------------
ServosHi PIN RC OUTPUT ' servos - high group
Servo15 PIN RC.7 ' use P15
Servo14 PIN RC.6 ' use P14
Servo13 PIN RC.5 ' use P13
Servo12 PIN RC.4 ' use P12
Servo11 PIN RC.3 ' use P11
Servo10 PIN RC.2 ' use P10
Servo9 PIN RC.1 ' use P9
Servo8 PIN RC.0 ' use P8
ServosLo PIN RB OUTPUT ' servos - low group
Servo7 PIN RB.7 ' use P7
Servo6 PIN RB.6 ' use P6
Servo5 PIN RB.5 ' use P5
Servo4 PIN RB.4 ' use P4
Servo3 PIN RB.3 ' use P3
Servo2 PIN RB.2 ' use P2
Servo1 PIN RB.1 ' use P1
Servo0 PIN RB.0 ' use P0
TX PIN RA.3 OUTPUT ' to PC
RX PIN RA.2 INPUT ' from PC
SCL PIN RA.1 INPUT ' EE clock line (I2C)
SDA PIN RA.0 INPUT ' EE data line (I2C)
' Note: TX, SCL, and SDA lines are not used by this program
' -------------------------------------------------------------------------
' Constants
' -------------------------------------------------------------------------
IsOn CON 1
IsOff CON 0
Yes CON 1
No CON 0
LO_LIMIT CON 55 ' to prevent servo burn-up
HI_LIMIT CON 245
' Bit dividers for 3.255 uS interrupt
' -- actual code runs at 3.33 uS (within tolerance for bit sampling)
Baud2400 CON 128
Baud4800 CON 64
Baud9600 CON 32
Baud19K2 CON 16
Baud38K4 CON 8
Baud1x0 CON Baud38K4 ' 1 bit period (ISR counts)
Baud1x5 CON Baud1x0 * 3 / 2 ' 1.5 bit periods
' -------------------------------------------------------------------------
' Variables
' -------------------------------------------------------------------------
flags VAR Byte ' (keep global)
isrFlag VAR flags.0
rxReady VAR flags.1 ' rx byte waiting
sync VAR Byte
chan VAR Byte
value VAR Byte
rxSerial VAR Byte (16)
rxBuf VAR rxSerial(0) ' 8-byte buffer
rxCount VAR rxSerial(8) ' rx bit count
rxDivide VAR rxSerial(9) ' bit divisor timer
rxByte VAR rxSerial(10) ' recevied byte
rxHead VAR rxSerial(11) ' buffer head (write to)
rxTail VAR rxSerial(12) ' buffer tail (read from)
rxBufCnt VAR rxSerial(13) ' # bytes in buffer
svoDataLo VAR Byte (16) ' servo data, 0 - 7
posLo VAR svoDataLo(0) ' position table
pos0 VAR svoDataLo(0)
pos1 VAR svoDataLo(1)
pos2 VAR svoDataLo(2)
pos3 VAR svoDataLo(3)
pos4 VAR svoDataLo(4)
pos5 VAR svoDataLo(5)
pos6 VAR svoDataLo(6)
pos7 VAR svoDataLo(7)
svoTixLo VAR svoDataLo(8) ' isr divider
svoFrLo_LSB VAR svoDataLo(9) ' frame timer
svoFrLo_MSB VAR svoDataLo(10)
svoIdxLo VAR svoDataLo(11) ' active servo pointer
svoTmrLo VAR svoDataLo(12) ' pulse timer
svoPinLo VAR svoDataLo(13) ' active servo pin
svoDataHi VAR Byte (16) ' servo data, 8 - 15
posHi VAR svoDataHi(0) ' position table
pos8 VAR svoDataHi(0)
pos9 VAR svoDataHi(1)
pos10 VAR svoDataHi(2)
pos11 VAR svoDataHi(3)
pos12 VAR svoDataHi(4)
pos13 VAR svoDataHi(5)
pos14 VAR svoDataHi(6)
pos15 VAR svoDataHi(7)
svoTixHi VAR svoDataHi(8) ' isr divider
svoFrHi_LSB VAR svoDataHi(9) ' frame timer
svoFrHi_MSB VAR svoDataHi(10)
svoIdxHi VAR svoDataHi(11) ' active servo pointer
svoTmrHi VAR svoDataHi(12) ' pulse timer
svoPinHi VAR svoDataHi(13) ' active servo pin
' =========================================================================
INTERRUPT NOPRESERVE 300_000 ' (4) run every 3.333 uS
' =========================================================================
Mark_ISR:
\ SETB isrFlag ' (1)
' -------
' RX UART
' -------
'
' UART code by C. Gracey, A. Williams, et al
' -- buffer and flow control mods by Jon Williams
'
Receive:
ASM
MOV FSR, #rxSerial ' (2)
JB rxBufCnt.3, RX_Done ' (2/4) skip if buffer is full
MOVB C, RX ' (4) sample serial input
TEST rxCount ' (1) receiving now?
JNZ RX_Bit ' (2/4) yes, get next bit
MOV W, #9 ' (1) no, prep for next byte
SC ' (1/2)
MOV rxCount, W ' (1) if start, load bit count
MOV rxDivide, #Baud1x5 ' (2) prep for 1.5 bit periods
RX_Bit:
DJNZ rxDivide, RX_Done ' (2/4) complete bit cycle?
MOV rxDivide, #Baud1x0 ' (2) yes, reload bit timer
DEC rxCount ' (1) update bit count
SZ ' (1/2)
RR rxByte ' (1) position for next bit
SZ ' (1/2)
JMP RX_Done ' (3)
RX_Buffer:
MOV W, #rxBuf ' (1) point to buffer head
ADD W, rxHead ' (1)
MOV FSR, W ' (1)
MOV IND, rxByte ' (2) move rxByte to head
INC rxHead ' (1) update head
CLRB rxHead.3 ' (1) keep 0..7
INC rxBufCnt ' (1) update buffer count
SETB rxReady ' (1) set ready flag
RX_Done:
ENDASM
' -------------------------
' Servo Processing - 0 to 7
' -------------------------
'
Test_Servo_Tix_Lo:
ASM
BANK svoDataLo ' (1)
INC svoTixLo ' (1) update divider
CJB svoTixLo, #3, Servos_Done_Lo ' (4/6) done?
CLR svoTixLo ' (1) yes, reset for next
' Low group code below this point runs every 10 uS
Check_Frame_Timer_Lo:
CJNE svoFrLo_LSB, #2000 & 255, Inc_Fr_Lo ' (4/6) svoFrame = 2000 (20 ms)?
CJNE svoFrLo_MSB, #2000 >> 8, Inc_Fr_Lo ' (4/6)
CLR svoFrLo_LSB ' (1) yes, reset
CLR svoFrLo_MSB ' (1)
MOV svoPinLo, #%0000_0001 ' (2) start servo sequence
CLR svoIdxLo ' (1) point to servo 0
MOV FSR, #posLo ' (2)
MOV svoTmrLo, IND ' (2)
JMP Refesh_Servo_Outs_Lo ' (3)
Inc_Fr_Lo:
INC svoFrLo_LSB ' (1) INC svoFrLo
ADDB svoFrLo_MSB, Z ' (2)
Check_Servo_Timer_Lo:
TEST svoPinLo ' (1) any servos on?
SNZ ' (1)
JMP Servos_Done_Lo ' (1) no, exit
DEC svoTmrLo ' (1) yes, update timer
SZ ' (1) still running?
JMP Servos_Done_Lo ' (1) yes, exit
Reload_Servo_Timer_Lo:
INC svoIdxLo ' (1) point to next servo
CLRB svoIdxLo.3 ' (1) keep 0 - 7
MOV W, #posLo ' (1) get pulse timing
ADD W, svoIdxLo ' (1)
MOV FSR, W ' (1)
MOV W, IND ' (1)
MOV svoTmrLo, W ' (1) move to timer
Select_Next_Servo_Lo:
CLC ' (1)
RL svoPinLo ' (1)
Refesh_Servo_Outs_Lo:
MOV ServosLo, svoPinLo ' (2) update outputs
Servos_Done_Lo:
ENDASM
' --------------------------
' Servo Processing - 8 to 15
' --------------------------
'
Test_Servo_Tix_Hi:
ASM
BANK svoDataHi ' (1)
INC svoTixHi ' (1) update divider
CJB svoTixHi, #3, Servos_Done_Hi ' (4/6) done?
CLR svoTixHi ' (1) yes, reset for next
' High group code beHiw this point runs every 10 uS
Check_Frame_Timer_Hi:
CJNE svoFrHi_LSB, #2000 & 255, Inc_Fr_Hi ' (4/6) svoFrame = 2000 (20 ms)?
CJNE svoFrHi_MSB, #2000 >> 8, Inc_Fr_Hi ' (4/6)
CLR svoFrHi_LSB ' (1) yes, reset
CLR svoFrHi_MSB ' (1)
MOV svoPinHi, #%0000_0001 ' (2) start servo sequence
CLR svoIdxHi ' (1) point to servo 8
MOV FSR, #posHi ' (2)
MOV svoTmrHi, IND ' (2)
JMP Refesh_Servo_Outs_Hi ' (3)
Inc_Fr_Hi:
INC svoFrHi_LSB ' (1) INC svoFrHi
ADDB svoFrHi_MSB, Z ' (2)
Check_Servo_Timer_Hi:
TEST svoPinHi ' (1) any servos on?
SNZ ' (1)
JMP Servos_Done_Hi ' (1) no, exit
DEC svoTmrHi ' (1) yes, update timer
SZ ' (1) still running?
JMP Servos_Done_Hi ' (1) yes, exit
ReHiad_Servo_Timer_Hi:
INC svoIdxHi ' (1) point to next servo
CLRB svoIdxHi.3 ' (1) keep 0 - 7
MOV W, #posHi ' (1) get pulse timing
ADD W, svoIdxHi ' (1)
MOV FSR, W ' (1)
MOV W, IND ' (1)
MOV svoTmrHi, W ' (1) move to timer
Select_Next_Servo_Hi:
CLC ' (1)
RL svoPinHi ' (1)
Refesh_Servo_Outs_Hi:
MOV ServosHi, svoPinHi ' (2) update outputs
Servos_Done_Hi:
ENDASM
ISR_Exit:
RETURNINT ' (4)
' -------------------------------------------------------------------------
' Subroutine / Function Declarations
' -------------------------------------------------------------------------
RX_BYTE FUNC 1, 0, 0 ' receive a byte
' =========================================================================
PROGRAM Start
' =========================================================================
Start:
TX = 1 ' set TX line to idle
' center servos
PUT posLo, 150, 150, 150, 150, 150, 150, 150, 150
PUT posHi, 150, 150, 150, 150, 150, 150, 150, 150
Main:
sync = RX_BYTE
IF sync <> $FF THEN Main
Get_Channel:
chan = RX_BYTE
IF chan = $FF THEN Get_Channel ' correct for re-sync
IF chan > 15 THEN Main ' check channel range
Get_Value:
value = RX_BYTE
IF value = $FF THEN Get_Channel ' correct for re-sync
value = value MIN LO_LIMIT ' force to legal limits
value = value MAX HI_LIMIT
Process_Value:
IF chan < 8 THEN ' low group?
posLo(chan) = value
ELSE
chan.3 = 0 ' adjust for high group
posHi(chan) = value
ENDIF
GOTO Main
' -------------------------------------------------------------------------
' Subroutine / Function Code
' -------------------------------------------------------------------------
' Use: aByte = RX_BYTE
' -- returns "aByte" from 8-byte circular buffer
' -- will wait if buffer is presently empty
' -- rxBufCnt holds byte count of receive buffer (0 to 8)
FUNC RX_BYTE
ASM
JNB rxReady, @$ ' wait if empty
MOV FSR, #rxSerial ' point to serial vars
MOV W, #rxBuf
ADD W, rxTail ' point to rxBuf(rxTail
MOV FSR, W
MOV __PARAM1, IND ' get byte at tail
INC rxTail ' update tail
CLRB rxTail.3 ' keep 0..7
DEC rxBufCnt ' update buffer count
SNZ ' exit if not zero
CLRB rxReady ' else clear ready flag
MOV FSR, #__DEFAULT
ENDASM
ENDFUNC
' =========================================================================
' User Data
' =========================================================================