November 13, 2024, 11:41:33 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.


Prop-SX sample master/slave programs

Started by tds234, March 22, 2009, 10:02:23 AM

Previous topic - Next topic

tds234

Hi Jon

Here's a programming request for the SX. It's a large project as you pointed out elsewhere so it may not be in scope for me this year's show but I sure would like to try...

I'm not sure how busy you are ( that is any busier than normal with all that you do) but I was wondering about some simple example code.

If I had two types of nodes:

One Prop-SX master sender sending 16 bytes periodically (up to 10 a second let's say) onto the network (and it'll be doing some other things but I can probably arrange that.)

Many Prop-sx slaves receiving each packet and pulling out a (different, configurable) specific bytes up to 10 times a second ( and then doing stuff from that which I can probably handle) i.e. they would wait in a loop for the next incoming packet, pick up the specific byte ( or multiple bytes) and then react based upon that.

Is this easy to prototype for the RS-485 adapter?

I think there is enough parts in the codes samples that you mention but this would seem to be a nice set to put in the library for people to use these to create simple master/slave networks for prop control.

What do you think? I would certainly understand if this is too much to tackle right away. If so, I'll order up two of these with SX's and start on it and post the basics if I need help or get it working...


The full program for the master will be reading these values from EEPROM and between watching the time signal from an MP3 player and keeping a real time clock it is going to send the slave commands out on the network at the proper time as each track plays. So a show will be a set or tracks and data files with the output packets to send to the slaves at the appropriate time (as well as some local command to turn on on off lights and such) This master will control multiple mp3 players as well. This part I figured I could do. Sort of vixen built onto an SX if you will.

Doug

JonnyMac

This project is going to be involved but will be fun, too.

If your system can tolerate 7-bit values you really simplify the process of synchronizing the slaves to the master.  Not long ago I did an experiment with an SX-based LED controller that could send its channel levels to slaves on an RS-485 network so that all would be in sync.  What I ultimately did is use MIDI-type messaging where BIT7 is indicates the first byte of a packet; this limits packet values to seven bits.  Of course, you can create a protocol in that will accept multiple bytes and put them back together. 

Here's the Main loop from the LED slave module (SX/B 2.00.16):

Main:
  cmd = RX_BYTE

Check_Cmd:
  IF cmd.7 = 0 THEN Main                        ' wait for start
  IF cmd = $FF then Get_Red

Check_Addr:
  myAddr = cmd & $7F                            ' strip start bit
  IF myAddr <> AddrLo THEN Main                 ' abort if no match

Get_Red:
  cmd = RX_BYTE
  IF cmd.7 = 1 THEN Check_Addr                  ' abort on new address byte
  level0 = cmd << 1

Get_Green:
  cmd = RX_BYTE
  IF cmd.7  = 1 THEN Check_Addr
  level1 = cmd << 1

Get_Blue:
  cmd = RX_BYTE
  IF cmd.7  = 1 THEN Check_Addr
  level2 = cmd << 1

Get_White:
  cmd = RX_BYTE
  IF cmd.7  = 1 THEN Check_Addr
  level3 = cmd << 1

  GOTO Main


Since my channel values are limited to 127 and the PWM controller in the project uses 0-255 I multiplied the incoming channel byte by two.  Yes, this cuts down on the step resolution but it's still less thatn 1% and doing so really made the protocol easy and robust.

On the master side it was just a matter of sending bytes; the first was usually $FF so that all slaves would respond.  When I wanted to "talk" to an individual slave I would add 128 to the slave address and send that as the first byte of the packet.

I am unbelivably busy leading up to TransWorld but when I'm back I should be able to help you with more code.
Jon McPhalen
EFX-TEK Hollywood Office

tds234

Thanks, that some good information. I figured you would be really busy, especially since I see that you are launching a new controller.
I've got 2 Sx's and 2 RS485 bits on the way from a large order I just placed...so I hope to have them communicating in the next few weeks.

Doug

tds234

Jon,

I've been working on the slave program adapting the code that I have seen in the various places you've pointed me to.
One item I see that I need to change is the RX_BYTE that I need will have to be non-blocking, i.e. I need my main loop still computing and doing other work.
Do you have a virtual peripheral type of code element that has a non-blocking receive?

Thanks
Doug

JonnyMac

April 13, 2009, 01:32:52 PM #4 Last Edit: April 13, 2009, 01:34:53 PM by JonnyMac
Here you go.  This template includes delay routines because anything time oriented will be adversely affected by the interrupt.

' =========================================================================
'
'   File...... RS-485_Template_BG.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... 13 APR 2009
'
' =========================================================================


' -------------------------------------------------------------------------
' Program Description
' -------------------------------------------------------------------------
'
' Note: Remove the ULN2803 that corresponds to P0-P7.


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


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

ID              "RS-485bg"

DEVICE          SX28, OSCHS2, BOR42
FREQ            50_000_000



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

UndefinedRC     PIN     RC   INPUT  PULLUP

Ctrl            PIN     RB
Aux            PIN     RB.7 INPUT  PULLUP      ' auxilliary input
RX             PIN     RB.6 INPUT              ' serial in
TX             PIN     RB.5 OUTPUT             ' serial out
TxEnable       PIN     RB.4 OUTPUT             ' tx control (0 = off)
Addr8          PIN     RB.3 INPUT              ' bit 8 of address
Clock          PIN     RB.2 OUTPUT             ' to 74x165.2 (CLK)
Din            PIN     RB.1 INPUT              ' to 74x165.9 (QH)
ShLoad         PIN     RB.0 OUTPUT             ' to 74x165.1 (S/LD)

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


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

' Dividers for ISR UART
' -- values set for 5.208us

Baud9600        CON     20
Baud19K2        CON     10
Baud38K4        CON     5

Baud1x0         CON     Baud38K4                ' 1 bit period (ISR counts)
Baud1x5         CON     Baud1x0 * 3 / 2         ' 1.5 bit periods

LF              CON     10                      ' line feed
CLS             CON     12                      ' form feed; use as CLS
CR              CON     13                      ' carriage return

Yes             CON     1
No              CON     0

IsOn            CON     1
IsOff           CON     0


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

flags           VAR     Byte
isrFlag        VAR     flags.0                 ' marks entry of ISR
rxReady        VAR     flags.1                 ' indicates rx byte ready

cmd             VAR     Byte
idx             VAR     Byte
addrSw          VAR     Word

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

rxSerial        VAR     Byte (16) BANK
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

txSerial        VAR     Byte (16) BANK
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

nStr            VAR     Byte (5)


' =========================================================================
  INTERRUPT NOCODE 192_000                      ' (3)   5.208us
' =========================================================================

Marker:
  \ SETB  isrFlag                               ' (1)


' -------
' RX UART
' -------
'
' UART code by C. Gracey, A. Williams, et al
' -- buffer 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


' -------
' TX UART
' -------
'
' UART code by C. Gracey, A. Williams, et al
' -- buffer mods by Jon Williams
'
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 char 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_Exit                               ' (2/4) exit if empty
    SETB  TxEnable                              ' (1)   enable transmitter
    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
    JMP   TX_Done                               ' (3)

TX_Exit:
    JNB   TxEnable, TX_Done                     ' (2/4) skip if enable clear
    TEST  txCount                               ' (1)   still transmitting?
    SNZ                                         ' (1/2) skip if yes
     CLRB TxEnable                              ' (1)   disable TX

TX_Done:
  ENDASM

  RETURNINT                                     ' (4)


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

DELAY_MS        SUB     2, 2,    Word           ' delay in milliseconds
DELAY_TIX       SUB     2, 2,    Word           ' delay in 5.208us units

FLUSH_RX        SUB     0                       ' clear RX buffer
RX_BYTE         FUNC    1, 0                    ' receive a byte

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

READ_ADDR_SW    FUNC    2, 0, 0, Word           ' read address switch
UCASE           FUNC    1, 1                    ' alpha to uppercase


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

Start:
  TX = 1                                        ' idle state
  Clock = 0                                     ' 75x165 clock 0-1-0
  ShLoad = 1                                    ' 75x165 to shift

  FLUSH_RX


Main:

  GOTO Main



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

' Use: DELAY_MS ms
' -- delay in ~milliseconds; uses ISR
' -- ideal ISR count is 192

SUB DELAY_MS
  '{$IFUSED DELAY_MS}
  msDuration    VAR     __WPARAM12
  msTix         VAR     __PARAM3

  DO WHILE msDuration > 0                       ' time left?
    msTix = 192                                 ' (re)load 1 ms timer
    DO WHILE msTix > 0
      \ CLRB isrFlag                            ' clear ISR flag
      \ JNB  isrFlag, @$                        ' wait for next flag
      DEC msTix                                 ' update 1 ms timer
    LOOP
    DEC msDuration                              ' update delay timer
  LOOP
  '{$ENDIF}
  ENDSUB

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

' Use: DELAY_TIX ticks
' -- delay in ISR "ticks" of 5.208 microseconds

SUB DELAY_TIX
  '{$IFUSED DELAY_MS}
  dlyTix        VAR     __WPARAM12

  DO WHILE dlyTix > 0                           ' ticks left?
    \ CLRB isrFlag                              ' clear ISR flag
    \ JNB  isrFlag, @$                          ' wait for next flag
    DEC dlyTix                                  ' update delay timer
  LOOP
  '{$ENDIF}
  ENDSUB

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

' Use: FLUSH_RX
' -- flushes pending bytes in rx buffer
' -- waits on incoming byte if in process

SUB FLUSH_RX
  '{$IFUSED 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
  '{$ENDIF}
  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
  '{$IFUSED RX_BYTE}
  ASM
    JNB   rxReady, @$                           ' wait if empty
    BANK  rxSerial
    MOV   FSR, #rxBuf                           ' point to rxBuf(rxTail)
    ADD   FSR, rxTail
    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
  '{$ENDIF}
  ENDFUNC

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

' Use: TX_STR [String | Label]
' -- transmits inline string or z-string at "Label"

SUB TX_STR
  '{$IFUSED TX_STR}
  sAddr         VAR     __WPARAM34              ' address of string
  sChar         VAR     __PARAM1                ' character to send

  sAddr = __WPARAM12                            ' get address for caller

  DO
    READINC sAddr, sChar                        ' get a character
    IF sChar = 0 THEN EXIT                      ' if 0 abort
    TX_BYTE sChar                               ' else send it
  LOOP
  '{$ENDIF}
  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
  '{$IFUSED TX_STR}
  ASM
    BANK  txSerial
    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
  '{$ENDIF}
  ENDSUB

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

' Use: READ_ADDR_SW
' -- reads nine-position DIP switch on RS-485 board

FUNC READ_ADDR_SW
  '{$IFUSED READ_ADDR_SW}
  myAddr        VAR     tmpW1

  ShLoad = 1                                    ' clear shift/load
  Clock = 0                                     ' device clocked 0-->1
  ShLoad = 0                                    ' blip shift/load pin
  DELAY_TIX 2
  ShLoad = 1                                    ' set to shift
  SHIFTIN DIn, Clock, MSBPRE, myAddr_LSB        ' get low bits of switch
  myAddr_MSB = Addr8                            ' get high bit
  RETURN myAddr
  '{$ENDIF}
  ENDFUNC

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

' Use: result = UCASE char
' -- converts "char" to uppercase if "a" to "z"

FUNC UCASE
  '{$IFUSED UCASE}
  ASM
    CJB   __PARAM1, #"a", @UC_Exit
    CJA   __PARAM1, #"z", @UC_Exit
    CLRB  __PARAM1.5
  ENDASM

UC_Exit:
  '{$ENDIF}
  ENDFUNC


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