For those that are looking for a servo/pwm controller for VSA this program will do the trick. It converts the EZ-8 into a VSA-compatible servo controller that will run six servos (Ch1 - Ch6) and two PWM channels (Ch7 - Ch8).
So, why would you use the EZ-8 for this instead of the Prop-SX?
1) Smaller footprint
2) EZ-8 has a 5A regulator for the 5v outputs (yes, we snuck that in just for this purpose)
-- that's not to say that 5A will handle all servo apps, but it should handle many if not most small props
3) The selector switch allows you to isloate a channel
-- 0 : all channels on
-- 1 to 8 : isolate a channel
-- 9 : all channels off
The ability to isolate a channel will be helpful when you want to go back and fine-tune the mouth movements of your talking skull without disrupting the whole VSA show.
I've attached a blank VSA (version 4) file for you to start with.
' =========================================================================
'
' File...... EZ-8_Servo.SXB
' Purpose...
' Author.... Jon Williams, EFX-TEK
' Copyright (c) 2009 EFX-TEK
' Some Rights Reserved
' -- see http://creativecommons.org/licenses/by/3.0/
' E-mail.... jwilliams@efx-tek.com
' Started...
' Updated... 24 AUG 2009
'
' =========================================================================
' -------------------------------------------------------------------------
' Program Description
' -------------------------------------------------------------------------
'
' Servo controller/output device designed for use with VSA or other
' real-time serial control programs.
'
' CH1 to CH6... Servo control
' CH7 to CH8... PWM 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 7 for pwm outputs
'
' value..... 55 to 245 for servos (150 = center)
' 0 to 254 for pwm outputs
'
' Default baud rate is 38.4K but can be changed down to as low as 2400.
' -------------------------------------------------------------------------
' Conditional Compilation Symbols
' -------------------------------------------------------------------------
' -------------------------------------------------------------------------
' Device Settings
' -------------------------------------------------------------------------
ID "EZ8_Srvo"
DEVICE SX28, OSCHS2, BOR42
FREQ 50_000_000
' -------------------------------------------------------------------------
' I/O Pins
' -------------------------------------------------------------------------
Outs PIN RC
Ch8 PIN RC.7 OUTPUT
Ch7 PIN RC.6 OUTPUT
Ch6 PIN RC.5 OUTPUT
Ch5 PIN RC.4 OUTPUT
Ch4 PIN RC.3 OUTPUT
Ch3 PIN RC.2 OUTPUT
Ch2 PIN RC.1 OUTPUT
Ch1 PIN RC.0 OUTPUT
LedGrn PIN RB.7 OUTPUT
LedRed PIN RB.6 OUTPUT
PgmBtn PIN RB.5 INPUT PULLUP
StartBtn PIN RB.4 INPUT PULLUP
MS3 PIN RB.3 INPUT PULLUP
MS2 PIN RB.2 INPUT PULLUP
MS1 PIN RB.1 INPUT PULLUP
MS0 PIN RB.0 INPUT PULLUP
TX PIN RA.3 OUTPUT
RX PIN RA.2 INPUT
SCL PIN RA.1 INPUT
SDA PIN RA.0 INPUT
' -------------------------------------------------------------------------
' 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
LED_OFF CON 0
LED_GRN CON 1
LED_RED CON 2
LED_YEL CON 3
' -------------------------------------------------------------------------
' Variables
' -------------------------------------------------------------------------
flags VAR Byte ' (keep global)
isrFlag VAR flags.0
rxReady VAR flags.1 ' rx byte waiting
ledFlash VAR flags.7
btnFlags VAR Byte ' (global)
swMS0 VAR btnFlags.0
swMS1 VAR btnFlags.1
swMS2 VAR btnFlags.2
swMS3 VAR btnFlags.3
btnStart VAR btnFlags.4
btnRec VAR btnFlags.5
pwmTemp VAR Byte
' -------------------------------------------------------------------------
sync VAR Byte
chan VAR Byte
value VAR Byte
mask VAR Byte
tmpW1 VAR Word
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
dimmer VAR Byte(4) BANK
level1 VAR dimmer(0)
level2 VAR dimmer(1)
acc1 VAR dimmer(2)
acc2 VAR dimmer(3)
ledCtrl VAR Byte (2) BANK ' LED color / flash ctrl
ledState VAR ledCtrl(0) ' 0-7; off, grn, red, yel
rgTimer VAR ledCtrl(1)
dbControl VAR Byte (4) BANK ' for debounce
ms1Timer_LSB VAR dbControl(0) ' isr ticks/millisecond
ms1Timer_MSB VAR dbControl(1)
btnTimer VAR dbControl(2) ' debounce timer (ms)
btnTemp VAR dbControl(3) ' work var for debounce
' =========================================================================
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
BANK rxSerial ' (1)
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 Outs, #%1100_0000 ' (2) clear last
OR Outs, svoPin ' (2) update outputs
Servo_Done:
ENDASM
' -----------
' PWM Control
' -----------
'
PWM_Control:
ASM
BANK dimmer ' (1)
CLR pwmTemp ' (1) clear work byte
ADD acc1, level1 ' (2) update accumlator
RR pwmTemp ' (1) rotate bit into work byte
ADD acc2, level2 ' (2)
RR pwmTemp ' (1)
AND Outs, #%0011_1111 ' (2) clear old bits
OR Outs, pwmTemp ' (2) update new pwm bits
ENDASM
' --------------
' LED Controller
' --------------
'
Led_Control:
ASM
BANK ledCtrl ' (1)
MOV W, ledState ' (1) get state
AND W, #%0000_0011 ' (1) keep legal (0 to 3)
JMP PC+W ' (3) jump to handler
JMP Led_0 ' (3)
JMP Led_1 ' (3)
JMP Led_2 ' (3)
JMP Led_3 ' (3)
Led_0: ' LED off
CLRB LedGrn ' (1)
CLRB LedRed ' (1)
JMP Led_Exit ' (3)
Led_1: ' LED green
SETB LedGrn ' (1)
CLRB LedRed ' (1)
JMP Led_Exit ' (3)
Led_3: ' LED yellow
INC rgTimer ' (1)
JNB rgTimer.3, Led_1 ' (2/4) 7G:1R
CLR rgTimer ' (1)
Led_2: ' LED red
CLRB LedGrn ' (1)
SETB LedRed ' (1)
JMP Led_Exit ' (3)
Led_Exit:
ENDASM
' ------------------------
' Switch / Button Debounce
' ------------------------
'
Check_Inputs:
ASM
BANK dbControl ' (1) point to timer
INC ms1Timer_LSB ' (1)
ADDB ms1Timer_MSB, Z ' (2)
CJNE ms1Timer_LSB, #$2C, DB_Exit ' (4/6) at 1ms?
CJNE ms1Timer_MSB, #$01, DB_Exit ' (4/6)
CLR ms1Timer_LSB ' (1) yes, re-start
CLR ms1Timer_MSB
DB_Check:
INC btnTimer
CJB btnTimer, #25, DB_Port_Scan ' (2/4) keep scanning for 25ms
CLR btnTimer ' (1) restart scan timer
MOV btnFlags, btnTemp ' (2) update program flags
MOV btnTemp, #%0011_1111 ' (2) reset for next scan
JMP DB_Exit
DB_Port_Scan:
MOV W, /RB ' (1) scan port bits (invert)
AND btnTemp, W ' (1) clear any released
DB_Exit:
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:
chan = btnFlags & $0F ' read the selector
IF chan = 0 THEN ' if 0
ledState = LED_GRN ' all channels active
ELSEIF chan < 9 THEN ' if 1-to-8 then
ledState = LED_YEL ' isolate
ELSE ' if 10 then
ledState = LED_RED ' all off
ENDIF
READ Isolate + chan, mask ' convert position to TRIS value
TRIS_C = mask ' update
IF rxReady = No THEN Main ' if no serial, keep waiting
Get_Sync:
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 > 7 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
value = HI_LIMIT - value ' invert to match VSA
value = value + LO_LIMIT
pos(chan) = value
ELSE
tmpW1 = value * value ' linearize; x = (x*x)/256
value = tmpW1_MSB
IF chan = 6 THEN
level1 = value
ELSE
level2 = value
ENDIF
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
BANK 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
BANK $00
ENDASM
ENDFUNC
' =========================================================================
' User Data
' =========================================================================
Isolate:
DATA %00_000000 ' all outputs on
DATA %11_111110 ' isolate ch1
DATA %11_111101
DATA %11_111011
DATA %11_110111
DATA %11_101111
DATA %11_011111
DATA %10_111111
DATA %01_111111 ' isolate ch8
DATA %11_111111 ' all off