;***************************************************************************
;                                                                     
;	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

