May 17, 2024, 03:59:46 AM

News:

You can now use Vixen to program your Prop-1 and Prop-2 controllers!  Get started quickly and easily, without having to learn PBASIC.  Details in the Library forum.


DMX Fader Control

Started by JonnyMac, January 14, 2008, 04:28:00 PM

Previous topic - Next topic

JonnyMac

January 14, 2008, 04:28:00 PM Last Edit: January 14, 2008, 04:32:51 PM by JonnyMac
A friend who asked me to help him with DMX relay control asked about PWMing some outputs for a different project.  As this blends well with my book, I gave it a try and got it to work.  This version is setup for three PWM channels, ideally to control an high-power RGB LED or lamp(s). 

This code is a little advanced; the background has a timing control bit, a virtual UART,and the fader controls; the foreground runs a state-machine that advance the program through its various elements.

' =========================================================================
'
'   File...... DMX512_RGB_Fader.SXB
'   Purpose... DMX-512 interface for controlling 3 lamp outputs (RGB)
'   Author.... Jon Williams
'   E-mail.... jwilliams@efx-tek.com
'   Started...
'   Updated... 13 JAN 2008
'
' =========================================================================


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


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

DEVICE          SX28, OSCHS2, TURBO, STACKX, OPTIONX, BOR42
FREQ            50_000_000
ID              "DMX-RGB"


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

RX              PIN     RC.7 INPUT              ' from DMX interface

LampB           PIN     RC.6 OUTPUT             ' blue output
LampG           PIN     RC.5 OUTPUT             ' green output
LampR           PIN     RC.4 OUTPUT             ' red output

UnusedRC3       PIN     RC.3 INPUT PULLUP
UnusedRC2       PIN     RC.2 INPUT PULLUP
UnusedRC1       PIN     RC.1 INPUT PULLUP

MyAddr8         PIN     RC.0 INPUT              ' address 8
MyAddr          PIN     RB   INPUT              ' address 0..7

UnusedRA        PIN     RA   INPUT PULLUP


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

Yes             CON     1
No              CON     0


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

flags           VAR     Byte                    ' (keep global)
isrFlag        VAR     flags.0
rxReady        VAR     flags.1                 ' has byte(s) in buffer

state           VAR     Byte                    ' program state

dmxStart        VAR     Word                    ' address or red channel
breakTmr        VAR     Byte                    ' to measure DMX break
channel         VAR     Word                    ' current channel
dmxByte         VAR     Byte                    ' value we want

rxCount         VAR     Byte                    ' rx bit count
rxDivide        VAR     Byte                    ' bit divisor timer
rxByte          VAR     Byte                    ' recevied byte

level1          VAR     Byte                    ' lamp levels
level2          VAR     Byte
level3          VAR     Byte

acc1            VAR     Byte                    ' pwm accumulators
acc2            VAR     Byte
acc3            VAR     Byte


' -------------------------------------------------------------------------
  INTERRUPT NOCODE 750_000                      ' 1.333 us
' -------------------------------------------------------------------------

' --------
' Mark ISR
' --------
'
Marker:
  ASM
    BANK  0
    SETB  isrFlag
  ENDASM


' -------
' RX UART
' -------
'
Receive:
  ASM
    JB    rxReady, RX_Done                      ' skip if byte waiting
    MOVB  C, RX                                 ' sample serial input
    TEST  rxCount                               ' receiving now?
    JNZ   RX_Bit                                ' yes, get next bit
    MOV   W, #9                                 ' no, prep for next byte
    SC
    MOV   rxCount, W                            ' if start, load  bit count
    MOV   rxDivide, #5                          ' prep for ~1.6 bit periods

RX_Bit:
    DJNZ  rxDivide, RX_Done                     ' complete bit cycle?
    MOV   rxDivide, #3                          ' yes, reload bit timer
    DEC   rxCount                               ' update bit count
    SZ
    RR    rxByte                                ' position for next bit
    JNZ   RX_Done
    SETB  rxReady                               ' alert foreground

RX_Done:
  ENDASM


' -----------------------
' LED PWM Control
' -- *liberated* from PJV
' -----------------------
'
Pwm_Control:
  ASM
    ADD   acc1, level1
    SC
    CLRB  LampR
    SNC
    SETB  LampR
    ADD   acc2, level2
    SC
    CLRB  LampG
    SNC
    SETB  LampG
    ADD   acc3, level3
    SC
    CLRB  LampB
    SNC
    SETB  LampB
    BANK  0
  ENDASM

  RETURNINT


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


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


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

Start:
  ASM
    SETB  rxReady                               ' disable UART
    CLR   state
    CLRB  isrFlag
  ENDASM


Get_Address:
  dmxStart_LSB = MyAddr                         ' read address switches
  dmxStart_MSB = MyAddr8
  IF dmxStart = 0 THEN Start                    ' trap bad address
  IF dmxStart > 510 THEN Start


Main:
  ASM
    JNB   isrFlag, $                            ' wait for flag
    CLRB  isrFlag                               ' clear for next cycle
  ENDASM


Handle_State:
  ASM
    MOV   W, state
    JMP   PC+W
    JMP   Wait_4_Break
    JMP   Break_Release
    JMP   Skip_Start
    JMP   Find_Target
    JMP   Update_Red
    JMP   Update_Green
    JMP   Update_Blue
  ENDASM


Wait_4_Break:
  ASM                                           ' waiting for break
    INC   breakTmr                              ' update break timer
    SNB   RX                                    ' in break state?
    CLR   breakTmr                              ' no, reset timer
    CJB   breakTmr, #66, Main                   ' wait for break
    MOV   state, #1                             ' set next state
    JMP   Main
  ENDASM


Break_Release:
  ASM
    JNB   RX, Main                              ' abort if still in break
    CLR   rxCount                               ' reset UART
    CLRB  rxReady                               ' enable UART
    MOV   state, #2                             ' update state
    JMP   Main
  ENDASM


Skip_Start:                                     ' skip start code
  ASM
    JNB   rxReady, Main                         ' exit if not ready
    CLRB  rxReady                               ' re-enable UART
    MOV   channel_LSB, #1                       ' reset for channel search
    CLR   channel_MSB
    MOV   state, #3                             ' update state
    JMP   Main
  ENDASM


Find_Target:
  IF channel < dmxStart THEN                    ' still looking?
    IF rxReady = Yes THEN                       ' byte available?
      rxReady = No                              ' re-enable UART
      INC channel                               ' update channel pointer
    ENDIF
    GOTO Main
  ELSE
    state = 4                                   ' at target, update state
  ENDIF

  ' let it drop through


Update_Red:
  ASM
    JNB   rxReady, Main                         ' abort if no byte ready
    MOV   level1, rxByte                        ' get level
    CLRB  rxReady                               ' re-enable UART
    MOV   state, #5                             ' update state
    JMP   Main
  ENDASM


Update_Green:
  ASM
    JNB   rxReady, Main
    MOV   level2, rxByte
    CLRB  rxReady
    MOV   state, #6
    JMP   Main
  ENDASM


Update_Blue:
  ASM
    JNB   rxReady, Main
    MOV   level3, rxByte
    CLR   state                                 ' reset for next frame
    CLR   breakTmr
    JMP   Get_Address                           ' scan address change
  ENDASM
Jon McPhalen
EFX-TEK Hollywood Office

livinlowe

Two awesome programs in the same day! To quote the Matrix - "He's....a machine."

Thanks Jon
Shawn
Scaring someone with a prop you built -- priceless!

JonnyMac

To be fair, these programs have been brewing for a while, so I finally bought a DMX interface fro my PC so I could give it a try.  It doesn't hurt that I'm working on an SX programming book and have been getting hit with a lot of DMX-related requests.  I'm actually kind of stoked about the fader version, because I didn't think I could do it. 
Jon McPhalen
EFX-TEK Hollywood Office

JonnyMac

January 16, 2008, 01:06:40 AM #3 Last Edit: January 16, 2008, 01:11:19 AM by JonnyMac
A friend was happy that I managed to get four PWM channels and promptly asked me to double it to eight.  This is not as easy as it first seems because of the bandwidth requirements of the interrupt.  After pounding my head for a couple hours I came up with a solution -- and here it is (only one PWM channel is process in the interrupt, conserving bandwidth).  The only *issue* with this program is that I'm using just 7 pins on RC for the address, and in the DMX-512 world addresses have 9 bits.  My next step, then, is to add a 74LS165 shift register to expand inputs and fold that into the foreground state machine.

I know this code looks a little gnarly and, admittedly, it's not for everyone.  But for those that want to do hardcore things this is proof that you can -- if I yutz like me can do it, you can too!

' =========================================================================
'
'   File...... DMX512-BG_Fader_x8.SXB
'   Purpose... DMX-512 interface for controlling 8 PWM outputs
'   Author.... Jon Williams
'   E-mail.... jwilliams@efx-tek.com
'   Started...
'   Updated... 15 JAN 2008
'
' =========================================================================


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


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

DEVICE          SX28, OSCHS2, TURBO, STACKX, OPTIONX, BOR42
FREQ            50_000_000
ID              "DMX-8x"


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

RX              PIN     RC.7 INPUT              ' from DMX interface

MyAddr          PIN     RC   INPUT              ' use RC.0-RC.6

Lamps           PIN     RB   OUTPUT
Lamp8          PIN     RB.7
Lamp7          PIN     RB.6
Lamp6          PIN     RB.5
Lamp5          PIN     RB.4
Lamp4          PIN     RB.3
Lamp3          PIN     RB.2
Lamp2          PIN     RB.1
Lamp1          PIN     RB.0

UnusedRA        PIN     RA   INPUT PULLUP


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

Yes             CON     1
No              CON     0


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

flags           VAR     Byte                    ' (global)
isrFlag        VAR     flags.0
rxReady        VAR     flags.1                 ' serial byte ready

pwmState        VAR     Byte                    ' dimmer state (global)
state           VAR     Byte                    ' program state

dmxStart        VAR     Word                    ' address or red channel
breakTmr        VAR     Byte                    ' to measure DMX break
channel         VAR     Word                    ' current channel

rxCount         VAR     Byte                    ' rx bit count
rxDivide        VAR     Byte                    ' bit divisor timer
rxByte          VAR     Byte                    ' recevied byte

dimmer           VAR    Byte(16)
level1          VAR    dimmer(0)
level2          VAR    dimmer(1)
level3          VAR    dimmer(2)
level4          VAR    dimmer(3)
level5          VAR    dimmer(4)
level6          VAR    dimmer(5)
level7          VAR    dimmer(6)
level8          VAR    dimmer(7)
acc1            VAR    dimmer(8)
acc2            VAR    dimmer(9)
acc3            VAR    dimmer(10)
acc4            VAR    dimmer(11)
acc5            VAR    dimmer(12)
acc6            VAR    dimmer(13)
acc7            VAR    dimmer(14)
acc8            VAR    dimmer(15)


' -------------------------------------------------------------------------
  INTERRUPT NOCODE 750_000                      ' 1.333 us
' -------------------------------------------------------------------------

' --------
' Mark ISR
' --------
'
Marker:
  ASM
    BANK  0
    SETB  isrFlag                               ' mark for foreground
  ENDASM


' -------
' RX UART
' -------
'
Receive:
  ASM
    JB    rxReady, RX_Done                      ' skip if byte waiting
    MOVB  C, RX                                 ' sample serial input
    TEST  rxCount                               ' receiving now?
    JNZ   RX_Bit                                ' yes, get next bit
    MOV   W, #9                                 ' no, prep for next byte
    SC
    MOV   rxCount, W                            ' if start, load  bit count
    MOV   rxDivide, #5                          ' prep for ~1.6 bit periods

RX_Bit:
    DJNZ  rxDivide, RX_Done                     ' complete bit cycle?
    MOV   rxDivide, #3                          ' yes, reload bit timer
    DEC   rxCount                               ' update bit count
    SZ
    RR    rxByte                                ' position for next bit
    JNZ   RX_Done
    SETB  rxReady                               ' alert foreground

RX_Done:
  ENDASM


' -----------
' PWM Control
' -----------
'
Pwm_Scheduler:
  ASM
    MOV   W, pwmState                           ' point to current channel
    JMP   PC+W                                  ' jump to it
    JMP   Pwm_L1
    JMP   Pwm_L2
    JMP   Pwm_L3
    JMP   Pwm_L4
    JMP   Pwm_L5
    JMP   Pwm_L6
    JMP   Pwm_L7
    JMP   Pwm_L8

Pwm_L1:
    BANK  dimmer
    ADD   acc1, level1                          ' update accumulator
    MOVB  Lamp1, C                              ' output on if rollover
    JMP   Pwm_Done

Pwm_L2:
    BANK  dimmer
    ADD   acc2, level2
    MOVB  Lamp2, C
    JMP   Pwm_Done

Pwm_L3:
    BANK  dimmer
    ADD   acc3, level3
    MOVB  Lamp3, C
    JMP   Pwm_Done

Pwm_L4:
    BANK  dimmer
    ADD   acc4, level4
    MOVB  Lamp4, C
    JMP   Pwm_Done

Pwm_L5:
    BANK  dimmer
    ADD   acc5, level5
    MOVB  Lamp5, C
    JMP   Pwm_Done

Pwm_L6:
    BANK  dimmer
    ADD   acc6, level6
    MOVB  Lamp6, C
    JMP   Pwm_Done

Pwm_L7:
    BANK  dimmer
    ADD   acc7, level7
    MOVB  Lamp7, C
    JMP   Pwm_Done

Pwm_L8:
    BANK  dimmer
    ADD   acc8, level8
    MOVB  Lamp8, C

Pwm_Done:
    BANK  0
    INC   pwmState                              ' point to next channel
    AND   pwmState, #%0000_0111                 ' keep 0-7
  ENDASM

  RETURNINT


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


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


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

Start:
  ASM
    SETB  rxReady                               ' disable UART
    CLR   state
    CLRB  isrFlag
  ENDASM


Get_Address:
  dmxStart = MyAddr & %0111_1111                ' mask out RX line
  dmxStart = dmxStart << 2                      ' x4
  IF dmxStart = 0 THEN Start                    ' trap bad address
  IF dmxStart > 505 THEN Start


Main:
  ASM
    JNB   isrFlag, $                            ' wait for flag
    CLRB  isrFlag                               ' clear for next cycle
  ENDASM


Handle_State:
  ASM
    MOV   W, state
    JMP   PC+W
    JMP   Wait_4_Break
    JMP   Break_Release
    JMP   Skip_Start
    JMP   Find_Target
    JMP   Update_Ch1
    JMP   Update_Ch2
    JMP   Update_Ch3
    JMP   Update_Ch4
    JMP   Update_Ch5
    JMP   Update_Ch6
    JMP   Update_Ch7
    JMP   Update_Ch8
  ENDASM


Wait_4_Break:
  ASM                                           ' waiting for break
    INC   breakTmr                              ' update break timer
    SNB   RX                                    ' in break state?
    CLR   breakTmr                              ' no, reset timer
    CJB   breakTmr, #66, Main                   ' wait for break
    INC   state
    JMP   Main
  ENDASM


Break_Release:
  ASM
    JNB   RX, Main                              ' abort if still in break
    CLR   rxCount                               ' reset UART
    CLRB  rxReady                               ' enable UART
    INC   state
    JMP   Main
  ENDASM


Skip_Start:                                     ' skip start code
  ASM
    JNB   rxReady, Main                         ' exit if not ready
    CLRB  rxReady                               ' re-enable UART
    MOV   channel_LSB, #1                       ' reset for channel search
    CLR   channel_MSB
    INC   state
    JMP   Main
  ENDASM


Find_Target:
  IF channel < dmxStart THEN                    ' still looking?
    IF rxReady = Yes THEN                       ' byte available?
      rxReady = No                              ' re-enable UART
      INC channel                               ' update channel pointer
    ENDIF
    GOTO Main
  ELSE
  \ INC   state
  ENDIF

  ' let it drop through


Update_Ch1:
  ASM
    JNB   rxReady, Main                         ' abort if no byte ready
    MOV   __PARAM1, rxByte
    MOV   FSR, #level1
    MOV   IND, __PARAM1
    BANK  0
    CLRB  rxReady                               ' re-enable UART
    INC   state
    JMP   Main
  ENDASM


Update_Ch2:
  ASM
    JNB   rxReady, Main
    MOV   __PARAM1, rxByte
    MOV   FSR, #level2
    MOV   IND, __PARAM1
    BANK  0
    CLRB  rxReady
    INC   state
    JMP   Main
  ENDASM


Update_Ch3:
  ASM
    JNB   rxReady, Main
    MOV   __PARAM1, rxByte
    MOV   FSR, #level3
    MOV   IND, __PARAM1
    BANK  0
    CLRB  rxReady
    INC   state
    JMP   Main
  ENDASM


Update_Ch4:
  ASM
    JNB   rxReady, Main
    MOV   __PARAM1, rxByte
    MOV   FSR, #level4
    MOV   IND, __PARAM1
    BANK  0
    CLRB  rxReady
    INC   state
    JMP   Main
  ENDASM


Update_Ch5:
  ASM
    JNB   rxReady, Main
    MOV   __PARAM1, rxByte
    MOV   FSR, #level5
    MOV   IND, __PARAM1
    BANK  0
    CLRB  rxReady
    INC   state
    JMP   Main
  ENDASM


Update_Ch6:
  ASM
    JNB   rxReady, Main
    MOV   __PARAM1, rxByte
    MOV   FSR, #level6
    MOV   IND, __PARAM1
    BANK  0
    CLRB  rxReady
    INC   state
    JMP   Main
  ENDASM


Update_Ch7:
  ASM
    JNB   rxReady, Main
    MOV   __PARAM1, rxByte
    MOV   FSR, #level7
    MOV   IND, __PARAM1
    BANK  0
    CLRB  rxReady
    INC   state
    JMP   Main
  ENDASM


Update_Ch8:
  ASM
    JNB   rxReady, Main
    MOV   __PARAM1, rxByte
    MOV   FSR, #level8
    MOV   IND, __PARAM1
    BANK  0
    CLR   state                                 ' reset for next frame
    CLR   breakTmr
    JMP   Get_Address                           ' rescan device address
  ENDASM
Jon McPhalen
EFX-TEK Hollywood Office