; 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