;*****************************************************************************
;   lights.asm               LED Lighting System
;*****************************************************************************
;                Bruce Abbott   bhabbott@paradise.net.nz
;
;          for Microchip PIC 12F629 or 12F675.
;
;=============================================================================
;                             Summary of Changes
;
; 2005/03/15 V0.0 - Created from RXDecode.asm
; 2005/08/06 V0.1 - Two strobe lights, adjustable flash sequence.
; 2005/12/17 V0.2 - 3 position switch selects Landing, Lights On, Lights Off.
; 2008/08/30 V0.3 - Active High outputs (driving external transistors)
; -----------------------------------------------------------------------------

        radix     dec

#DEFINE version  "0.3"

;#DEFINE __12C508	; enable if processor not specified elsewhere
;#DEFINE __12F675	; MPLAB users should use menu <Configure/Select Device>

;#DEFINE NO_OSCCAL 	; enable if OSCCAL value was erased!


	ifdef	  __12C508
	PROCESSOR PIC12C508
        INCLUDE   <P12C508.inc>
	__CONFIG  _MCLRE_OFF&_CP_OFF&_WDT_ON&_IntRC_OSC
	else
        PROCESSOR PIC12F675
        INCLUDE   <P12F675.inc>
	__CONFIG  _MCLRE_OFF&_CP_OFF&_WDT_ON&_BODEN_ON&_INTRC_OSC_NOCLKOUT
	endif



	errorlevel 0,-305,-302

#DEFINE	TIME 4		; number of servo pulses per strobe state
;#DEFINE ACTIVE_LOW	; active Low outputs (direct LED drive)


; Bit definitions for the GPIO register and the TRIS register

#DEFINE STRB1	 5    ; pin 2   Strobe Light #1
#DEFINE STRB2	 4    ; pin 3   Strobe Light #2
#DEFINE SERVO	 3    ; pin 4   servo pulse input
#DEFINE LAND	 2    ; pin 5   Landing Lights
#DEFINE LED1	 1    ; pin 6   Fixed lighting (eg. Navigation Lights)
#DEFINE LED2	 0    ; pin 7   More Fixed Lighting (eg. Cabin Lights)
;
#DEFINE TrisBits (1<<SERVO) ; servo pulse is an input

		ifdef	ACTIVE_LOW	; Active Low outputs (direct LED drive)

#DEFINE	ALL_ON		0
#DEFINE	ALL_OFF		1<<STRB1|1<<STRB2|1<<LAND|1<<LED1|1<<LED2
#DEFINE	STRB1_ON	bcf	GPIO,STRB1
#DEFINE	STRB1_OFF	bsf	GPIO,STRB1
#DEFINE	STRB2_ON	bcf	GPIO,STRB2
#DEFINE	STRB2_OFF	bsf	GPIO,STRB2
#DEFINE	LAND_ON		bcf	GPIO,LAND
#DEFINE	LAND_OFF	bsf	GPIO,LAND
#DEFINE	LED1_ON		bcf	GPIO,LED1
#DEFINE	LED1_OFF	bsf	GPIO,LED1
#DEFINE	LED2_ON		bcf	GPIO,LED2
#DEFINE	LED2_OFF	bsf	GPIO,LED2

		else		; active High outputs (use driver transistors)

#DEFINE	ALL_ON		1<<STRB1|1<<STRB2|1<<LAND|1<<LED1|1<<LED2
#DEFINE	ALL_OFF		0
#DEFINE	STRB1_ON	bsf	GPIO,STRB1
#DEFINE	STRB1_OFF	bcf	GPIO,STRB1
#DEFINE	STRB2_ON	bsf	GPIO,STRB2
#DEFINE	STRB2_OFF	bcf	GPIO,STRB2
#DEFINE	LAND_ON		bsf	GPIO,LAND
#DEFINE	LAND_OFF	bcf	GPIO,LAND
#DEFINE	LED1_ON		bsf	GPIO,LED1
#DEFINE	LED1_OFF	bcf	GPIO,LED1
#DEFINE	LED2_ON		bsf	GPIO,LED2
#DEFINE	LED2_OFF	bcf	GPIO,LED2
		endif


; -------------------- OPTIONS ------------------------
;   no wakeup (12C508)
;   weak pullups disabled
;   Timer 0 source internal
;   Prescaler to Timer 0, divide by 256.
;

 ifdef	__12C508
#DEFINE OptionBits B'11000111'
 else
#DEFINE OptionBits B'10000111'
 endif


;=========================================================================
; Macro for generating short time delays
;
NO_OP           MACRO   count
NO_OP_COUNT     SET     count
                WHILE   NO_OP_COUNT>1
		goto	$+1		; 2 clocks
NO_OP_COUNT     SET     NO_OP_COUNT-2
                ENDW
		IF	NO_OP_COUNT
		nop			; 1 clock
		ENDIF
                ENDM

;===========================================================================
; Macro to create offsets for variables in RAM
;
		ifdef	__12C508
ByteAddr	SET	7
		else
ByteAddr	SET	32		; user RAM starts here
		endif

BYTE            MACRO     ByteName
ByteName        EQU       ByteAddr
ByteAddr	SET       ByteAddr+1
                ENDM

; ==========================================================================
;                 RAM Variable Definitions
;
        BYTE	Flags		; various boolean flags

        BYTE	ServoCount	; servo pulse length. 1~255 = 0.01~2.55mS

	BYTE	Timer		; delay timer for strobes

	BYTE	Strobe1		; strobe 1 sequencer
	BYTE	Strobe2		; strobe 2 sequencer

	BYTE	Glitches	; number of consecutive bad servo pulses

	BYTE	Temp1
	BYTE	Temp2



; flag values
;
#DEFINE WATCH		0	; Watchdog timed out
#DEFINE	STROBE1		1	; strobe 1 on
#DEFINE	STROBE2		2	; strobe 2 on
#DEFINE LANDING		3	; Landing lights on



;****************************************************************************
;                                Code
;
	ORG	0
	goto	ColdStart

;-------------------------- version string ----------------------------------

	ifdef	__12F629
	dt	"NAVL629"
	endif
	ifdef	__12F675
	dt	"NAVL675"
	endif
	dt	"--V"
	dt	version
	dt	"--"

;============================================================================

ColdStart:
	bcf	Flags,WATCH
	btfss	STATUS,NOT_TO		; copy Watchdog timeout flag
	bsf	Flags,WATCH

; get oscillator calibration value and use it to fine-tune clock frequency.
; 12C508/9 has value in W at startup, 12F629/75 gets it from RETLW at 0x3ff.

	ifdef	__12C508
	ifdef	NO_OSCCAL
	movlw	0x90			; replace with value for your PIC!
	endif
	movwf	OSCCAL			; set oscillator calibration
	else
	bsf	STATUS,RP0		; register bank 1 (12F629/75)
	call	0x3ff			; get OSCCAL value
	movwf	OSCCAL			; set oscillator calibration
        bcf	STATUS, RP0		; register bank 0
        endif

; set options

	ifdef	__12C508
        movlw   OptionBits
        option
	else
	bsf	STATUS,RP0		; register bank 1 (12F629/75)
        movlw   OptionBits
        movwf	OPTION_REG
	bcf	STATUS,RP0		; register bank 0
	endif

;========================================================================
; initialise I/O registers

        movlw	ALL_OFF			; all outputs will be off
	movwf	GPIO
	ifdef	__12C508
        movlw	TrisBits
        TRIS    GPIO			; set I/O pin directions (12F509)
	else
	bsf	STATUS,RP0		; register bank 1 (12F629/75)
        movlw	TrisBits
        movwf	TRISIO			; set I/O pin directions (12F629/75)
	ifdef	ANSEL
	clrf	ANSEL			; disable analog inputs (12F675)
	endif
	bcf	STATUS,RP0		; register bank 0
	ifdef	CMCON
	movlw	b'00000111'
        movwf	CMCON			; Comparator off
	endif
	endif

; CPU specific stuff done, now we can start the main program!

        goto	Main

SeqStrobe1:
		ifdef	PCLATH
		clrf	PCLATH
		endif
		movf	Strobe1,w
		addwf	PCL
		retlw	b'00000001'
		retlw	b'00000000'
		retlw	b'00000001'
		retlw	b'00000000'
		retlw	b'00000001'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'10000000'	; bit 7 set = end of sequence

SeqStrobe2:
		ifdef	PCLATH
		clrf	PCLATH
		endif
		movf	Strobe2,w
		addwf	PCL
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000001'
		retlw	b'00000000'
		retlw	b'00000001'
		retlw	b'00000000'
		retlw	b'00000001'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'00000000'
		retlw	b'10000000'	; bit 7 set = end of sequence

;-------------------------------------------------------------------------------
;                    Millisecond Delay Timer
;-------------------------------------------------------------------------------
; Input: W = number of milliseconds to wait (max 256mS)
;

dx1k:		movwf	Temp1
_dx1k1:		movlw	(1000-5)/5
		movwf	Temp2
_dx1k2:		clrwdt			; avoid watchdog timeout
		nop
		decfsz	Temp2		; wait 1mS
		goto	_dx1k2
		decfsz	Temp1
		goto	_dx1k1
		retlw	0


;*******************************************************************************
;				   Main
;*******************************************************************************


Main:		btfsc	Flags,WATCH	; did the watchdog timeout ?
        	goto    MainLoop	; oops! try to keep going ...

		clrf	Flags		; clear all flags

		movlw	250		; wait 250mS for power to stabilise
		call	dx1k

		movlw	ALL_ON		; all LEDs on
		movwf	GPIO
		movlw	250		; wait 250mS
		call	dx1k
		movlw	ALL_OFF		; all LEDs off
		movwf	GPIO

		movlw	TIME
		movwf	Timer

		clrf	Strobe1
		clrf	Strobe2

		clrf	Glitches

MainLoop:	clrf	TMR0		; start timer
WaitLow:	clrwdt			; avoid watchdog timeout
		btfsc	TMR0,7		; timer reached 33mS ?
		goto	NoSignal
		btfsc	GPIO,SERVO	; wait for no servo pulse
		goto	WaitLow

		clrf	TMR0		; start timer
waitpulse:	clrwdt			; avoid watchdog timeout
		btfsc	TMR0,7		; timer reached 33mS ?
		goto	NoSignal
		btfss	GPIO,SERVO	; wait for start of servo pulse
		goto	waitpulse

		clrf	ServoCount
measure:	clrwdt			;1
		incf	ServoCount	;2    count = pulse width (* 10uS)
		skpnz			;3    > 2.55mS ?
		goto	BadPulse	;4    Yes, error
		NO_OP	3		;5~7
		btfsc	GPIO,SERVO	;8    servo pulse finished ?
		goto	measure		;9,10 no, continue measuring

		movlw	70
		subwf	ServoCount,w	; less than 0.7mS ?
		skpc
		goto	BadPulse	; yes, bad pulse

		bcf	Flags,LANDING	; assume landing lights are off
		movlw	130
		subwf	ServoCount,w	; less than 1.3mS ?
		skpc
		bsf	Flags,LANDING	; yes, landing lights on

		movlw	170
		subwf	ServoCount,w	; less than 1.7mS ?
		skpc
		goto	power_on	; yes, power on

power_off	movlw	ALL_OFF
		movwf	GPIO		; All lights OFF
		goto	GoodPulse

power_on:	LED1_ON			; fixed lights ON
		LED2_ON


landing_lights:	btfss	Flags,LANDING
		LAND_OFF		; Landing lights OFF
		btfsc	Flags,LANDING
		LAND_ON			; Landing lights ON

strobes:	decfsz	Timer		; time for next strobe change?
		goto	GoodPulse	; no, done

		movlw	TIME
		movwf	Timer		; restart timer

		call	SeqStrobe1	; get Strobe 1 state
		movwf	Temp1
		incf	Strobe1		; next state
		btfsc	Temp1,7		; end of sequence ?
		clrf	Strobe1		; restart sequence
		bcf	Flags,STROBE1
		btfsc	Temp1,0		; strobe 1 to be on ?
		bsf	Flags,STROBE1

		call	SeqStrobe2	; get Strobe2 state
		movwf	Temp1
		incf	Strobe2		; next state
		btfsc	Temp1,7		; end of sequence ?
		clrf	Strobe2		; restart sequence
		bcf	Flags,STROBE2
		btfsc	Temp1,0		; strobe 2 to be on ?
		bsf	Flags,STROBE2

		btfsc	Flags,STROBE1
		STRB1_ON		; flash strobe 1
		btfss	Flags,STROBE1
		STRB1_OFF

		btfsc	Flags,STROBE2
		STRB2_ON		; flash strobe 2
		btfss	Flags,STROBE2
		STRB2_OFF

GoodPulse:	tstf	Glitches
		skpnz
		goto	done
		decf	Glitches	; another good pulse received
		goto	done

BadPulse:	incf	Glitches	; another bad pulse!
		movlw	10
		subwf	Glitches,w
		skpc			; too many bad pulses?
		goto	done
		decf	Glitches

NoSignal:	movlw	ALL_OFF
		movwf	GPIO		; turn off all outputs

done:		goto	MainLoop






;---------- Oscillator Calibration Subroutine (12F629/75 only) --------------

		ifdef	__12F675
		org	0x3ff
		retlw	0x90	; replace with oscal value for your PIC!
		endif

		END


