; Analog Voltage to DMX512 Converter ; Copyright (C) 2000 by Kelly J. Kohls ; All rights reserved ; Version 1.00; February 16, 2000 ; 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 ; This include file is P16F876.INC __CONFIG (_HS_OSC & _WDT_OFF & _CP_OFF & _PWRTE_ON & _BODEN_ON & _CPD_OFF & _DEBUG_OFF & _LVP_OFF & _WRT_ENABLE_ON) ; Define program constants Start_Vector EQU H'00' Interrupt_Vector EQU H'04' Ram_Base EQU H'20' Common_Ram_Base EQU H'70' Osc_Freq EQU 20000000 Baud_Rate EQU 250000 Baud_Rate_Const EQU (Osc_Freq/(16*Baud_Rate))-1 DMX_Channels EQU 4 A2D_Channels EQU 4 Range_Const_L EQU (512 - DMX_Channels + 1) & H'0FF' Range_Const_H EQU (512 - DMX_Channels + 1) >> 8 Break_Timer_Const_L EQU 64 ; Timer 1/Compare 1 low value w/o prescale (90 uS) Break_Timer_Const_H EQU 234 ; Timer 1/Compare 1 high value w/o prescale (90 uS) MAB_Delay_Const EQU 2 ; Delay loop constant (10 uS) Packet_Timer_Const_L EQU 144 ; Timer 1 low value w/o prescale (1200 uS) Packet_Timer_Const_H EQU 232 ; Timer 1 high value w/o prescale (1200 uS) Start_Code EQU 0 ; Define status flag bit value A2D_Time_Const EQU 33 ; Channel aquisition time constant ; 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 option jumper control port #DEFINE Option_Jumper PORTB,7 ; Define scope trigger output port #DEFINE Scope_Trigger PORTC,0 ; Define LED output port #DEFINE LED PORTC,3 ; Set up RAM ; Define interrupt handler global variables CBLOCK Common_Ram_Base Int_W Int_Status Int_PCLath Int_FSR ENDC ; Define other variables CBLOCK Ram_Base ; Define interrupt handler local variables Int_Status_Flags Int_Dimmer_Count_L Int_Dimmer_Count_H Int_Temp_Diff_H Int_MAB_Delay ; Define BCD switch variables BCD_Digit_H BCD_Digit_T BCD_Digit_O BCD_L_Byte BCD_H_Byte ; Define DMX512 specific variables DMX_Start_Addr_L DMX_Start_Addr_H DMX_Start_Code ; Program specific variables Config_Data Delay_Count A2D_Index ; Define multiply routine local variables Mult_Temp_L Mult_Temp_H ; Define DMX data DMX_Data_Index DMX_Data : DMX_Channels ENDC ; Start program code ORG Start_Vector ; 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 ; Interrupt_Poll Check_CCP1_IE BSF STATUS,RP0 ; Switch to bank 1 BTFSS PIE1,CCP1IE ; Is compare 1 interrupt enabled? GOTO CCP1_Bank_Zero ; No, switch banks and exit BCF STATUS,RP0 ; Switch to bank 0 GOTO Check_CCP1_IF ; Yes, is compare 1 interrupt pending? CCP1_Bank_Zero BCF STATUS,RP0 ; Switch to bank 0 GOTO Check_T1_IE ; No Check_CCP1_IF BTFSS PIR1,CCP1IF ; Is compare 1 interrupt pending? GOTO Check_T1_IE ; No BCF PIR1,CCP1IF ; BSF STATUS,RP0 ; BSF TXSTA,TXEN ; Enable transmitter BCF PIE1,CCP1IE ; BCF STATUS,RP0 ; CLRF DMX_Data_Index ; Reset data index MOVLW 1 ; Reset dimmer count MOVWF Int_Dimmer_Count_L ; CLRF Int_Dimmer_Count_H ; BSF Int_Status_Flags,Start_Code ; Set flag to transmit start code MOVLW MAB_Delay_Const ; Load the MAB delay constant MOVWF Int_MAB_Delay ; MAB_Loop DECFSZ Int_MAB_Delay,F ; Generate the MAB GOTO MAB_Loop ; BSF STATUS,RP0 ; BSF PIE1,TXIE ; Enable UART TX interrupts BCF STATUS,RP0 ; GOTO Interrupt_Poll ; Check_T1_IE BSF STATUS,RP0 ; Switch to bank 1 BTFSS PIE1,TMR1IE ; Is timer 1 interrupt enabled? GOTO T1_Bank_Zero ; No, switch banks and exit BCF STATUS,RP0 ; Switch to bank 0 GOTO Check_T1_IF ; Yes, is timer 1 interrupt pending? T1_Bank_Zero BCF STATUS,RP0 ; Switch to bank 0 GOTO Check_UART_TX_IE ; No Check_T1_IF BTFSS PIR1,TMR1IF ; Is timer 1 interrupt pending? GOTO Check_UART_TX_IE ; No BSF STATUS,RP0 ; Yes Int_TSR_Empty BTFSS TXSTA,TRMT ; Is the TSR empty? GOTO Int_TSR_Empty ; No, need to wait BCF STATUS,RP0 ; Yes, generate the "Break" BSF Scope_Trigger ; Trigger our scope so we can see the "Break" NOP BCF Scope_Trigger ; pulse width around 400 nS MOVLW Packet_Timer_Const_L ; Load timer 1 MOVWF TMR1L ; with "Packet" MOVLW Packet_Timer_Const_H ; time constant MOVWF TMR1H ; BCF PIR1,TMR1IF ; Clear timer 1 interrupt flag BCF PIR1,CCP1IF ; Clear compare 1 interrupt flag BSF STATUS,RP0 ; BCF TXSTA,TXEN ; Disable transmitter BCF PIE1,TMR1IE ; Clear timer 1 interrupt enable BSF PIE1,CCP1IE ; Set compare 1 interrupt enable BCF STATUS,RP0 ; GOTO Interrupt_Poll ; Check other interrupts Check_UART_TX_IE BSF STATUS,RP0 ; Switch to bank 1 BTFSS PIE1,TXIE ; Is UART interrupt enabled? GOTO UART_TX_Bank_Zero ; No, switch banks and exit BCF STATUS,RP0 ; Switch to bank 0 GOTO Check_UART_TX_IF ; Yes, is UART interrupt pending? UART_TX_Bank_Zero BCF STATUS,RP0 ; Switch to bank 0 GOTO Interrupt_Exit ; Check_UART_TX_IF BTFSS PIR1,TXIF ; Is UART interrupt pending? GOTO Interrupt_Exit ; No BSF STATUS,RP0 ; Yes, Set 2nd stop bit BSF TXSTA,TX9D ; BCF STATUS,RP0 ; BTFSS Int_Status_Flags,Start_Code ; Do we need to transmit the start code? GOTO Load_DMX_Data ; No, transmit DMX dimmer data BCF Int_Status_Flags,Start_Code ; Yes, clear flag MOVF DMX_Start_Code,W ; Send the start code MOVWF TXREG ; GOTO Interrupt_Poll ; Check other interrupts Load_DMX_Data MOVF DMX_Start_Addr_H,W ; Have we reached the SUBWF Int_Dimmer_Count_H,W ; BCD switch value? 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 Send_Null ; No, transmit a '0' MOVLW DMX_Data ; Yes, load DMX data address ADDWF DMX_Data_Index,W ; Add offset MOVWF FSR ; Load file select register MOVF INDF,W ; Move data MOVWF TXREG ; into transmitter INCF DMX_Data_Index,F ; Move to next data location GOTO Inc_Dimmer_Count ; Increment dimmer count Send_Null CLRW ; Transmit a value of '0' MOVWF TXREG ; Inc_Dimmer_Count INCF Int_Dimmer_Count_L,F ; Increment dimmer count BTFSC STATUS,Z ; INCF Int_Dimmer_Count_H,F ; MOVF DMX_Data_Index,W ; Have we transmitted all XORLW DMX_Channels ; data bytes? BTFSS STATUS,Z ; GOTO Interrupt_Poll ; No, check other interrupts BSF STATUS,RP0 ; Yes BSF PIE1,TMR1IE ; Set timer 1 to generate the "Break" BCF PIE1,TXIE ; Disable UART interrupts BCF STATUS,RP0 ; GOTO Interrupt_Poll ; Check other interrupts Interrupt_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'00111111' ; MOVWF TRISA ; MOVLW B'10001111' ; MOVWF TRISB ; MOVLW B'11110000' ; MOVWF TRISC ; BCF STATUS,RP0 ; ; Initialize BCD switches BSF Hundreds_Digit ; BSF Tens_Digit ; BSF Ones_Digit ; ; Read option jumper(s) CLRF Config_Data ; BTFSC 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 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 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 ; GOTO Check_For_Zero ; MOVF BCD_H_Byte,W ; 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 1 ; 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 ; Store_DMX_Start_Code MOVLW 0 ; Default start code MOVWF DMX_Start_Code ; Store DMX start code ; Initialize DMX data CLRF DMX_Data_Index ; Reset index variable Init_DMX_Data MOVLW DMX_Data ; Load DMX data address ADDWF DMX_Data_Index,W ; Add offset MOVWF FSR ; Move into file select register CLRF INDF ; Clear data INCF DMX_Data_Index,F ; Point to next byte MOVF DMX_Data_Index,W ; Have we reached the end? XORLW DMX_Channels ; BTFSS STATUS,Z ; GOTO Init_DMX_Data ; No, keep initializing ; Initialize UART BSF STATUS,RP0 ; MOVLW Baud_Rate_Const ; Set baud rate MOVWF SPBRG ; BSF TXSTA,BRGH ; Enable high speed transmission BCF TXSTA,SYNC ; Async transmission BCF STATUS,RP0 ; BSF RCSTA,SPEN ; Turn on UART BSF STATUS,RP0 ; BSF TXSTA,TX9 ; Enable 9 bit data transmission BSF TXSTA,TXEN ; Enable transmitter BCF STATUS,RP0 ; ; Initialize timer 1 CLRF T1CON ; BSF T1CON,TMR1ON ; Turn on the timer ; Initialize compare module CLRF CCP1CON BSF CCP1CON,CCP1M3 BSF CCP1CON,CCP1M1 MOVLW Break_Timer_Const_L MOVWF CCPR1L MOVLW Break_Timer_Const_H MOVWF CCPR1H ; Initialize interrupts BSF PIR1,TMR1IF ; Want to "force" an interrupt BCF PIR1,CCP1IF ; Clear compare 1 interrupt flag BSF STATUS,RP0 ; BSF PIE1,TMR1IE ; Enable timer 1 interrupts BCF PIE1,CCP1IE ; Clear compare 1 interrupt enable BCF PIE1,RCIE ; Clear receive interrupt enable BCF PIE1,TXIE ; Clear transmit interrupt enable BCF STATUS,RP0 ; CLRF INTCON ; BSF INTCON,PEIE ; BSF INTCON,GIE ; Enable interrupts ; Initialize analog to digital converter BSF STATUS,RP0 ; MOVLW B'00000010' ; Enable analog channels MOVWF ADCON1 ; BCF STATUS,RP0 ; CLRF ADCON0 ; BSF ADCON0,ADCS1 ; BSF ADCON0,ADON ; Turn on A/D module A2D_Start CLRF A2D_Index ; Clear index MOVLW DMX_Data ; Load DMX data address MOVWF FSR ; Move to file select register BSF LED ; Turn LED off COMF INDF,W ; Retreive and complement data BTFSC STATUS,Z ; Do we have 0? BCF LED ; Yes, turn LED on A2D_Conversion MOVLW B'11000111' ; Clear analog channel ANDWF ADCON0,F ; BCF STATUS,C ; RLF A2D_Index,F ; RLF A2D_Index,F ; RLF A2D_Index,W ; IORWF ADCON0,F ; Select new channel BCF STATUS,C ; RRF A2D_Index,F ; Rotate right to get RRF A2D_Index,F ; our original data back MOVLW A2D_Time_Const ; Load channel aquisition MOVWF Delay_Count ; delay constant A2D_Delay DECFSZ Delay_Count,F ; GOTO A2D_Delay ; BSF ADCON0,GO_DONE ; Start A/D conversion A2D_Wait BTFSC ADCON0,GO_DONE ; Are we finished? GOTO A2D_Wait ; No, keep waiting MOVLW DMX_Data ; Yes, load DMX data address ADDWF A2D_Index,W ; Add offset MOVWF FSR ; Move to file select register BTFSC Config_Data,0 ; GOTO Complement_Data ; MOVF ADRESH,W ; Retrieve conversion result GOTO Store_Data ; Complement_Data COMF ADRESH,W Store_Data MOVWF INDF ; Store into data area INCF A2D_Index,F ; Move to next channel MOVF A2D_Index,W ; Have we converted the last channel? XORLW A2D_Channels ; BTFSS STATUS,Z ; GOTO A2D_Conversion ; No, do next conversion GOTO A2D_Start ; Yes, start over 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