I am trying to control a 3-Axis skull using vsa. Its working great. However, since i only am using 6 servos, two for the eyes, i am wasting the other 2 channels. Is there any way to make this a relay output?
If you're up for some fun you could modify the 8+8 program so that it has six servo channels plus 10 digital channels. If you get stumped, let me know.
Okay, it's another one of those nights with Chinese takeout at my desk, but as I've got it covered with other boards at the moment I can't test this -- still, I feel pretty confident. If you have troubles before I can dump it into a Prop-SX please let me know.
Updated: 25 AUG 2009
' =========================================================================
'
' File...... Servo6_Digital10.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... 25 AUG 2009
'
' =========================================================================
' -------------------------------------------------------------------------
' Program Description
' -------------------------------------------------------------------------
'
' Servo controller/output device designed for use with VSA or other
' real-time serial control programs.
'
' P0 to P5.... Servo control
' P6 to P15... Digitial outputs
'
' The protocol is identical to the SEETRON (www.seetron.com) MiniSSC:
'
' <sync><channel><value>
'
' sync...... $FF (255)
'
' channel... 0 to 5 for servos
' 6 to 15 for digital outputs
'
' value..... 55 to 245 for servos (150 = center)
' >127 = on for digital channels
' <128 = off for digital channels
'
' Default baud rate is 38.4K but can be changed down to as low as 2400.
' -------------------------------------------------------------------------
' Conditional Compilation Symbols
' -------------------------------------------------------------------------
' -------------------------------------------------------------------------
' Device Settings
' -------------------------------------------------------------------------
ID "Srvo6+10"
DEVICE SX28, OSCHS2, BOR42
FREQ 50_000_000
' -------------------------------------------------------------------------
' I/O Pins
' -------------------------------------------------------------------------
OutsHi PIN RC OUTPUT ' solenoid control outputs
Out9 PIN RC.7 ' use P15/OUT15
Out8 PIN RC.6 ' use P14/OUT14
Out7 PIN RC.5 ' use P13/OUT13
Out6 PIN RC.4 ' use P12/OUT12
Out5 PIN RC.3 ' use P11/OUT11
Out4 PIN RC.2 ' use P10/OUT10
Out3 PIN RC.1 ' use P9/OUT9
Out2 PIN RC.0 ' use P8/OUT8
OutsLo PIN RB OUTPUT
Out1 PIN RB.7 ' use P7/OUT7
Out0 PIN RB.6 ' use P6/OUT6
ServoCtrl PIN RB OUTPUT ' servo control pins
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
' -- ISR actually runs at 3.333 uS for better servo timing
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
mask 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
svoData VAR Byte (16) ' bank servo data
pos VAR svoData(0) ' position table
pos0 VAR svoData(0)
pos1 VAR svoData(1)
pos2 VAR svoData(2)
pos3 VAR svoData(3)
pos4 VAR svoData(4)
pos5 VAR svoData(5)
pos6 VAR svoData(6)
pos7 VAR svoData(7)
svoTix VAR svoData(8) ' isr divider
svoFrame_LSB VAR svoData(9) ' frame timer
svoFrame_MSB VAR svoData(10)
svoIdx VAR SvoData(11) ' active servo pointer
svoTimer VAR svoData(12) ' pulse timer
svoPin VAR svoData(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
' ----------------
'
Test_Servo_Tix:
ASM
BANK svoData ' (1)
INC svoTix ' (1) update divider
CJB svoTix, #3, Servo_Done ' (4/6) done?
CLR svoTix ' (1) yes, reset for next
' Code below this point runs every 10 uS
Check_Frame_Timer:
CJNE svoFrame_LSB, #2000 & 255, Inc_FrTmr ' (4/6) svoFrame = 2000 (20 ms)?
CJNE svoFrame_MSB, #2000 >> 8, Inc_FrTmr ' (4/6)
CLR svoFrame_LSB ' (1) yes, reset
CLR svoFrame_MSB ' (1)
MOV svoPin, #%0000_0001 ' (2) start servo sequence
CLR svoIdx ' (1) point to servo 0
MOV FSR, #pos ' (2)
MOV svoTimer, IND ' (2)
JMP Refesh_Servo_Outs ' (3)
Inc_FrTmr:
INC svoFrame_LSB ' (1) DEC svoFrame
ADDB svoFrame_MSB, Z ' (2)
Check_Servo_Timer:
TEST svoPin ' (1) any servos on?
SNZ ' (1)
JMP Servo_Done ' (1) no, exit
DEC svoTimer ' (1) yes, update timer
SZ ' (1) still running?
JMP Servo_Done ' (1) yes, exit
Reload_Servo_Timer:
INC svoIdx ' (1) point to next servo
MOV W, #pos ' (1) get pulse timing
ADD W, svoIdx ' (1)
MOV FSR, W ' (1)
MOV W, IND ' (1)
MOV svoTimer, W ' (1) move to timer
Select_Next_Servo:
CLC ' (1)
RL svoPin ' (1)
AND svoPin, #%0011_1111 ' (2) limit servo channels
Refesh_Servo_Outs:
AND ServoCtrl, #%1100_0000 ' (2) clear last
OR ServoCtrl, svoPin ' (2) update outputs
Servo_Done:
ENDASM
ISR_Exit:
RETURNINT ' (4)
' -------------------------------------------------------------------------
' Subroutine / Function Declarations
' -------------------------------------------------------------------------
RX_BYTE FUNC 1, 0 ' receive a byte
' =========================================================================
PROGRAM Start
' =========================================================================
Start:
TX = 1 ' set TX line to idle
' center servos
PUT pos, 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
Process_Value:
IF chan < 6 THEN ' servo channel?
value = value MIN LO_LIMIT ' force to legal limits
value = value MAX HI_LIMIT
pos(chan) = value
GOTO Main
ELSE
IF chan < 8 THEN
GOTO Dig_Lo
ELSE
GOTO Dig_Hi
ENDIF
ENDIF
Dig_Lo:
chan = 1 << mask
IF value.7 = 1 THEN ' >127?
OutsLo = OutsLo | mask ' yes, activate output
ELSE
OutsLo = OutsLo &~ mask ' no, deactivate output
ENDIF
GOTO Main
Dig_Hi:
chan.3 = 0 ' align for control port
mask = 1 << chan ' make channel bit mask
IF value.7 = 1 THEN ' >127?
OutsHi = OutsHi | mask ' yes, activate output
ELSE
OutsHi = OutsHi &~ mask ' no, deactivate output
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
' =========================================================================
Note that I've updated the program above -- I found some problems in the six-channel servo code while porting it to the EZ-8.