; DMX512 to Analog Voltage Converter
; Copyright (C) 2001 by Kelly J. Kohls
; All rights reserved
; Version 1.12; December 31, 2001
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version 2
; of the License, or any later version.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
; Author may be reached at: n5tle@qsl.net
LIST P=16F876
RADIX DEC
INCLUDE__CONFIG (_HS_OSC & _WDT_OFF & _CP_OFF & _PWRTE_ON & _BODEN_ON & _CPD_OFF &
_DEBUG_OFF & _LVP_OFF & _WRT_ENABLE_ON)
Start_Vector EQU 0
Interrupt_Vector EQU 4
Ram_Base EQU H'20'
Common_Ram_Base EQU H'70'
; Define program constants
Osc_Freq EQU 20000000
Baud_Rate EQU 250000
Baud_Rate_Constant EQU (Osc_Freq/(16*Baud_Rate))-1
Number_Of_Channels EQU 16
Number_Of_DACS EQU 16
Range_Const_H EQU (512 - Number_Of_Channels + 1) >> 8
Range_Const_L EQU (512 - Number_Of_Channels + 1) & H'0FF'
Valid_Break EQU 0
Valid_SC EQU 1
Valid_Data EQU 2
; Define DAC8800 control ports
#DEFINE Clear_Bar PORTA,0
#DEFINE Clock PORTA,1
#DEFINE SDI PORTA,2
#DEFINE Load2_Bar PORTA,3 ; DAC channels 9-16
#DEFINE Load1_Bar PORTA,5 ; DAC channels 1-8
; Define BCD switch control/data ports
#DEFINE BCD1 PORTB,0
#DEFINE BCD2 PORTB,1
#DEFINE BCD4 PORTB,2
#DEFINE BCD8 PORTB,3
#DEFINE Ones_Digit PORTB,4
#DEFINE Tens_Digit PORTB,5
#DEFINE Hundreds_Digit PORTB,6
; Define data received LED control port
#DEFINE Data_Rec_LED PORTB,7
; Define relay control ports
#DEFINE Relays_9_16 PORTC,0
#DEFINE Relays_1_8 PORTC,1
; Define option jumper port
#DEFINE Option_Jumper PORTC,2
; Define debug LED control ports
#DEFINE HW_Overrun_LED PORTC,4
#DEFINE SW_Overrun_LED PORTC,5
; Set up RAM
; Define interrupt handler variables
CBLOCK Common_Ram_Base
Int_W
Int_Status
Int_PCLath
Int_FSR
Int_Dimmer_Count_L
Int_Dimmer_Count_H
Int_RX_Status
Int_RX_Data
Int_Dimmer_Index
Int_Temp_Diff_H
ENDC
CBLOCK Ram_Base
; Define program specific variables
DAC_Count
Loop1
Config_Data
Rx_Data
; Define DMX512 specific variables
DMX_Start_Addr_L
DMX_Start_Addr_H
DMX_Start_Code
; Define multiply routine local variables
Mult_Temp_L
Mult_Temp_H
; Define BCD switch variables
BCD_Digit_H
BCD_Digit_T
BCD_Digit_O
BCD_L_Byte
BCD_H_Byte
; Define Dimmer Value Array
Dimmer_Data : Number_Of_Channels
ENDC
; Start program code
ORG Start_Vector
MOVLW HIGH Start
MOVWF PCLATH
GOTO Start
; Interrupt handler
ORG Interrupt_Vector
MOVWF Int_W ; Save PIC state
SWAPF STATUS,W
CLRF STATUS
MOVWF Int_Status
MOVF PCLATH,W
MOVWF Int_PCLath
CLRF PCLATH
BCF STATUS,IRP
MOVF FSR,W
MOVWF Int_FSR
IH_Interrupt_Poll
IH_Check_UART_IE
BSF STATUS,RP0 ; Switch to bank 1
BTFSC PIE1,RCIE ; Is UART interrupt enabled ?
GOTO IH_UART_Bank_Zero ; Yes
BCF STATUS,RP0 ; Switch to bank 0
GOTO IH_Check_Timer1_IE ; Check Timer 1 IE
IH_UART_Bank_Zero
BCF STATUS,RP0 ; Switch to bank 0
IH_Check_UART_RX_IF
BTFSS PIR1,RCIF ; UART interrupt pending ?
GOTO IH_Check_Timer1_IE ; No, check Timer 1 IE
BCF Data_Rec_LED ; Turn on data rec indicator
BSF Relays_1_8 ; Turn relays 1-8 on
BSF Relays_9_16 ; Turn relays 9-16 on
BSF Int_RX_Status,Valid_Data
CLRF TMR1L ; Clear timer registers
CLRF TMR1H
BCF PIR1,TMR1IF ; Clear timer interrupt flag
BSF STATUS,RP0 ; Switch to bank 1
BSF PIE1,TMR1IE ; Allow timer interrupts
BCF STATUS,RP0 ; Switch to bank 0
MOVF RCREG,W
MOVWF Int_RX_Data
BTFSC RCSTA,OERR ; Has a HW overrun occurred ?
GOTO IH_RX_HW_Overrun ; Yes, turn on error LED
BTFSS RCSTA,FERR ; No, has a framing error occurred ?
GOTO IH_Check_Valid_Break ; No
BCF RCSTA,CREN ; Yes, reset UART
BSF RCSTA,CREN ; receive logic
BSF Int_RX_Status,Valid_Break
CLRF Int_Dimmer_Count_H
CLRF Int_Dimmer_Count_L
CLRF Int_Dimmer_Index
GOTO IH_Interrupt_Poll
IH_Check_Valid_Break
BTFSS Int_RX_Status,Valid_Break
GOTO IH_Interrupt_Poll
MOVF Int_Dimmer_Count_L,F
BTFSS STATUS,Z
GOTO IH_Check_Valid_SC
MOVF Int_Dimmer_Count_H,F
BTFSS STATUS,Z
GOTO IH_Check_Valid_SC
BCF Int_RX_Status,Valid_SC
MOVF DMX_Start_Code,W ; No, retrieve our start code
XORWF Int_RX_Data,W ; Compare the two codes
BTFSC STATUS,Z
BSF Int_RX_Status,Valid_SC
GOTO IH_Inc_Dimmer_Num
IH_Check_Valid_SC
BTFSS Int_RX_Status,Valid_SC
GOTO IH_Interrupt_Poll
MOVF Int_Dimmer_Index,W ; Have we saved all values?
XORLW Number_Of_Channels
BTFSC STATUS,Z
GOTO IH_Inc_Dimmer_Num
MOVF DMX_Start_Addr_H,W
SUBWF Int_Dimmer_Count_H,W
MOVWF Int_Temp_Diff_H
MOVF DMX_Start_Addr_L,W
SUBWF Int_Dimmer_Count_L,W
BTFSS STATUS,C
DECF Int_Temp_Diff_H,F
BTFSC Int_Temp_Diff_H,7
GOTO IH_Inc_Dimmer_Num
MOVLW Dimmer_Data ; Load buffer address
ADDWF Int_Dimmer_Index,W ; Add offset
MOVWF FSR ; Load address into indirect register
MOVF Int_RX_Data,W ; Get received data from UART
MOVWF INDF ; Store data into buffer
INCF Int_Dimmer_Index,F
IH_Inc_Dimmer_Num
INCF Int_Dimmer_Count_L,F
BTFSC STATUS,Z
INCF Int_Dimmer_Count_H,F
GOTO IH_Interrupt_Poll
IH_RX_HW_Overrun
BCF RCSTA,CREN ; Reset UART
BSF RCSTA,CREN ; receive logic
BCF HW_Overrun_LED ; Turn on hardware overrun LED
GOTO IH_Interrupt_Poll
IH_Check_Timer1_IE
BSF STATUS,RP0 ; Switch to bank 1
BTFSC PIE1,TMR1IE ; Is timer 1 interrupt enabled ?
GOTO IH_Timer1_Bank_Zero ; Yes
BCF STATUS,RP0 ; Switch to bank 0
GOTO IH_Exit ; Exit
IH_Timer1_Bank_Zero
BCF STATUS, RP0 ; Switch to bank 0
IH_Check_Timer1_IF
BTFSS PIR1,TMR1IF ; Has the timer overflowed ?
GOTO IH_Exit ; No, exit
BCF PIR1,TMR1IF ; Yes, clear timer interrupt flag
BSF STATUS,RP0 ; Switch to bank 1
BCF PIE1,TMR1IE ; Prevent further interrupts
BCF STATUS,RP0 ; Switch to bank 0
BSF Data_Rec_LED ; Turn off data rec indicator
BCF Relays_1_8 ; Turn relays 1-8 off
BCF Relays_9_16 ; Turn relays 9-16 off
BCF Int_RX_Status,Valid_Data
GOTO IH_Interrupt_Poll ; Finish interrupt routine
IH_Exit
MOVF Int_FSR,W ; Restore PIC state
MOVWF FSR
MOVF Int_PCLath,W
MOVWF PCLATH
SWAPF Int_Status,W
MOVWF STATUS
SWAPF Int_W,F
SWAPF Int_W,W
RETFIE ; Return from interrupt
Start CLRF PORTA ; \
CLRF PORTB ; Clear ports
CLRF PORTC ; /
BSF STATUS,RP0 ; Set up port I/O directions
MOVLW B'00010000'
MOVWF TRISA
MOVLW B'00001111'
MOVWF TRISB
MOVLW B'11001100'
MOVWF TRISC
MOVLW B'00000111'
MOVWF ADCON1 ; Set port A as digital I/O
BCF STATUS,RP0
BCF SDI ; Set SDI low
BCF Clock ; Set Clock low
BSF Load1_Bar ; Set Load_Bar high - DAC 1
BSF Load2_Bar ; Set Load_Bar high - DAC 2
BSF Clear_Bar ; Set Clear_Bar high
BCF Clear_Bar ; Reset DAC'S
BSF Clear_Bar ; /
BSF Data_Rec_LED ; Turn data received LED off
BCF Relays_1_8 ; Turn relays 1-8 off (future use)
BCF Relays_9_16 ; Turn relays 9-16 off (future use)
BSF HW_Overrun_LED ; Turn hardware overrun LED off
BSF SW_Overrun_LED ; Turn software overrun LED off
BSF Ones_Digit ; Turn off BCD ones digit
BSF Tens_Digit ; Turn off BCD tens digit
BSF Hundreds_Digit ; Turn off BCD hundreds digit
; Initialize the dimmer data to zero's
CLRF Loop1
MOVLW Dimmer_Data
MOVWF FSR
Continue_Init
CLRF INDF
INCF FSR,F
INCF Loop1,F
MOVF Loop1,W
XORLW Number_Of_Channels
BTFSS STATUS,Z
GOTO Continue_Init
; Initialize interrupt handler variables
CLRF Int_RX_Status
CLRF Int_Dimmer_Count_H
CLRF Int_Dimmer_Count_L
CLRF Int_Dimmer_Index
; Read option jumper (button)
CLRF Config_Data
BTFSS Option_Jumper
BSF Config_Data,0
; Load DMX512 start address
BCF Hundreds_Digit ; Select hundreds digit
NOP ; Let things stabilize
COMF PORTB,W ; Read and complement BCD value
ANDLW H'0F' ; Mask off upper nibble
MOVWF BCD_Digit_H ; Save hundreds digit
BSF Hundreds_Digit ; Deselect hundreds digit
NOP
BCF Tens_Digit ; Select tens digit
NOP ; Let things stabilize
COMF PORTB,W ; Read and complement BCD value
ANDLW H'0F' ; Mask off upper nibble
MOVWF BCD_Digit_T ; Save tens digit
BSF Tens_Digit ; Deselect tens digit
NOP
BCF Ones_Digit ; Select ones digit
NOP ; Let things stabilize
COMF PORTB,W ; Read and complement BCD value
ANDLW H'0F' ; Mask off upper nibble
MOVWF BCD_Digit_O ; Save ones digit
BSF Ones_Digit ; Deselect ones digit
CALL BCD_To_Binary ; Convert BCD digits to binary
MOVF BCD_H_Byte,F ; Check range of start address
BTFSC STATUS,Z ; Must be between 1 and 497
GOTO Check_For_Zero ; If > 497 then set to 497
MOVF BCD_H_Byte,W ; If < 1 then set to 1
SUBLW Range_Const_H
BTFSS STATUS,C
GOTO Set_H_Byte
MOVF BCD_L_Byte,W
SUBLW Range_Const_L
BTFSS STATUS,C
GOTO Set_L_Byte
GOTO Store_DMX_Address
Set_H_Byte MOVLW Range_Const_H
MOVWF BCD_H_Byte
Set_L_Byte MOVLW Range_Const_L
MOVWF BCD_L_Byte
GOTO Store_DMX_Address
Check_For_Zero MOVF BCD_L_Byte,F
BTFSS STATUS,Z
GOTO Store_DMX_Address
MOVLW H'01'
MOVWF BCD_L_Byte
Store_DMX_Address MOVF BCD_L_Byte,W
MOVWF DMX_Start_Addr_L ; Store DMX address
MOVF BCD_H_Byte,W
MOVWF DMX_Start_Addr_H
; Load DMX512 start code
Store_DMX_Start_Code MOVLW 0 ; Start code of 0 = dimmer data
MOVWF DMX_Start_Code ; Store DMX start code
; Initialize UART
BSF STATUS,RP0
MOVLW Baud_Rate_Constant
MOVWF SPBRG
BSF TXSTA,BRGH
BCF TXSTA,SYNC
BSF PIE1,RCIE
BCF STATUS,RP0
BSF RCSTA,RX9
BSF RCSTA,CREN
BSF RCSTA,SPEN
; Initialize timer 1
CLRF T1CON
BSF T1CON,T1CKPS1 ; Select prescale of 4
BSF T1CON,TMR1ON ; Turn on the timer
; Initialize interrupts
CLRF INTCON
BSF INTCON,PEIE
BSF INTCON,GIE ; Enable interrupts
Main
CLRF DAC_Count ; Start with DAC 0
MOVLW Dimmer_Data
MOVWF FSR
Next_DAC
BCF STATUS,C ; Shift in the DAC number
MOVLW H'05' ; Skip over 5 MSB's
MOVWF Loop1
Shift_Address RLF DAC_Count,F
DECFSZ Loop1,F
GOTO Shift_Address
MOVLW H'03' ; Load our DAC number
MOVWF Loop1
Address_Load RLF DAC_Count,F
BCF SDI
BTFSC STATUS,C
BSF SDI
NOP
BSF Clock
NOP
BCF Clock
DECFSZ Loop1,F
GOTO Address_Load
RLF DAC_Count,F ; Get original value back
MOVF INDF,W
MOVWF Rx_Data
BTFSC Config_Data,0 ; Do we complement data ?
COMF Rx_Data,F ; Yes
BCF STATUS,C ; Shift in dimmer value
MOVLW H'08'
MOVWF Loop1
Data_Load RLF Rx_Data,F
BCF SDI
BTFSC STATUS,C
BSF SDI
NOP
BSF Clock
NOP
BCF Clock
DECFSZ Loop1,F
GOTO Data_Load
RLF Rx_Data,F ; Get original value back
BTFSC DAC_Count,3 ; Strobe the correct DAC
GOTO Strobe_DAC2
BCF Load1_Bar
NOP
BSF Load1_Bar
GOTO Inc_DAC_Count
Strobe_DAC2 BCF Load2_Bar
NOP
BSF Load2_Bar
Inc_DAC_Count INCF DAC_Count,F
INCF FSR,F
Finished_All_DACS MOVF DAC_Count,W ; Have we updated all
XORLW Number_Of_DACS ; 16 DACS ?
BTFSS STATUS,Z
GOTO Next_DAC ; No
GOTO Main ; Yes
BCD_To_Binary CLRF BCD_H_Byte ; Clear high value
MOVF BCD_Digit_H,W ; Move hundreds digit
MOVWF BCD_L_Byte ; to low value
CALL Mult_x_10 ; Multiply result by ten
MOVF BCD_Digit_T,W ; Add tens digit
ADDWF BCD_L_Byte,F ; to result
CALL Mult_x_10 ; Multiply result by ten
MOVF BCD_Digit_O,W ; Add ones digit
ADDWF BCD_L_Byte,F ; to result
BTFSC STATUS,C ; Has the result overflowed ?
INCF BCD_H_Byte,F ; Yes, increment high byte
RETURN
Mult_x_10 MOVF BCD_L_Byte,W ; Save
MOVWF Mult_Temp_L ; original
MOVF BCD_H_Byte,W ; values
MOVWF Mult_Temp_H
BCF STATUS,C ; Clear status
RLF BCD_L_Byte,F ; Rotate to
RLF BCD_H_Byte,F ; multiply by two
BCF STATUS,C ; Clear status
RLF BCD_L_Byte,F ; Rotate to
RLF BCD_H_Byte,F ; multiply by two
MOVF Mult_Temp_L,W ; Load original low byte
ADDWF BCD_L_Byte,F ; Add to new low byte
BTFSS STATUS,C ; Did a carry occur?
GOTO Continue ; No, continue....
INCF BCD_H_Byte,F ; Yes, increment high byte
Continue MOVF Mult_Temp_H,W ; Load original high byte
ADDWF BCD_H_Byte,F ; Add to new high byte
BCF STATUS,C ; Clear status
RLF BCD_L_Byte,F ; Rotate to
RLF BCD_H_Byte,F ; multiply by two
RETURN ; Data is now multiplied by ten
END
DMX512 to Analog Voltage Converter (Version 1.12, December 31, 2001)
Contents
1. General Description
2. PIC Microcontroller
3. RS-485 Differential Receiver
4. BCD Input Switches
5. Status LEDs
6. Relay Outputs/Option Switch
7. Digital to Analog Converters
8. DAC Reference Voltage
9. Channel Output Buffers
1. General Description
1.1 The circuit presented here implements a DMX512 to analog voltage
converter.
2. PIC Microcontroller
2.1 The heart of this DMX receiver is a Microchip PIC16F876
microcontroller. The 16F876 was chosen for primarily two reasons.
One, it has a built in UART; and two, the UART is capable of
receiving 250 kbps with no speed error (@ 20 MHz).
3. RS-485 Differential Receiver
3.1 This RS-485 transceiver (75176B) provides the necessary conversion
from the RS-485 voltage specification to a standard TTL level. This
signal is connected to the PICs UART receive data input (pin 18).
4. BCD Input Switches
4.1 BCD Switch Decoding
PIC
Output BCD Switch Common
------------------------------------
Port B, pin 6 Start Address Hundreds
Port B, pin 5 Start Address Tens
Port B, pin 4 Start Address Ones
BCD Switch PIC
Input Input
----------------------------
Binary 1 Port B, pin 0
Binary 2 Port B, pin 1
Binary 4 Port B, pin 2
Binary 8 Port B, pin 3
4.2 DMX Start Address Switches
4.2.1 These three BCD switches provide input for setting the
starting DMX address.
5. Status LEDs
5.1 Data Received LED
5.1.1 This signal is provided by output RB7 (pin 28).
5.1.2 This LED illuminates anytime a DMX signal is being received.
5.2 Hardware Overrun LED
5.2.1 This signal is provided by output RC4 (pin 15).
5.2.2 This LED will illuminate if data is being received faster than
the hardware UART is configured to handle. During normal
program execution this LED should never illuminate.
5.3 Software Overrun LED
5.3.1 This signal is provided by output RC5 (pin 16).
5.3.2 This LED will illuminate if data is being received faster than
the interrupt handler/main program loop can handle. During
normal program execution this LED should never illuminate.
6. Relay Outputs/Option Switch
6.1 Relay Outputs
6.1.1 These signals are provided by outputs RC0 and RC1 (pins 11,12).
6.1.2 These outputs are for future use. At the present time, they
provide a logic high when a DMX signal is being received,
logic low otherwise.
6.2 Option Switch
6.2.1 This signal is present on input RC2 (pin 13).
6.2.2 This switch is read upon reset of the PIC. If this signal is
at logic high, then the data received in the DMX data stream
is sent to the DACs "as is". This results in a linear
relationship between the DAC output voltages and DMX dimmer
values. If this signal is at logic low, then the data
received is complemented before it is sent to the DACs. This
results in an inverse relationship between the DAC output
voltages and DMX dimmer values.
7. Digital to Analog Converters
7.1 The digital to analog conversion is provided by a pair of Analog
Devices DAC8800s. Each DAC8800 provides eight outputs, thus
bringing the total number of channels to sixteen.
7.2 DAC/PIC Interfacing
7.2.1 Clear
7.2.1.1 The clear pins (pin 12) of both DAC8800s are tied
together and are driven by output RA0 (pin 2) of the
PIC.
7.2.2 Clock
7.2.2.1 The clock pins (pin 9) of both DAC8800s are tied
together and are driven by output RA1 (pin 3) of the
PIC.
7.2.3 SDI
7.2.3.1 The serial data in pins (pin 8) of both DAC8800s are
tied together and are driven by output RA2 (pin 4)
of the PIC.
7.2.4 Load
7.2.4.1 The load pin (pin 13) of the first DAC8800
(channels 1-8) is driven by ouput RA5 (pin 7) of the
PIC.
7.2.4.2 The load pin (pin 13) of the second DAC8800
(channels 9-16) is driven by ouput RA3 (pin 5) of the
PIC.
8. DAC Reference Voltage
8.1 The voltage reference provided to the DAC8800s is obtained from a
resistive voltage divider and buffered by an Analog Devices OP284
operational amplifier. The same voltage reference is provided to
both DAC8800s.
Use of other op amps (less expensive) is possible. Use of these was
due to samples on hand.
9. Channel Output Buffers
9.1 The outputs from each DAC channel are routed through an Analog
Devices OP484 operational amplifier. This op amp buffers the DAC
output and provides the DAC with a high impedance load.
Use of other op amps (less expensive) is possible. Use of these was
due to samples on hand.