April 18, 2024, 11:42:53 AM

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.


Prop-SX - 8 Servos plus 8 PWM with VSA

Started by JonnyMac, February 27, 2008, 07:03:10 AM

Previous topic - Next topic

JonnyMac

February 27, 2008, 07:03:10 AM Last Edit: October 25, 2009, 10:04:25 PM by JonnyMac
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
' =========================================================================
Jon McPhalen
EFX-TEK Hollywood Office

gadget-evilusions

February 27, 2008, 07:49:28 AM #1 Last Edit: February 27, 2008, 08:59:50 AM by JonnyMac
Am I the only one who sees a bunch of smiley faces in the code?
Brian
Evilusions LLC
www.evilusions.com for all your pneumatic components

JonnyMac

February 27, 2008, 07:58:08 AM #2 Last Edit: February 27, 2008, 09:00:00 AM by JonnyMac
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.
Jon McPhalen
EFX-TEK Hollywood Office