November 23, 2024, 06:48:53 PM

News:

Got VSA?  Want to use your Prop-SX?  Now you can!  See the VSA section of the Library forum for Prop-SX code that works with VSA.


EZ-8 as a Servo/PWM Controller

Started by JonnyMac, August 24, 2009, 03:23:49 PM

Previous topic - Next topic

JonnyMac

August 24, 2009, 03:23:49 PM Last Edit: August 24, 2009, 04:33:21 PM by JonnyMac
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
Jon McPhalen
EFX-TEK Hollywood Office