November 23, 2024, 08:28:28 PM

News:

Be sure to checkout our Vixen interfaces in the Library forum -- if you want PC automation at near zero cost, EFX-TEK and Vixen is a great combination of tools.


Servos and LEDs

Started by davisgraveyard, June 11, 2008, 10:23:57 AM

Previous topic - Next topic

davisgraveyard

In reading the post about fading LED's I started wondering what the code would look like (or if it is even possible) to be doing LED fading at the same time you are controlling servos.   

The example I had in mind is the code where the servo movements are part of the EPROM data and you are running the generic player style code to move the servos.  If you also want to fade LED's up and down as part of that sequence would that be possible?   How would you approach the problem?

Jeff

JonnyMac

That is possible, Jeff, in fact it's pretty easy after you get the hang of SX VPs.

What you need installed are two "virtual peripherals": a servo driver and an LED pwm driver.  If you'll tell me what the combo is, I'll bang out a template (code is done, will need just a little tweaking).  Let me suggest we work with common boundaries and have eight servos plus four PWM, or eight PWM plus four servos.  This will leave P12-P15 open for normal activities like triggering and serial (yes, I could also add a TX UART to the mix to allow the Prop-SX to send commands to accessory boards).
Jon McPhalen
EFX-TEK Hollywood Office

JonnyMac

June 11, 2008, 12:46:34 PM #2 Last Edit: June 11, 2008, 01:04:08 PM by JonnyMac
Since we're heading to MHC tomorrow I'm in full demo mode.  Here's what I think the most likely combination will be: 8 servos (P0..P7), 4 pwm outputs (P8..P11), and TTL serial on P15.  P14 is setup as the trigger, and P12 and P13 are available for other use.

This template includes little demos for each major sub-section -- just rip that stuff out and replace it with your own code.  I tested the serial at 2400 and 38.4K; both work fine, but I wouldn't select anything but 38.4K unless you're going to talk to a non-EFX-TEK device that doesn't use this baud rate.

' =========================================================================
'
'   File...... Servo8_PWM4_38k4.SXB
'   Purpose... Template program for 8 servo pins, 4 pwm pins with TX
'   Author.... Jon Williams, EFX-TEK
'              Copyright (c) 2008 EFX-TEK
'              Some Rights Reserved
'              -- see http://creativecommons.org/licenses/by/3.0/
'   E-mail.... jwilliams@efx-tek.com
'   Started...
'   Updated... 11 JUN 2008
'
' =========================================================================


' -------------------------------------------------------------------------
' Program Description
' -------------------------------------------------------------------------
'


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


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

DEVICE          SX28, OSCHS2, TURBO, STACKX, OPTIONX, BOR42
FREQ            50_000_000
ID              "Svo8Pwm4"


' -------------------------------------------------------------------------
' I/O Pins
' -------------------------------------------------------------------------

PwmCtrl         PIN     RC                      ' PWM & control pins
TX             PIN     RC.7 OUTPUT             ' no SETUP; no ULN
Trigger        PIN     RC.6 INPUT              ' SETUP = DN
UnusedP13      PIN     RC.5 INPUT
UnusedP12      PIN     RC.4 INPUT
Pwm3           PIN     RC.3 OUTPUT             ' use P11/OUT11
Pwm2           PIN     RC.2 OUTPUT             ' use P10/OUT10
Pwm1           PIN     RC.1 OUTPUT             ' use P9/OUT9
Pwm0           PIN     RC.0 OUTPUT             ' use P8/OUT8

ServoCtrl       PIN     RB                      ' servo control pins
Servo7         PIN     RB.7 OUTPUT             ' use P7
Servo6         PIN     RB.6 OUTPUT             ' use P6
Servo5         PIN     RB.5 OUTPUT             ' use P5
Servo4         PIN     RB.4 OUTPUT             ' use P4
Servo3         PIN     RB.3 OUTPUT             ' use P3
Servo2         PIN     RB.2 OUTPUT             ' use P2
Servo1         PIN     RB.1 OUTPUT             ' use P1
Servo0         PIN     RB.0 OUTPUT             ' use P0

TxD             PIN     RA.3 OUTPUT             ' DB-9 to PC
RxD             PIN     RA.2 INPUT  PULLUP      ' DB-9 from PC
SCL             PIN     RA.1 INPUT              ' EE clock line (I2C)
SDA             PIN     RA.0 INPUT              ' EE data line (I2C)

' Note: TxD, RxD, 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     55                      ' to prevent servo burn-up
HI_LIMIT        CON     245


' Bit dividers for 3.333 uS interrupt
' -- actual rate should be 3.255 (error is acceptable)
' -- 3.333 used for servo timing

Baud2400        CON     128
Baud4800        CON     64
Baud9600        CON     32
Buad19K2        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

idx             VAR     Byte

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

txSerial        VAR     Byte (16)               ' tx serial data
txBuf          VAR     txSerial(0)             ' eight-byte buffer
txCount        VAR     txSerial( 8 )           ' tx bit count
txDivide       VAR     txSerial(9)             ' bit divisor timer
txLo           VAR     txSerial(10)            ' holds start bit
txHi           VAR     txSerial(11)            ' tx output reg
txHead         VAR     txSerial(12)            ' buffer head (write to)
txTail         VAR     txSerial(13)            ' buffer tail (read from)
txBufCnt       VAR     txSerial(14)            ' # bytes in buffer

dimmers         VAR     Byte( 8 )
level          VAR     dimmers(0)
level0         VAR     dimmers(0)
level1         VAR     dimmers(1)
level2         VAR     dimmers(2)
level3         VAR     dimmers(3)
acc0           VAR     dimmers(4)
acc1           VAR     dimmers(5)
acc2           VAR     dimmers(6)
acc3           VAR     dimmers(7)

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                  ' run every 3.333 uS
' =========================================================================

Mark_ISR:
  ASM
    SETB  isrFlag                               ' (1)
  ENDASM


' -------
' TX UART
' -------
'
Transmit:
  ASM
    BANK  txSerial                              ' (1)
    TEST  txCount                               ' (1)   transmitting now?
    JZ    TX_Buffer                             ' (2/4) if txCount = 0, no
    DEC   txDivide                              ' (1)   update bit timer
    JNZ   TX_Done                               ' (2/4) time for new bit?
    MOV   txDivide, #Baud1x0                    ' (2)   yes, reload timer
    STC                                         ' (1)   set for stop bit
    RR    txHi                                  ' (1)   rotate TX buf
    RR    txLo                                  ' (1)
    DEC   txCount                               ' (1)   update the bit count
    MOVB  TX, txLo.6                            ' (4)   output the bit
    JMP   TX_Done                               ' (3)

TX_Buffer:
    TEST  txBufCnt                              ' (1)   anything in buffer?
    JZ    TX_Done                               ' (2/4) exit if empty
    MOV   W, #txBuf                             ' (2)   point to buffer tail
    ADD   W, txTail                             ' (1)
    MOV   FSR, W                                ' (1)
    MOV   txHi, IND                             ' (2)   move byte to TX reg
    CLR   txLo                                  ' (1)   clear for start bit
    MOV   txCount, #10                          ' (2)   start + 8 + 1 stop
    INC   txTail                                ' (1)   update tail pointer
    CLRB  txTail.3                              ' (1)   keep 0..7
    DEC   txBufCnt                              ' (1)   update buffer count

TX_Done:
    BANK  $00                                   ' (1)
  ENDASM


' -----------
' PWM Dimmers
' -----------
'
Process_Dimmers:
  ASM
    BANK  dimmers                               ' (1)
    ADD   acc0, level0                          ' (2)   update accumulator
    MOVB  Pwm0, C                               ' (4)   output on if rollover
    ADD   acc1, level1                          ' (2)
    MOVB  Pwm1, C                               ' (4)
    ADD   acc2, level2                          ' (2)
    MOVB  Pwm2, C                               ' (4)
    ADD   acc3, level3                          ' (2)
    MOVB  Pwm3, C                               ' (4)
    BANK  $00                                   ' (1)
  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 = 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)   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
    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:
    BANK  $00                                   ' (1)
  ENDASM

  RETURNINT


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


' -------------------------------------------------------------------------
' Subroutine / Function Declarations
' -------------------------------------------------------------------------

DELAY_MS        SUB     1, 2                    ' replaces PAUSE

TX_BYTE         SUB     1                       ' transmit a byte
TX_STR          SUB     2                       ' transmit a string


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

Start:
  TxD = 1                                       ' set TX lines to idle
  TX = 1

  ' center servos
  PUT pos, 150, 150, 150, 150, 150, 150, 150, 150


Main:
  FOR idx = 0 TO 255
    PUT level, idx, idx, idx, idx
    DELAY_MS 2
  NEXT

  FOR idx = 255 TO 0 STEP -1
    PUT level, idx, idx, idx, idx
    DELAY_MS 2
  NEXT

  FOR idx = 100 TO 200
    pos(0) = idx
    DELAY_MS 20
  NEXT

  TX_STR  "!DC16"
  TX_BYTE %00
  TX_BYTE "P"
  TX_BYTE 1
  TX_BYTE IsOn

  DELAY_MS 1000

  TX_STR  "!DC16"
  TX_BYTE %00
  TX_BYTE "X"

  GOTO Main


' -------------------------------------------------------------------------
' Subroutine / Function Code
' -------------------------------------------------------------------------

' Use: DELAY_MS duration
' -- duration in milliseconds
' -- replaces PAUSE

SUB DELAY_MS
  IF __paramcnt = 1 THEN                        ' capture duration
    tmpW1 = __param1
  ELSE
    tmpW1 = __wparam12
  ENDIF

  DO WHILE tmpW1 > 0                            ' run delay timer
    tmpW2 = 300                                 ' load 1ms timer
    DO WHILE tmpW2 > 0                          ' run 1ms timer
      \ CLRB isrFlag                            ' clear ISR entry flag
      \ JNB  isrFlag, @$                        ' wait for next
      DEC tmpW2                                 ' update 1ms timer
    LOOP
    DEC tmpW1                                   ' update delay timer
  LOOP
  ENDSUB

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

' Use: TX_BYTE aByte
' -- moves "aByte" to 8-byte circular buffer (when space is available)
' -- will wait if buffer is presently full
' -- txBufCnt holds byte count of transmit buffer (0 to 8)

SUB TX_BYTE
  ASM
    BANK  txSerial                              ' point to tx vars
    JB    txBufCnt.3, @$                        ' prevent buffer overrun
    MOV   W, #txBuf                             ' point to buffer head
    ADD   W, txHead
    MOV   FSR, W
    MOV   IND, __param1                         ' move byte to tx buf
    INC   txHead                                ' update head pointer
    CLRB  txHead.3                              ' keep 0..7
    INC   txBufCnt                              ' update buffer count
    BANK  $00
  ENDASM
  ENDSUB

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

' Use: TX_STR [String | Label]
' -- pass embedded string or DATA label

SUB TX_STR
  tmpW1 = __wparam12                            ' capture address

  DO
    READINC tmpW1, tmpB1                        ' read a character
    IF tmpB1 = 0 THEN EXIT                      ' if 0, string complete
    TX_BYTE tmpB1                               ' send the byte
  LOOP
  ENDSUB


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


' -------------------------------------------------------------------------
' User Data
' -------------------------------------------------------------------------
Jon McPhalen
EFX-TEK Hollywood Office

davisgraveyard

Here is how I'd like to setup the show for the Hearse driver.

12V green LEDs fades up in a coffin in the hearse.
small low volume fog machine starts to fill the coffin
Coffin lid slowly raises up a crack and down to let the fog roll out.  (12V DC motor?)

2 12V LED's fade up on the hearse driver head.
4 Servos come alive and head centers itself
2 12V LED eyes fade up.
Audio speach starts playing through the uMP3
4 servos move to a preset show
LED Eyes fade down
Servos go limp
LED Spotlights fade down
Coffin lid stops moving
Fog is turned off
Coffin LED fades down

wait 2-3 minutes and repeat show.

If it is easier I might put the whole coffin on a Prop1 and just trigger it with the PropSX?  Then the PROP-SX just handles the 4 servos, the 2 LED EYES fading and the on/off trigger for the coffin sequence?






JonnyMac

Questions:
- Are the 12 LEDs for the head on one channel?
- Are the LED eyes on the same channel?
- Do you want and RX UART for the uMP3 or are you okay with just talking to it?
- How is the coffin lid moved?
- How is the fog controlled?
Jon McPhalen
EFX-TEK Hollywood Office

davisgraveyard

 Each Eye in the head has 1 12V LED.  They would be on the same channel.   Fading in unison.
The two 12V LED spotlights would also be on a single channel fading together.
The uMP3 could just be "fire and forget"  as long as there aren't any sync issues with the servos. 
The coffin including lid is made out of lightweight styrofoam.  I was thinking of using a slow RPM DC motor to lift and close the lid on a circular cam.
Not sure about the fog control?  I was thinking of hacking the remote trigger on a cheap fogger?  I'd like to stay away from 110V switching.  I'd rather the fogger were always powered on and just activated as needed.

Does that help?


davisgraveyard

Just wondering where we were going with this?  You'd asked some questions and I tried to give the details of what I was thinking.   Were you going to show an example or does the one above spell it all out?

Jeff

JonnyMac

Sorry, I've been on the road all week, teaching classes and giving presentations at UAT (University of Advancing Technology) in Tempe, AZ -- have go to recruit haunt engineers early!  I'll give everything a look this weekend after I've had a little time to return to human.
Jon McPhalen
EFX-TEK Hollywood Office

JonnyMac

Jeff,

Can you assign the outputs for me -- I'm still a little fuzzy about your exact requirements.
Jon McPhalen
EFX-TEK Hollywood Office

davisgraveyard

Lets see....

0-3  Head motion servos
4  - uMP3 Player switch
5  -  LED Eyes
6  -  LED Spotlights
7  -  LED in Coffin
8  - Coffin LID Motor (12VDC) switch 
9  -  Fog Switch (probably a relay to a hacked remote timer)

Sequence would be something like this....

Fog switch on for 2 seconds the pause for 30 seconds
Coffin LED fades on
Coffin Motor switch ON
wait for 10 seconds
fade up LED spotlights
fade up LED eyes
start MP3 file
start playing back servo movements
(when MP3 file is done and/or movements are finished)
fade LED eyes off
fade LED spotlights  off
stop fog
stop coffin motor
fade coffin LED
wait 120 seconds and repeat







JonnyMac

Here's a new framework to build your program with.  The servo and PWM outputs are interleaved on port RB (P0..P7) so that required some minor code changes, everything else is simple.  I'm keeping it generic so that you can use it as a starting port for this and other projects.  Rename the outputs to make sense to your program.

Note that the digital outputs on P8/OUT8 through P11/OUT11 are activated/deactivated like this:

Ctrl0 = IsOn
DELAY_MS 250
Ctrl0 = IsOff


There is no need to use HIGH and LOW as the pins are already outputs, and you cannot use PAUSE because the interrupt will affect the timing; the DELAY_MS subroutine replaces PAUSE.

I also added a receive UART for projects that need it -- you can use this with the uMP3 player for full control and knowing that it's ready.  Note that I put the UART pins on P14 (RX) and 15 (TX); I did this to make removing the ULN pins easier (remove pins 1 and 2 of the ULN2803, pin 1 of a ULN2003 if that's what you're using).

Have fun -- take your time.  This is now getting into moderately-sophisticated embedded programming and you'll want to work through in a disciplined manner.  I know the framework works, so going step-by-step will help you find bugs should you introduce them.

' =========================================================================
'
'   File...... Servo4_PWM4_38k4.SXB
'   Purpose... Template program for 4 servo pins, 4 pwm pins with TX/RX
'   Author.... Jon Williams, EFX-TEK
'              Copyright (c) 2008 EFX-TEK
'              Some Rights Reserved
'              -- see http://creativecommons.org/licenses/by/3.0/
'   E-mail.... jwilliams@efx-tek.com
'   Started...
'   Updated... 24 JUN 2008
'
' =========================================================================


' -------------------------------------------------------------------------
' Program Description
' -------------------------------------------------------------------------
'


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


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

DEVICE          SX28, OSCXT2, TURBO, STACKX, OPTIONX, BOR42
FREQ            50_000_000
ID              "Svo4Pwm4"


' -------------------------------------------------------------------------
' I/O Pins
' -------------------------------------------------------------------------

TX              PIN     RC.7 OUTPUT             ' SETUP = UP/OUT, no ULN
RX              PIN     RC.6 INPUT              ' SETUP = UP, no ULN

Trigger1        PIN     RC.5 INPUT              ' SETUP = DN
Trigger0        PIN     RC.4 INPUT              ' SETUP = DN

Ctrl3           PIN     RC.3 OUTPUT             ' use P11/OUT11
Ctrl2           PIN     RC.2 OUTPUT             ' use P10/OUT10
Ctrl1           PIN     RC.1 OUTPUT             ' use P9/OUT9
Ctrl0           PIN     RC.0 OUTPUT             ' use P8/OUT8

Pwm3            PIN     RB.7 OUTPUT             ' use P7/OUT7
Pwm2            PIN     RB.6 OUTPUT             ' use P6/OUT6
Pwm1            PIN     RB.5 OUTPUT             ' use P5/OUT5
Pwm0            PIN     RB.4 OUTPUT             ' use P4/OUT4

ServoPort       PIN     RB                      ' low nib only
Servo3         PIN     RB.3 OUTPUT             ' use P3
Servo2         PIN     RB.2 OUTPUT             ' use P2
Servo1         PIN     RB.1 OUTPUT             ' use P1
Servo0         PIN     RB.0 OUTPUT             ' use P0

TxD             PIN     RA.3 OUTPUT             ' DB-9 to PC
RxD             PIN     RA.2 INPUT  PULLUP      ' DB-9 from PC
SCL             PIN     RA.1 INPUT              ' EE clock line (I2C)
SDA             PIN     RA.0 INPUT              ' EE data line (I2C)


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

IsOn            CON     1
IsOff           CON     0

Yes             CON     1
No              CON     0


' Bit dividers for 3.333 uS interrupt
' -- actual rate should be 3.255 (error is acceptable)
' -- 3.333 used for clean servo timing

Baud2400        CON     128
Baud4800        CON     64
Baud9600        CON     32
Buad19K2        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                 ' byte(s) in rx buffer

idx             VAR     Byte

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

txSerial        VAR     Byte (16)               ' tx serial data
txBuf          VAR     txSerial(0)             ' eight-byte buffer
txCount        VAR     txSerial( 8 )           ' tx bit count
txDivide       VAR     txSerial(9)             ' bit divisor timer
txLo           VAR     txSerial(10)            ' holds start bit
txHi           VAR     txSerial(11)            ' tx output reg
txHead         VAR     txSerial(12)            ' buffer head (write to)
txTail         VAR     txSerial(13)            ' buffer tail (read from)
txBufCnt       VAR     txSerial(14)            ' # bytes in buffer

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)
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

dimmers         VAR     Byte( 8 )
level          VAR     dimmers(0)
level0         VAR     dimmers(0)
level1         VAR     dimmers(1)
level2         VAR     dimmers(2)
level3         VAR     dimmers(3)
acc0           VAR     dimmers(4)
acc1           VAR     dimmers(5)
acc2           VAR     dimmers(6)
acc3           VAR     dimmers(7)


' =========================================================================
  INTERRUPT NOPRESERVE 300_000                  ' run every 3.333 uS
' =========================================================================

Mark_ISR:
  ASM
    SETB  isrFlag                               ' (1)
  ENDASM


' -------
' TX UART
' -------
'
Transmit:
  ASM
    BANK  txSerial                              ' (1)
    TEST  txCount                               ' (1)   transmitting now?
    JZ    TX_Buffer                             ' (2/4) if txCount = 0, no
    DEC   txDivide                              ' (1)   update bit timer
    JNZ   TX_Done                               ' (2/4) time for new bit?
    MOV   txDivide, #Baud1x0                    ' (2)   yes, reload timer
    STC                                         ' (1)   set for stop bit
    RR    txHi                                  ' (1)   rotate TX buf
    RR    txLo                                  ' (1)
    DEC   txCount                               ' (1)   update the bit count
    MOVB  TX, txLo.6                            ' (4)   output the bit
    JMP   TX_Done                               ' (3)

TX_Buffer:
    TEST  txBufCnt                              ' (1)   anything in buffer?
    JZ    TX_Done                               ' (2/4) exit if empty
    MOV   W, #txBuf                             ' (2)   point to buffer tail
    ADD   W, txTail                             ' (1)
    MOV   FSR, W                                ' (1)
    MOV   txHi, IND                             ' (2)   move byte to TX reg
    CLR   txLo                                  ' (1)   clear for start bit
    MOV   txCount, #10                          ' (2)   start + 8 + 1 stop
    INC   txTail                                ' (1)   update tail pointer
    CLRB  txTail.3                              ' (1)   keep 0..7
    DEC   txBufCnt                              ' (1)   update buffer count

TX_Done:
    BANK  $00                                   ' (1)
  ENDASM


' -------
' RX UART
' -------
'
Receive:
  ASM
    BANK  rxSerial                              ' (1)
    JB    rxBufCnt.4, 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
    BANK  flags                                 ' (1)
    SETB  rxReady                               ' (1)   set ready flag

RX_Done:
    BANK  $00                                   ' (1)
  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 = 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)   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
    CLRB  svoidx.2                              ' (1)   keep 0 - 3
    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)
    CLRB  svoPin.4                              ' (1)

Refesh_Servo_Outs:
    AND   ServoPort, #$F0                       ' (2)   clear old pin
    OR    ServoPort, svoPin                     ' (2)   active new output

Servo_Done:
    BANK  $00                                   ' (1)
  ENDASM


' -----------
' PWM Dimmers
' -----------
'
Process_Dimmers:
  ASM
    BANK  dimmers                               ' (1)
    ADD   acc0, level0                          ' (2)   update accumulator
    MOVB  Pwm0, C                               ' (4)   output on if rollover
    ADD   acc1, level1                          ' (2)
    MOVB  Pwm1, C                               ' (4)
    ADD   acc2, level2                          ' (2)
    MOVB  Pwm2, C                               ' (4)
    ADD   acc3, level3                          ' (2)
    MOVB  Pwm3, C                               ' (4)
    BANK  $00                                   ' (1)
  ENDASM

  RETURNINT


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


' -------------------------------------------------------------------------
' Subroutine / Function Declarations
' -------------------------------------------------------------------------

DELAY_MS        SUB     1, 2                    ' replaces PAUSE

TX_BYTE         SUB     1                       ' transmit a byte
TX_STR          SUB     2                       ' transmit a string

RX_BYTE         FUNC    1, 0                    ' receive a byte (buffered)


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

Start:
  TxD = 1                                       ' set TX lines to idle
  TX = 1

  ' center servos
  PUT pos, 150, 150, 150, 150


Main:

  GOTO Main


' -------------------------------------------------------------------------
' Subroutine / Function Code
' -------------------------------------------------------------------------

' Use: DELAY_MS duration
' -- duration in milliseconds
' -- replaces PAUSE

SUB DELAY_MS
  IF __paramcnt = 1 THEN                        ' capture duration
    tmpW1 = __param1
  ELSE
    tmpW1 = __wparam12
  ENDIF

  DO WHILE tmpW1 > 0                            ' run delay timer
    tmpW2 = 300                                 ' load 1ms timer
    DO WHILE tmpW2 > 0                          ' run 1ms timer
      \ CLRB isrFlag                            ' clear ISR entry flag
      \ JNB  isrFlag, @$                        ' wait for next
      DEC tmpW2                                 ' update 1ms timer
    LOOP
    DEC tmpW1                                   ' update delay timer
  LOOP
  ENDSUB

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

' Use: TX_BYTE aByte
' -- moves "aByte" to 8-byte circular buffer (when space is available)
' -- will wait if buffer is presently full
' -- txBufCnt holds byte count of transmit buffer (0 to Cool

SUB TX_BYTE
  ASM
    BANK  txSerial                              ' point to tx vars
    JB    txBufCnt.3, @$                        ' prevent buffer overrun
    MOV   W, #txBuf                             ' point to buffer head
    ADD   W, txHead
    MOV   FSR, W
    MOV   IND, __param1                         ' move byte to tx buf
    INC   txHead                                ' update head pointer
    CLRB  txHead.3                              ' keep 0..7
    INC   txBufCnt                              ' update buffer count
    BANK  $00
  ENDASM
  ENDSUB

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

' Use: TX_STR [String | Label]
' -- pass embedded string or DATA label

SUB TX_STR
  tmpW1 = __wparam12                            ' capture address

  DO
    READINC tmpW1, tmpB1                        ' read a character
    IF tmpB1 = 0 THEN EXIT                      ' if 0, string complete
    TX_BYTE tmpB1                               ' send the byte
  LOOP
  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
    BANK  rxSerial
    TEST  rxBufCnt                              ' check buffer count
    JZ    @RX_BYTE                              ' wait if empty
    MOV   W, #rxBuf                             ' point to tail
    ADD   W, 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
    TEST  rxBufCnt                              ' check the count
    JNZ    @$+5                                 ' exit if not zero
    BANK  flags
    CLRB  rxReady                               ' else clear ready flag
    BANK  $00
  ENDASM
  ENDFUNC

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


' -------------------------------------------------------------------------
' User Data
' -------------------------------------------------------------------------

Jon McPhalen
EFX-TEK Hollywood Office