;*************************************************************************** ; ; Test routine for DCF77 bit stream decoding V1.01 ; ================================================ ; ; written by Peter Luethi, 18.01.2003, Germany ; http://www.electronic-engineering.ch ; last update: 27.04.2004 ; ; V1.01: Updated to comply with latest m_rsxxx.asm modules ; (27.04.2004) ; ; V1.00: Initial release (20.01.2003) ; ; This code and accompanying files may be distributed freely and ; modified, provided this header with my name and this notice remain ; intact. Ownership rights remain with me. ; You may not sell this software without my approval. ; ; This software comes with no guarantee or warranty except for my ; good intentions. By using this code you agree to indemnify me from ; any liability that might arise from its use. ; ; ; SPECIFICATIONS: ; =============== ; Processor: Microchip PIC 16F84 ; Clock: 4.00 MHz XT ; Throughput: 1 MIPS ; Serial Output: 19200 baud, 8 bit, no parity, 1 stopbit ; Acquisition Methodology: Preemptive, interrupt-based ; DCF77 PWM data decoding ; Required Hardware: PWM based DCF77 decoder, ; RS232 level shifter (MAX232) ; Required Software: DCF77_Visualizer.xls (Excel) ; or equivalent RS232 terminal ; ; ; DESCRIPTION: ; ============ ; Developed and tested on PIC 16F84. ; The DCF77 bit stream decoder delivers PWM data specified to be ; logical 0 (100 ms low) or logical 1 (200 ms low). ; Default state of the DCF77 PWM line is high. ; This program performs software based PWM decoding, which is done ; using a busy wait routine and a busy flag. ; DCF77 PWM data is applied on portB,7 and recognized with portB ; change interrupts. A falling edge starts the busy wait routine, ; running for exactly 150 ms, with busy flag set. On exit, the ; busy flag is cleared. ; The subsequent DCF77 rising edge will either occur around 100 ms ; or 200 ms after the beginning of the busy wait routine - and can ; then be decoded to logic 0 or 1, according to the current busy ; flag state. ; The controller transmits RS232 data every DCF77 rising edge, ; i.e. every completed bit. With clean DCF77 reception, there ; will be an RS232 transmission every second. ; On the 59th bit ('minute mark'), there is no DCF77 bit, and ; the software will preceed 0x2 to the transmission of the first ; bit (bit 0) of the next 59 bit DCF77 stream. ; ; ; IMPLEMENTED FEATURES: ; ===================== ; - DCF77 PWM data decoding, 'minute mark' capture ; - Uni-directional RS232 data transmission ; (used portA,4 for RS232 TX -> needs 6k pull-up resistor!) ; ; LIMITATIONS: ; ============ ; - Only RS232 transmission, no reception. ; - No DCF77 parity bit decoding and error handling. ; - No error handling on bad DCF77 reception. ; (data consistency check) ; ; ;*************************************************************************** ;***** COMPILATION MESSAGES & WARNINGS ***** ERRORLEVEL -207 ; found label after column 1 ERRORLEVEL -302 ; register in operand not in bank 0 ;***** PROCESSOR DECLARATION & CONFIGURATION ***** PROCESSOR 16F84 #include "p16f84.inc" ; embed Configuration Data within .asm File. __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC ;***** MEMORY STRUCTURE ***** ORG 0x00 ; processor reset vector goto MAIN ORG 0x04 ; interrupt vector location goto ISR ; Interrupt Service Routine ;***** PORT DECLARATION ***** #define DCF_IN PORTB,7 ; chip select, active low ; portA,4 needs 6k pull-up resistor (open-collector)! #define TXport PORTA,4 ; RS232 output port, could be #define TXtris TRISA,4 ; any active push/pull port ; RS232 input port is RB0, because of its own interrupt flag. ; No RS232 input used in this project. ;***** CONSTANT DECLARATION ***** CONSTANT BASE = 0x0C ; Base address of user file registers 16F84 ;***** REGISTER DECLARATION ***** ; universal temporary register ; (TEMP1 used by m_wait, TEMP1 and TEMP2 by m_rsxxx) TEMP1 set BASE+d'0' TEMP2 set BASE+d'1' TEMP3 set BASE+d'2' ; general FLAGreg equ BASE+d'4' ; register containing various flags #define DCFprev FLAGreg,0x00 ; previous state of DCF_IN #define BUSYflag FLAGreg,0x01 ; flag for DCF busy wait #define XMITflag FLAGreg,0x02 ; flag for RS232 transmission CNT equ BASE+d'5' ; DCF77 bit counter, cleared every minute DAT equ BASE+d'6' ; DCF data bit (0/1) LO equ BASE+d'7' ; for 24 bit counter MED equ BASE+d'8' ; for 24 bit counter HI equ BASE+d'9' ; for 24 bit counter ; RS232 TXD equ BASE+d'13' ; used for transmission RXD equ BASE+d'14' ; received value ; interrupt context save/restore ISR_TMP3 equ BASE+d'15' ; temporary register for ISR W_TEMP equ BASE+d'16' ; context register (ISR) STATUS_TEMP equ BASE+d'17' ; context register (ISR) PCLATH_TEMP equ BASE+d'18' ; context register (ISR) FSR_TEMP equ BASE+d'19' ; context register (ISR) ;***** INCLUDE FILES ***** #include "..\m_bank.asm" #include "..\m_wait.asm" #include "..\m_rs192.asm" ;***** MACROS ***** ;***** SUBROUTINES ***** XMIT bcf XMITflag ; clear transmit flag (for ISR) decf HI,f ; decrement HI skpnz ; skip if not zero goto _xmit1 movlw 0x02 ; 'start signal', send 0x2 SENDw ; HI was > 1, send start signal clrf CNT ; reset counter every full minute _xmit1 clrf LO ; clear 24 bit counter registers clrf MED clrf HI movfw DAT ; get DCF data SENDw ; transmit content of DAT register RETURN ;***** INTERRUPT SERVICE ROUTINE ***** ISR ;************************ ;*** ISR CONTEXT SAVE *** ;************************ bcf INTCON,GIE ; disable all interrupts btfsc INTCON,GIE ; assure interrupts are disabled goto ISR movwf W_TEMP ; context save: W swapf STATUS,W ; context save: STATUS movwf STATUS_TEMP ; context save clrf STATUS ; bank 0, regardless of current bank movfw PCLATH ; context save: PCLATH movwf PCLATH_TEMP ; context save clrf PCLATH ; page zero, regardless of current page bcf STATUS,IRP ; return to bank 0 movfw FSR ; context save: FSR movwf FSR_TEMP ; context save ;*** context save done *** ;************************** ;*** ISR MAIN EXECUTION *** ;************************** ;*** check origin of interrupt *** btfss INTCON,RBIF ; check RBIF interrupt flag goto ISRend ; unexpected interrupt ;goto _ISR_DCF ; on portB change, goto DCF decoding ;********************** ;*** DCF77 DECODING *** ;********************** _ISR_DCF btfsc DCF_IN ; check DCF input, skip if cleared goto _DCF_rise ; DCF_IN = 1 ;goto _DCF_fall ; DCF_IN = 0 _DCF_fall ;*** falling edge on DCF input *** ; check previous DCF state btfss DCFprev goto _DCF_end ; previous state has been 0, exit ; proceed bcf DCFprev ; set DCFprev to 0 bsf BUSYflag ; request another wait loop (in main) incf CNT,f ; increment bit counter every second goto _DCF_end ; terminate DCF ISR _DCF_rise ;*** rising edge on DCF input *** ; check previous DCF state btfsc DCFprev goto _DCF_end ; previous state has been 1, exit ; proceed: decide whether it has been a logical 0 (100 ms low) ; or logical 1 (200 ms low) bsf DCFprev ; set DCFprev to 1 bsf XMITflag ; request RS232 transmission (in main) clrf DAT ; reset DCF data register btfss BUSYflag ; check, if DCF busy wait flag still on incf DAT,f ; if no longer active, logic 1 (> 150 ms) bcf BUSYflag ; reset DCF busy flag _ISR_RS232error ; just a dummy label for m_rs096.asm _DCF_end bcf INTCON,RBIF ; clear RBIF interrupt flag ;***************************************** ;*** ISR TERMINATION (CONTEXT RESTORE) *** ;***************************************** ISRend movfw FSR_TEMP ; context restore movwf FSR ; context restore movfw PCLATH_TEMP ; context restore movwf PCLATH ; context restore swapf STATUS_TEMP,W ; context restore movwf STATUS ; context restore swapf W_TEMP,F ; context restore swapf W_TEMP,W ; context restore RETFIE ; enable global interrupt (INTCON,GIE) ;***** END OF INTERRUPT SERVICE ROUTINE ***** ;************** MAIN ************** MAIN BANK1 clrf OPTION_REG ; enable portB pull-ups bcf TXtris ; set output ; RS232 reception not used ;bsf RXtris ; set input with weak pull-up ;bcf OPTION_REG,INTEDG ; RS232 interrupt on falling edge BANK0 bsf TXport ; set default state: logical 1 clrf CNT ; initialize bit counter clrf DAT ; initialize DCF data register clrf LO ; clear 24 bit counter registers clrf MED clrf HI clrf INTCON ; disable all interrupt sources, clear flags bsf INTCON,RBIE ; enable portB change interrupt bsf INTCON,GIE ; global interrupt enable goto m_loop ; start within main loop busy_loop ; *** inner loop *** ; this loop is executed whenever a falling DCF_IN edge has been ; sampled in the interrupt service routine WAITX d'73', d'2' ; busy wait (150 ms) bcf BUSYflag ; clear busy flag (for ISR) m_loop ; *** main loop *** ; 24 bit counter movlw 0x01 addwf LO,f ; increment LO => affects carry bit skpc ; skip on carry goto _next ; if no carry, exit addwf MED,f ; if carry, increment MED addcf HI,f ; add MED's carry to HI (every 840 ms) _next btfsc BUSYflag ; check for new loop request by ISR goto busy_loop ; start inner loop btfsc XMITflag ; check for new TX request by ISR call XMIT ; call RS232 TX routine goto m_loop ; return to main loop END