For those that need more than digital control of channels P8 to P15 the program below converts them to PWM outputs. Now you you can control the brightness of the LED spots on your talking skull.
VSA project setup: Channels: 0 to 15
Channels 0 to 7:
* MiniSSC Servo
* +Value = 240
* -Value = 60
* Default = 150
Channels 8 to 15:
* MiniSSC Dimmer or MiniSSC Relay
* +Value = 254
* -Value = 0
* Default = 0
Note that channels 8 to 15 can be setup as a dimmer or relay; use a relay if you're only going to be doing on/off control with a given channel in that range.
[Edit]: Posted updated version 25 OCT 2009
' =========================================================================
'
' File...... VSA_Servo8_PWM8.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 OCT 2009
'
' =========================================================================
' -------------------------------------------------------------------------
' Program Description
' -------------------------------------------------------------------------
'
' Servo/PWM controller designed for use with VSA or other real-time
' serial control programs.
'
' P0 to P7... Servo control
' P8 to P15... PWM/Digital outputs
'
' The protocol is identical to the SEETRON (www.seetron.com) MiniSSC:
'
' <sync><channel><value>
'
' sync...... $FF (255)
'
' channel... 0 to 7 for servos
' 8 to 15 for PWM/digital outputs
'
' value..... 60 to 240 for servos (150 = center)
' 0 to 254 for pwm outputs
'
' In VSA define servo channels as MiniSSC servo using 240, 60, 150 as the
' settings, and pwm channels as MiniSSC dimmer using 254 (max) and 0 (min).
' For digital behavior of a PWM output (e.g., for a valve) use MiniSSC
' Relay with an on value of 254 and off value of 0.
'
' Note: Servo min/max range is based on 180 values for Hitec servos, and
' this program reverses the VSA values to match them.
' -------------------------------------------------------------------------
' Conditional Compilation Symbols
' -------------------------------------------------------------------------
' -------------------------------------------------------------------------
' Device Settings
' -------------------------------------------------------------------------
ID "VSA_S8P8"
DEVICE SX28, OSCHS2, BOR42
FREQ 50_000_000
' -------------------------------------------------------------------------
' I/O Pins
' -------------------------------------------------------------------------
PwmCtrl PIN RC OUTPUT ' PWM control pins
Pwm7 PIN RC.7 ' use P15/OUT15
Pwm6 PIN RC.6 ' use P14/OUT14
Pwm5 PIN RC.5 ' use P13/OUT13
Pwm4 PIN RC.4 ' use P12/OUT12
Pwm3 PIN RC.3 ' use P11/OUT11
Pwm2 PIN RC.2 ' use P10/OUT10
Pwm1 PIN RC.1 ' use P9/OUT9
Pwm0 PIN RC.0 ' use P8/OUT8
ServoCtrl PIN RB OUTPUT ' servo control pins
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 60 ' to prevent servo burn-up
HI_LIMIT CON 240
SYNC CON 255
' Bit dividers for 3.255us interrupt
' -- actual code runs at 3.33us (within tolerance for bit sampling)
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
pwmTemp VAR Byte ' (keep global)
global3 VAR Byte
cmd VAR Byte ' SSC packet
chan VAR Byte
value VAR Byte
tValue 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
pwmData VAR Byte (16) ' bank for pwm vars
level VAR pwmData(0)
level0 VAR pwmData(0)
level1 VAR pwmData(1)
level2 VAR pwmData(2)
level3 VAR pwmData(3)
level4 VAR pwmData(4)
level5 VAR pwmData(5)
level6 VAR pwmData(6)
level7 VAR pwmData(7)
acc0 VAR pwmData(8)
acc1 VAR pwmData(9)
acc2 VAR pwmData(10)
acc3 VAR pwmData(11)
acc4 VAR pwmData(12)
acc5 VAR pwmData(13)
acc6 VAR pwmData(14)
acc7 VAR pwmData(15)
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
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
' --------------
' PWM Processing
' --------------
'
Process_PWM:
ASM
BANK pwmData ' (1)
ADD acc0, level0 ' (2)
RR pwmTemp ' (1)
ADD acc1, level1 ' (2)
RR pwmTemp ' (1)
ADD acc2, level2 ' (2)
RR pwmTemp ' (1)
ADD acc3, level3 ' (2)
RR pwmTemp ' (1)
ADD acc4, level4 ' (2)
RR pwmTemp ' (1)
ADD acc5, level5 ' (2)
RR pwmTemp ' (1)
ADD acc6, level6 ' (2)
RR pwmTemp ' (1)
ADD acc7, level7 ' (2)
RR pwmTemp ' (1)
MOV PwmCtrl, pwmTemp ' (2)
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, #%00000001 ' (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) INC 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
CLRB svoidx.3 ' (1) keep 0 - 7
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)
Refesh_Servo_Outs:
MOV ServoCtrl, svoPin ' (2) update outputs
Servo_Done:
ENDASM
ISR_Exit:
RETURNINT ' (4)
' -------------------------------------------------------------------------
' Subroutine / Function Declarations
' -------------------------------------------------------------------------
FLUSH_RX SUB 0 ' clear RX buffer
RX_BYTE FUNC 1, 0 Byte ' receive a byte
DELAY_MS SUB 2, 2, Word ' delay in 1ms units
' =========================================================================
PROGRAM Start
' =========================================================================
Start:
TX = 1 ' set TX line to idle
DELAY_MS 50
FLUSH_RX
' center servos
PUT pos, 150 , 150, 150, 150, 150, 150, 150, 150
Main:
cmd = RX_BYTE
IF cmd <> SYNC THEN Main
Get_Channel:
chan = RX_BYTE
IF chan = SYNC 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 < 8 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
chan = chan & $07 ' adjust for PWM port
tValue = value * value ' linearize; x = (x*x)/256
level(chan) = tValue_MSB ' update pwm channel
ENDIF
GOTO Main
' -------------------------------------------------------------------------
' Subroutine / Function Code
' -------------------------------------------------------------------------
' Use: FLUSH_RX
' -- flushes pending bytes in rx buffer
' -- waits on incoming byte if in process
SUB FLUSH_RX
ASM
BANK rxSerial ' point to serial vars
TEST rxCount ' receiving now?
JNZ @FLUSH_RX ' let it finish
CLR rxHead ' clear buffer pointers
CLR rxTail
CLR rxBufCnt ' clear buffer count
BANK $00
CLRB rxReady ' clear ready flag
ENDASM
ENDSUB
' -------------------------------------------------------------------------
' 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
' -------------------------------------------------------------------------
' Use: DELAY_MS duration
' -- delay in milliseconds
SUB DELAY_MS
ms VAR __WPARAM12
msTix VAR __WPARAM34
DO WHILE ms > 0
msTix = 300 ' load 1 ms timer
DO WHILE msTix > 0 ' let timer expire
\ CLRB isrFlag ' clear ISR flag
\ JNB isrFlag, @$ ' wait for flag to be set
DEC msTix ' update 1 ms timer
LOOP
DEC ms ' update delay timer
LOOP
ENDSUB
' =========================================================================
' User Data
' =========================================================================
Am I the only one who sees a bunch of smiley faces in the code?
No, the problem is that if I don't leave smileys enable someone may think I'm cranky -- when an array index is 8 we end up with that. So...
(8) = ( 8 )
I'll fix those indexes because I just found that the smileys cause an error when one copies and pastes from the forum listing.