;;======================================================================;;
;;			Booster-Manager					;;
;;======================================================================;;
;;									;;
;; Program:         Booster_Manager -- 4 DCC booster manager 		;;
;;                  DCC2Coil -- DCC 2 coil turnout decoder		;;
;; Code:            Paco Caņada						;;
;; Platform:        Microchip PIC12F629, 4 Mhz				;;
;; Date:            17.08.2010						;;
;; First release:   17.08.2010						;;
;; LastDate:        17.08.2010						;;
;;									;;
;;======================================================================;;
;
; DCC2Coil: Controls 2 turnouts (coil) with a fixed pulse time (0,5s)
; Booster-Manager: DCC decoder to turn on boosters
;
; Pressing programming button and sending an accessory command, stores the address.
;
; Minimal external components, uses internal oscilator at 4 MHz, 
;
; This program is distributed as is but WITHOUT ANY WARRANTY
; I hope you enjoy!!
;
; Revisions:
; 17.08.2010	Start of writting code
; 09.02.2011	Booster Manager: Added automatic reset on DCC fault


; ----- Definitions

#define		__VERNUM	D'1'
#define		__VERDAY	0x17
#define		__VERMONTH	0x08
#define		__VERYEAR	0x10


;#define		__BOOSTER_MANAGER	1		; Uncomment for Booster Manager application
;		^Yes	^No


	LIST	   p=12F629	; target processor

	errorlevel -305,-302

	#include p12F629.inc


	__CONFIG  _BODEN_ON & _CP_OFF & _WDT_OFF & _MCLRE_OFF & _PWRTE_OFF & _INTRC_OSC_NOCLKOUT 

				; Make sure that internal osc. is calibrated
				; Value has to be read before reprogramming the device.



; --- Constant values

FXTAL		equ	D'4000000'		; internal oscilator

GP_TRIS         equ     0x0C			; GP2,GP3: inputs
GP_INI          equ     0x00			; all zero
OPTION_INI	equ	0x88			; Option register: no pull-up, falling GP2, no prescaler, wdt 1:1
WPU_INI		equ	0x33			; Weak pull-up enable. default, no pull-ups

INTC_INI	equ	0x90			; GIE, INTE enable, PEIE disable
PIE1_INI	equ	0x00			; no interrupts


#define		COIL1A	GPIO,0			; 
#define		COIL1B	GPIO,1			; 
#define		DCCIN	GPIO,2			; DCC input pin
#define		SWITCH	GPIO,3			; Programm switch (GPIO,3 only input)
#define		COIL2A	GPIO,4			; 
#define		COIL2B	GPIO,5			; 


; --- EEPROM Section

#define		EE_INI		0x00

EE_ADDR1H	equ	EE_INI+0x00
EE_ADDR1L	equ	EE_INI+0x01


; ----- Variables

; --- Internal RAM Section

#define		RAMINI0		0x020		; 64 bytes of RAM

INT_W		equ	RAMINI0+0x00		; interrupt context registers
INT_STAT	equ	RAMINI0+0x01

SHIFT0		equ	RAMINI0+0x02
DATA1		equ	RAMINI0+0x03
DATA2		equ	RAMINI0+0x04
DATA3		equ	RAMINI0+0x05
DATA4		equ	RAMINI0+0x06

PREAMBLE	equ	RAMINI0+0x08
DCCSTATE	equ	RAMINI0+0x09
DCCBYTE		equ	RAMINI0+0x0A

EEDATA0		equ	RAMINI0+0x0B		; EEPROM shadow variables
EEADR0		equ	RAMINI0+0x0C

SRVADRH1	equ	RAMINI0+0x10		; DCC addresses
SRVADRL1	equ	RAMINI0+0x11		; 

FLAGS		equ	RAMINI0+0x30

ANALOGCNT	equ	RAMINI0+0x39		; analog detector timeout

; --- Flags

#define		NEW_PACKET	FLAGS,0		; New 3 byte packet received
#define		DCC4BYTE	FLAGS,3		; DCC command 4 bytes
#define		RESET_FLG	FLAGS,7		; reset packet



; --------------- Program Section --------------------------------------


		org	0x000

PowerUp:
		clrf	STATUS			; Bank 0 default
		clrf	INTCON			; Disable all interrupts
		clrf	PCLATH			; tables on page 0
		goto	INIT

; ----------------------------------------------------------------------

		org	0x004

Interrupt:
		movwf	INT_W			; save context registers		;1
		swapf	STATUS,w							;2
		movwf	INT_STAT							;3
		clrf	STATUS			; interrupt uses bank 0			;4

Int_DCC:
		btfss	INTCON,INTF		; RB0 interrupt?			;+4,	+6
		goto	EndInt								;+5,6,	+7,8

		btfss	DCCIN								;5
		goto	Int_Low_Half							;6,7

Int_High_Half:
		movf	DCCSTATE,w							; 8
		addwf	PCL,f								; 9

		goto	Preamble							; 10,11
		goto	WaitLow
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadLastBit
		goto	EndByte1
		goto	EndByte2
		goto	EndByte3
		goto	EndByte4

Int_Low_Half:
		movlw	d'256' - d'80'		; 77us: between 64us (one) and 90us (zero);8
		movwf	TMR0								;9
		bcf	INTCON,T0IF		; clear overflow flag for counting	;10
		bcf	INTCON,INTF							;11
		bsf	STATUS,RP0							;12
		bsf	OPTION_REG,INTEDG	; next interrupt on rising edge GP2	;13
		swapf	INT_STAT,w		; restore context registers		;14
		movwf	STATUS								;15
		swapf	INT_W,f								;16
		swapf	INT_W,w								;17
		retfie									;18,19

EndHighHalf:
		bcf	INTCON,INTF							;21
		bsf	STATUS,RP0							;22
		bcf	OPTION_REG,INTEDG	; next interrupt on falling edge GP2	;23
EndInt:
		swapf	INT_STAT,w		; restore context registers		;24
		movwf	STATUS								;25
		swapf	INT_W,f								;26
		swapf	INT_W,w								;27
		retfie									;28,29


Preamble:
		btfss	NEW_PACKET		; wait until last decoded		;12
		incf	PREAMBLE,f		;					;13
		btfsc	INTCON,T0IF		; if timer 0 overflows then is a DCC zero;14
		clrf	PREAMBLE		;					;15
		movlw	0xF6			; 10 preamble bits?			;16
		addwf	PREAMBLE,w		;					;17
		btfsc	STATUS,C		;					;18
		incf	DCCSTATE,f		; yes, next state			;19
		goto	EndHighHalf		;					;20,21
		

WaitLow:
		btfsc	INTCON,T0IF		; if timer 0 overflows then is a DCC zero;12
		incf	DCCSTATE,f		; then state				;13
		clrf	DCCBYTE			;					;14
		clrf	PREAMBLE		;					;15
		clrf	DATA4			;					;16
		goto	EndHighHalf		;					;17,18


ReadBit:
		bsf	STATUS,C							;12
		btfsc	INTCON,T0IF		; if timer 0 overflows then is a DCC zero;13
		bcf	STATUS,C							;14
		rlf	SHIFT0,f		; receiver shift register		;15
		incf	DCCSTATE,f		;					;16
		goto	EndHighHalf		;					;17,18
			
ReadLastBit:
		bsf	STATUS,C							;12
		btfsc	INTCON,T0IF		; if timer 0 overflows then is a DCC zero;13
		bcf	STATUS,C							;14
		rlf	SHIFT0,f		; receiver shift register		;15
		incf	DCCBYTE,w							;16
		addwf	DCCSTATE,f							;17
		goto	EndHighHalf		;					;18,19

EndByte1:
		movlw	0x00			;					;12
		btfsc	INTCON,T0IF		; End bit=1, invalid packet		;13
		movlw	0x02			;					;14
		movwf	DCCSTATE		;					;15
		movf	SHIFT0,w		;					;16
		movwf	DATA1			;					;17
		incf	DCCBYTE,f		;					;18
		goto	EndHighHalf		;					;19,20

EndByte2:
		movlw	0x00			;					;12
		btfsc	INTCON,T0IF		; End bit=1, invalid packet		;13
		movlw	0x02			;					;14
		movwf	DCCSTATE		;					;15
		movf	SHIFT0,w		;					;16
		movwf	DATA2			;					;17
		incf	DCCBYTE,f		;					;18
		goto	EndHighHalf		;					;19,20


EndByte3:
		btfss	INTCON,T0IF		; End bit=1, end of packet		;12
		goto	EndByte3x		;					;13,14
		movlw	0x02			;					;14
		movwf	DCCSTATE		;					;15
		movf	SHIFT0,w		;					;16
		movwf	DATA3			;					;17
		incf	DCCBYTE,f		;					;18
		bsf	DCC4BYTE		;					;19
		goto	EndHighHalf		;					;20,21
EndByte3x:
		clrf	DCCSTATE		;					;15
		movf	SHIFT0,w		;					;16
		movwf	DATA3			;					;17
		bsf	NEW_PACKET		;					;18
		bcf	DCC4BYTE		;					;19
		goto	EndHighHalf		;					;20,21

EndByte4:
		clrf	DCCSTATE		;					;12
		btfsc	INTCON,T0IF		; End bit=1, end of packet		;13
		goto	EndInt			; End bit=0, invalid packet		;14,15
		movf	SHIFT0,w		;					;15
		movwf	DATA4			;					;16
		bsf	NEW_PACKET		;					;17
		goto	EndHighHalf		;					;18,19


; ----------------------------------------------------------------------

SetOutput:
	ifndef	__BOOSTER_MANAGER
		clrf	GPIO
	endif
		addwf	PCL,f

		bsf	COIL1A
		return
		bsf	COIL1B
		return
		bsf	COIL2A
		return
		bsf	COIL2B
		return
	
	
	if ($ > d'255') 
		ERROR "  Tables exceded page 0.   If it works, why do you change it?   "
	endif


; ----- Initialization

INIT:
		clrf	GPIO
		movlw	0x07
		movwf	CMCON			; set GP2:0 to digital I/O

		bsf	STATUS,RP0		; bank 1
		movlw	GP_TRIS
		movwf	TRISIO
		call	0x3FF			; get OSCCAL value
		movwf	OSCCAL
		movlw	WPU_INI			; pull-ups
		movwf	WPU
		clrf	IOC			; interrupt on change
		clrf	VRCON			; voltage reference off
		movlw	OPTION_INI		; Option register: no pull-up, falling GP2, no prescaler, wdt 1:1
		movwf	OPTION_REG
		movlw	PIE1_INI
		movwf	PIE1
		bcf	STATUS,RP0		; bank 0
		clrf	PIR1
		movlw	0x31			; Timer 1 on, 1:8 prescaler
		movwf	T1CON


		movlw	0x20			; clear RAM
		movwf	FSR
ClearRAM:
		clrf	INDF
		incf	FSR,f
		movlw	0x60
		xorwf	FSR,w
		btfss	STATUS,Z
		goto	ClearRAM

		movlw	INTC_INI
		movwf	INTCON			; enable GP2 external interrupt

		call	LoadCV			; Load CV in SFR

	ifdef	__BOOSTER_MANAGER
		bsf	ANALOGCNT,2		; 65ms * 8 * 4
		goto	Analog
	endif

; ----------------------------------------------------------------------


MainLoop:
		btfsc	NEW_PACKET		; new packet?
		call	Decode			; yes, decode

		btfss	PIR1,TMR1IF		; pulse end?
		goto	MainLoop
		bcf	PIR1,TMR1IF
		clrf	GPIO			; all output clear

	ifdef	__BOOSTER_MANAGER
		decfsz	ANALOGCNT,f		; check analog signal
		goto	MainLoop		; 
Analog:						; no DCC signal present
		movlw	0x33			; activate all outputs
		movwf	GPIO
		clrf	TMR1L			; start timer
		clrf	TMR1H
		bcf	PIR1,TMR1IF		; ANALOGCNT=0: 65ms * 8 * 256
	endif

		goto	MainLoop
	

; ----------------------------------------------------------------------

Decode:
		bcf	NEW_PACKET		; prepare for next packet
		bcf	INTCON,INTE		; disable interrupts for more speed

		movf	DATA1,w			; exclusive or check
		xorwf	DATA2,w
		xorwf	DATA3,w
		xorwf	DATA4,w

		btfss	STATUS,Z		; valid packet?
		goto	ExitDecode		; no, return

		clrf	ANALOGCNT		; clear analog timeout
		bsf	ANALOGCNT,2		; 65ms * 8 * 4

; 'AAAAAAAA''DDDDDDDD''EEEEEEEE'		; 3 byte packet
;   DATA1     DATA2     DATA3

		movf	DATA1,w			; address = '00000000' ?
		btfsc	STATUS,Z
		goto	Broadcast

;		movf	DATA1,w
		andlw	0xC0
		xorlw	0x80			;'10AAAAAA'  '1AAADxxx' AAA:111 D:1 activate
		btfsc	DATA2,7
		btfss	STATUS,Z
		goto	ExitFunction
		goto	Accessory

Accessory:
		btfss	DATA2,3			; activate?
		goto	ExitFunction

		btfsc	SWITCH			; pressed switch?
		goto	AccSrv1			
AccessProg:
		movf	DATA1,w			; yes, program new address
		movwf	EEDATA0
		movlw	EE_ADDR1H
		call	SetParm			; save address
		movf	DATA2,w
		movwf	EEDATA0
		movlw	EE_ADDR1L
		call	SetParm			; save address
		call	LoadCV

AccSrv1:
		movf	SRVADRH1,w		; '10AAAAAA'  
		xorwf	DATA1,w
		btfss	STATUS,Z
		goto	ExitFunction
		movf	SRVADRL1,w		; '1AAADSxx'
		xorwf	DATA2,w
		andlw	0xFC
		btfss	STATUS,Z
		goto	ExitFunction
		rlf	DATA2,w			; calc output
		andlw	0x06
		call	SetOutput
		clrf	TMR1L			; start timer
		clrf	TMR1H
		bcf	PIR1,TMR1IF
		goto	ExitFunction
;AccSrv1B:
;		xorlw	0x01			; other position?
;		btfss	STATUS,Z
;		goto	ExitFunction
;
;		goto	ExitFunction



ExitFunction:
		bcf	RESET_FLG
		bsf	INTCON,INTE		; enable DCC interrupts
		return


; -----------------------------------------------------------------------

Broadcast:
		movf	DATA2,w			; reset packet?
		btfss	STATUS,Z
		goto	ExitDecode
;		bcf	PROG_2X
		bsf	RESET_FLG
Clear:						; reset decoder
		clrf	GPIO

		bsf	INTCON,INTE		; enable interrupts
		return


ExitDecode:
		bcf	RESET_FLG
		bsf	INTCON,INTE		; enable interrupts
		return


;---------------------------------------------------------------------------


LoadCV:
		movlw	0x00
		call	EE_Read
		movwf	SRVADRH1
		movlw	0x01
		call	EE_Read
		movwf	SRVADRL1
		return



;----- Internal EEPROM routines ------------------------------------------------


EE_Read:
		bsf	STATUS,RP0		; w=ADR
		movwf	EEADR
		bsf	EECON1,RD
		movf	EEDATA,w
		bcf	STATUS,RP0
		return

SetParm:
		call	EE_Read			; w=ADR, EEDATA0=data. Write only changes
		xorwf	EEDATA0,w
		btfsc	STATUS,Z
		return
EE_Write:		
		movf	EEDATA0,w
		bsf	STATUS,RP0
		movwf	EEDATA
		bsf	EECON1,WREN
		bcf	INTCON,GIE
		movlw	0x55
		movwf	EECON2
		movlw	0xAA
		movwf	EECON2
		bsf	EECON1,WR
		bsf	INTCON,GIE
		bcf	EECON1,WREN
EEWrite0:
		btfsc	EECON1,WR
		goto	EEWrite0
		bcf	STATUS,RP0
		return

;---------------------------------------------------------------------------


; ----- EEPROM default values


		org	0x2100


						;'10AAAAAA'1AAACDDD'
		dw	0x81			; address
		dw	0xF8



		org	0x2120

	ifdef	__BOOSTER_MANAGER
		dt	"Booster-"
		dt	"Manager."
	else
		dt	"dcc2coil"
	endif
		dt	"F.Caņada"
		dt	(__VERDAY   >> 4)  +0x30
		dt	(__VERDAY   & 0x0F)+0x30,"/"
		dt	(__VERMONTH >> 4)  +0x30
		dt	(__VERMONTH & 0x0F)+0x30,"/"
		dt	(__VERYEAR  >> 4)  +0x30
		dt	(__VERYEAR  & 0x0F)+0x30


	end

