Shop OBEX P1 Docs P2 Docs Learn Events
Simple Servo VP in SX/B — Parallax Forums

Simple Servo VP in SX/B

JonnyMacJonnyMac Posts: 8,942
edited 2007-02-08 14:50 in General Discussion
In the prop industry servos are frequently used for animatronics and special FX. Since we're (EFX-TEK) about to release a Prop-SX controller, I thought it would be fun to write a little framework program that allows our customers to control servos as easily as their digital outputs. What I did, in effect, was make a little servo controller VP. The code is pretty easy, and entirely in SX/B. By using the VP approach one can simply write a position value to a servo and the interrupt code will take care of the refreshing.

The only restriction is that you can't use things like PAUSE, SERIN, etc. -- those functions that are timing-dependant. Since timing is a big part of prop control this framework includes subroutines that use an interrupt-driven timer for delays.

' =========================================================================
'
'   File...... Prop484-EZ.SXB
'   Purpose... Prop Control framework: 4 triggers, 8 outs, 4 servos
'   Author.... Jon Williams, EFX-TEK
'              Copyright (c) 2007 EFX-TEK
'              Some Rights Reserved
'              -- see http://creativecommons.org/licenses/by/2.5/
'   E-mail.... jwilliams@efx-tek.com
'   Started...
'   Updated... 06 FEB 2007
'
' =========================================================================


' -------------------------------------------------------------------------
' Program Description
' -------------------------------------------------------------------------
'
' This program provides a framework for simple prop control that may use
' multiple trigger inputs, digital outputs, as well as servos.
'
' An ISR (Interrupt Servo Routine) is used to process the servos so that
' the user needs only set the current position (in the pos(x) array) -- no
' updating or delays are required (as with the Prop-1 or Prop-2). 
'
' Note: SERIN, SEROUT, PAUSE, PAUSEUS cannot be used with this framework.
' Use the WAIT_x subroutines for program delays.


' -------------------------------------------------------------------------
' Conditional Compilation Symbols
' -------------------------------------------------------------------------


' -------------------------------------------------------------------------
' Device Settings
' -------------------------------------------------------------------------

DEVICE          SX28, OSCXT2, TURBO, STACKX, OPTIONX, BOR42
FREQ            20_000_000
ID              "P448-EZ"


' -------------------------------------------------------------------------
' IO Pins
' -------------------------------------------------------------------------

Trigger3        PIN     RC.7                    ' inputs
Trigger2        PIN     RC.6
Trigter1        PIN     RC.5
Trigger0        PIN     RC.4

ServoCtrl       PIN     RC
Servo3          PIN     RC.3 OUTPUT             ' servo outputs
Servo2          PIN     RC.2 OUTPUT
Servo1          PIN     RC.1 OUTPUT
Servo0          PIN     RC.0 OUTPUT

Outs            PIN     RB   OUTPUT             ' digital outputs
Out7            PIN     RB.7
Out6            PIN     RB.6
Out5            PIN     RB.5
Out4            PIN     RB.4
Out3            PIN     RB.3
Out2            PIN     RB.2
Out1            PIN     RB.1
Out0            PIN     RB.0


' -------------------------------------------------------------------------
' Constants
' -------------------------------------------------------------------------

IsOn            CON     1
IsOff           CON     0


' -------------------------------------------------------------------------
' Variables
' -------------------------------------------------------------------------

flags           VAR     Byte
sync            VAR     flags.0                 ' marks start of frame

idx             VAR     Byte

tix             VAR     Word                    ' (ISR) timer

svoPos          VAR     Byte (4)                ' servo positions
svoFrame        VAR     Word                    ' (ISR) servo frame timer
svoIdx          VAR     Byte                    ' (ISR) active servo index
svoPin          VAR     Byte                    ' (ISR) mask for active servo
svoTimer        VAR     Byte                    ' (ISR) servo pulse timer

tmpB1           VAR     Byte                    ' for subs/funcs
tmpB2           VAR     Byte
tmpW1           VAR     Word


' -------------------------------------------------------------------------
  INTERRUPT 100_000                             ' run every 10 uS
' -------------------------------------------------------------------------

' The ISR code is setup to run every 10 uS so that the pos(x) values match
' the BS1 PULSOUT parameter for servo control (100 to 200);  this simplifies 
' the code and uses less variable space. Only one servo pulse is on at a 
' given time.
'
' Note that after Servo3 is done the ISR waits until the start of the next
' 20 mS frame to start Servo0.

Update_Timer:
  IF tix > 0 THEN                               ' update delay timer if on
    DEC tix
  ENDIF

Check_Frame_Timer:
  IF svoFrame = 0 THEN                          ' time for new servo frame?
    svoFrame = 2000                             '   reload with 20 ms
    svoPin = %0001                              '   start servo cycle
    svoIdx = 0                                  '   set position pointer
    svoTimer = svoPos(0)                        '   load position timing
    sync = IsOn                                 '   mark start of frame
    GOTO Refesh_Servo_Outs
  ELSE
    DEC svoFrame                                ' no, update running timer
    sync = IsOff                                ' clear sync marker
  ENDIF

Check_Servo_Timer:
  IF svoPin = 0 THEN ISR_Exit                   ' abort if none running
  DEC svoTimer
  IF svoTimer > 0 THEN ISR_Exit                 ' abort if servo still running
  
Reload_Servo_Timer:
  INC svoIdx                                    ' point to next servo
  svoIdx.2 = 0                                  ' keep 0 - 3
  svoTimer = svoPos(svoIdx)                     ' load position timing

Select_Next_Servo:
  svoPin = svoPin << 1
  svoPin = svoPin & $0F                         ' limit to four servos

Refesh_Servo_Outs:
  ServoCtrl = ServoCtrl & $F0                   ' clear last servo
  ServoCtrl = ServoCtrl | svoPin                ' set active servo
    
ISR_Exit:
  RETURNINT


' =========================================================================
  PROGRAM Start
' =========================================================================


' -------------------------------------------------------------------------
' Subroutine Declarations
' -------------------------------------------------------------------------

WAIT_TIX        SUB     1, 2                    ' wait in 10 us units
WAIT_MS         SUB     1, 2                    ' wait in 1 ms units
WAIT_SYNC       SUB     0                       ' wait for frame


' -------------------------------------------------------------------------
' Program Code
' -------------------------------------------------------------------------

Start:
  FOR idx = 0 TO 3
    svoPos(idx) = 150                           ' start at center
  NEXT

Main:
  FOR idx = 100 TO 200
    svoPos(0) = idx
    WAIT_MS 60                                  ' slow sweep
  NEXT

  GOTO Main


' -------------------------------------------------------------------------
' Subroutine Code
' -------------------------------------------------------------------------

' Use: WAIT_MS ticks
' -- hold program in 10 uS ticks (1 to 65535)

WAIT_TIX:
  IF __PARAMCNT = 1 THEN
    tix = __PARAM1                              ' get byte value
  ELSE
    tix = __WPARAM12                            ' get word value
  ENDIF
  DO WHILE tix > 0                              ' hold while running
  LOOP
  RETURN

' -------------------------------------------------------------------------

' Use: WAIT_MS mSecs
' -- hold program in milliseconds (1 to 65535)

WAIT_MS:
  IF __PARAMCNT = 1 THEN
    tmpW1 = __PARAM1                            ' get byte value
  ELSE
    tmpW1 = __WPARAM12                          ' get word value
  ENDIF
  DO WHILE tmpW1 > 0
    tix = 100
    DO WHILE tix > 0
    LOOP
    DEC tmpW1
  LOOP
  RETURN

' -------------------------------------------------------------------------

' Use: WAIT_SYNC
' -- wait until start of next servo frame (up to 20 mS)

WAIT_SYNC:
  DO WHILE sync = IsOff
  LOOP
  RETURN


' =========================================================================
' User Data
' =========================================================================



Post Edited (JonnyMac) : 2/7/2007 8:29:51 PM GMT

Comments

  • JonnyMacJonnyMac Posts: 8,942
    edited 2007-02-07 23:25
    Here's another version of that program with the VP code converted to straight assembly. All I did was take the assembly output from SX and remove any uses of __PARAMx variables. In this version, all servo-related information is in one bank which frees up BANK 0 variable space for your foreground program. This version is easy to expand to eight servos without losing BANK 0 variable space.

    ' =========================================================================
    '
    '   File...... Prop484.SXB
    '   Purpose... Prop Control framework: 4 triggers, 8 outs, 4 servos
    '   Author.... Jon Williams, EFX-TEK
    '              Copyright (c) 2007 EFX-TEK
    '              Some Rights Reserved
    '              -- see http://creativecommons.org/licenses/by/2.5/
    '   E-mail.... jwilliams@efx-tek.com
    '   Started...
    '   Updated... 06 FEB 2007
    '
    ' =========================================================================
    
    
    ' -------------------------------------------------------------------------
    ' Program Description
    ' -------------------------------------------------------------------------
    '
    ' This program provides a framework for simple prop control that may use
    ' multiple trigger inputs, digital outputs, as well as servos.
    '
    ' An ISR (Interrupt Servo Routine) is used to process the servos so that
    ' the user needs only set the current position (in the pos(x) array) -- no
    ' updating or delays are required (as with the Prop-1 or Prop-2).  The
    ' ISR has been coded in assembly for maximum efficiency.
    '
    ' Note: SERIN, SEROUT, PAUSE, PAUSEUS cannot be used with this framework.
    ' Use the WAIT_x subroutines for program delays.
    
    
    ' -------------------------------------------------------------------------
    ' Conditional Compilation Symbols
    ' -------------------------------------------------------------------------
    
    
    ' -------------------------------------------------------------------------
    ' Device Settings
    ' -------------------------------------------------------------------------
    
    DEVICE          SX28, OSCXT2, TURBO, STACKX, OPTIONX, BOR42
    FREQ            20_000_000
    ID              "Prop484"
    
    
    ' -------------------------------------------------------------------------
    ' IO Pins
    ' -------------------------------------------------------------------------
    
    Trigger3        PIN     RC.7                    ' inputs
    Trigger2        PIN     RC.6
    Trigter1        PIN     RC.5
    Trigger0        PIN     RC.4
    
    ServoCtrl       PIN     RC                      ' servo outputs
    Servo3          PIN     RC.3 OUTPUT
    Servo2          PIN     RC.2 OUTPUT
    Servo1          PIN     RC.1 OUTPUT
    Servo0          PIN     RC.0 OUTPUT
    
    Outs            PIN     RB   OUTPUT             ' digital outputs
    Out7            PIN     RB.7
    Out6            PIN     RB.6
    Out5            PIN     RB.5
    Out4            PIN     RB.4
    Out3            PIN     RB.3
    Out2            PIN     RB.2
    Out1            PIN     RB.1
    Out0            PIN     RB.0
    
    
    ' -------------------------------------------------------------------------
    ' Constants
    ' -------------------------------------------------------------------------
    
    IsOn            CON     1
    IsOff           CON     0
    
    
    ' -------------------------------------------------------------------------
    ' Variables
    ' -------------------------------------------------------------------------
    
    flags           VAR     Byte
    sync            VAR     flags.0                 ' marks start of frame
    
    idx             VAR     Byte                    ' loop control
    tix             VAR     Word                    ' (ISR) for delay timing
    
    tmpB1           VAR     Byte                    ' for subs/funcs
    tmpB2           VAR     Byte
    tmpW1           VAR     Word
    
    
    ' banked variables
    
    svoData         VAR     Byte (16)               ' bank servo data (ISR)
    pos             VAR     svoData(0)              ' position table
    pos0            VAR     svoData(0)
    pos1            VAR     svoData(1)
    pos2            VAR     svoData(2)
    pos3            VAR     svoData(3)
    svoFrame_LSB    VAR     svoData(9)              ' frame timer (for 20 mS)
    svoFrame_MSB    VAR     svoData(10)
    svoIdx          VAR     svoData(11)             ' active servo index
    svoPin          VAR     svoData(12)             ' mask for active servo
    svoTimer        VAR     svoData(13)             ' servo pulse timer
    svoMask         VAR     svoData(14)             ' mask for disabled pins
    
    
    ' -------------------------------------------------------------------------
      INTERRUPT NOPRESERVE 100_000                  ' run every 10 uS
    ' -------------------------------------------------------------------------
    
    ' The ISR code is setup to run every 10 uS so that the pos(x) values match
    ' the BS1 PULSOUT parameter for servo control (100 to 200);  this simplifies
    ' the code and uses less variable space. Only one servo pulse is on at a
    ' given time.
    '
    ' Note that after Servo3 is done the ISR waits until the start of the next
    ' 20 mS frame to start Servo0.
    
    Update_Timer:
      BANK 0
      IF tix > 0 THEN                               ' delay timer running?
        DEC tix                                     '   yes, decrement
      ENDIF
    
    Check_Frame_Timer:
      ASM
        BANK svoData
        CJNE svoFrame_LSB, #0, Update_Frame_Timer   ' frame timer done?
        CJNE svoFrame_MSB, #0, Update_Frame_Timer
        MOV  svoFrame_LSB, #2000 & 255              ' yes, svoFrame = 2000
        MOV  svoFrame_MSB, #2000 >> 8
        MOV  svoPin, #%00000001                     ' start servo sequence
        CLR  svoIdx                                 ' point to servo 0
        MOV  FSR, #pos
        MOV  svoTimer, IND
        BANK 0
        SETB sync                                   ' sync bit on
        BANK svoData
        JMP  Refesh_Servo_Outs
    
    Update_Frame_Timer:
        SUB  svoFrame_LSB, #1                       ' DEC svoFrame
        SUBB svoFrame_MSB, /C
        BANK 0
        CLRB sync                                   ' sync bit off
        BANK svoData
    
    Check_Servo_Timer:
        TEST svoPin                                 ' any servos on?
        SNZ
        JMP  ISR_Exit                               ' no, exit
        DEC  svoTimer                               ' yes, update timer
        SZ                                          ' still running?
        JMP  ISR_Exit                               ' yes, exit
    
    Reload_Servo_Timer:
        INC  svoIdx                                 ' point to next servo
        CLRB svoidx.2                               ' keep 0 - 3
        MOV  W, #pos                                ' get pulse timing
        ADD  W, svoIdx
        MOV  FSR, W
        MOV  svoTimer, IND                          ' move to timer
        BANK svoData
    
    Select_Next_Servo:
        CLC
        RL   svoPin
        CLRB svoPin.4                               ' limit to four servos
    
    Refesh_Servo_Outs:
        MOV  W, ServoCtrl                           ' copy port bits
        AND  W, #%11110000                          ' clear last servo
        OR   W, svoPin                              ' set active servo
        AND  W, svoMask                             ' apply disable mask
        MOV  ServoCtrl, W                           ' update outputs
    
    ISR_Exit:
        BANK 0
      ENDASM
      RETURNINT
    
    
    ' =========================================================================
      PROGRAM Start
    ' =========================================================================
    
    
    ' -------------------------------------------------------------------------
    ' Subroutine Declarations
    ' -------------------------------------------------------------------------
    
    WAIT_TIX        SUB     1, 2                    ' wait in 10 us units
    WAIT_MS         SUB     1, 2                    ' wait in 1 ms units
    WAIT_SYNC       SUB     0                       ' wait for frame
    SET_OUTPUTS     SUB     1                       ' set all outputs
    
    GET_TRIGGERS    FUNC    1, 0                    ' read all trigger pins
    
    
    ' -------------------------------------------------------------------------
    ' Program Code
    ' -------------------------------------------------------------------------
    
    Start:
      FOR idx = 0 TO 3
        pos(idx) = 150                              ' center all
      NEXT
      svoMask = %00001111                           ' all servos active
    
    
    Main:
    
      ' put your control code here
    
      GOTO Main
    
    ' -------------------------------------------------------------------------
    ' Subroutine Code
    ' -------------------------------------------------------------------------
    
    ' Use: WAIT_MS ticks
    ' -- hold program in 10 uS ticks (1 to 65535)
    
    WAIT_TIX:
      IF __PARAMCNT = 1 THEN
        tix = __PARAM1                              ' get byte value
      ELSE
        tix = __WPARAM12                            ' get word value
      ENDIF
      DO WHILE tix > 0                              ' hold while running
      LOOP
      RETURN
    
    ' -------------------------------------------------------------------------
    
    ' Use: WAIT_MS mSecs
    ' -- hold program in milliseconds (1 to 65535)
    
    WAIT_MS:
      IF __PARAMCNT = 1 THEN
        tmpW1 = __PARAM1                            ' get byte value
      ELSE
        tmpW1 = __WPARAM12                          ' get word value
      ENDIF
      DO WHILE tmpW1 > 0
        tix = 100
        DO WHILE tix > 0
        LOOP
        DEC tmpW1
      LOOP
      RETURN
    
    ' -------------------------------------------------------------------------
    
    ' Use: WAIT_SYNC
    ' -- wait until start of next servo frame (up to 20 mS)
    
    WAIT_SYNC:
      DO WHILE sync = IsOff
      LOOP
      RETURN
    
    ' -------------------------------------------------------------------------
    
    ' Use: SET_OUTPUTS byteVal
    ' -- sets output bits to "byteVal"
    
    SET_OUTPUTS:
      Outs = __PARAM1
      RETURN
    
    ' -------------------------------------------------------------------------
    
    ' Use: result = GET_TRIGGERS
    ' -- returns byte containing trigger bit levels
    ' -- result.0 is Trigger0; result.3 is Trigger3
    
    GET_TRIGGERS:
      tmpB1 = RC & $F0                              ' copy trigger bits
      SWAP tmpB1                                    ' move to low nib
      RETURN tmpB1
    
    
    ' =========================================================================
    ' User Data
    ' =========================================================================
    
    
  • BeanBean Posts: 8,129
    edited 2007-02-07 23:38
    Jon,
    Cool code...

    I can't wait until the Prop-SX board are out! Great idea putting the EEPROM on them. Any ETA ?

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cheap used 4-digit LED display with driver IC·www.hc4led.com

    Low power SD Data Logger www.sddatalogger.com
    SX-Video Display Modules www.sxvm.com
    Stuff I'm selling on ebay http://search.ebay.com/_W0QQsassZhittconsultingQQhtZ-1

    "USA Today has come out with a new survey - apparently, three out of every four people make up 75% of the population." - David Letterman
  • CCraigCCraig Posts: 163
    edited 2007-02-08 14:32
    Jon,

    Thanks so much for making this (and all the other) code available.

    I think you know how much it helps people to learn. I can read the help files and books all day long, but a clear cut example shows me the light.

    Chris
  • JonnyMacJonnyMac Posts: 8,942
    edited 2007-02-08 14:50
    Bean (Hitt Consulting) said...
    Jon,
    Cool code...

    I can't wait until the Prop-SX board are out! Great idea putting the EEPROM on them. Any ETA ?

    Bean.


    Thanks; coming from you that means a lot. We don't have an ETA yet, but will be showing first-run prototypes at trade shows (Haunt X and Transworld) over the next few weeks.
Sign In or Register to comment.