; PIC12C509 program to sense off/on state from a
; radio control receiver output.  Outputs active 
; if longer than 1.5mS; inactive if less.
;
; Inputs  - R/C pulse input		GP0	;positive 1-2mS pulse every 25mS
; 	  - Loss-of-signal control	GP4	;1 to turn on if signal lost
;	  - reset			GP3	;tie high
; Outputs - switch			GP5	;on when active
;	  - switch inverted		GP1	;off when active
;	  - flash			GP2	;1 second flashing when active

    LIST    P=12C509
    #include <P12C509.INC>
;   #include <jonsinc.h>
    ;__CONFIG   _CP_OFF & _WDT_OFF & _MCLRE_ON & _IntRC_OSC
    RADIX DEC

GP0		equ 0
GP1		equ 1
GP2		equ 2
GP3		equ 3
GP4		equ 4
GP5		equ 5

DEBUG		equ 0		;1 to shorten delays; 0 otherwise
AVERAGE_COUNT	equ 20						;number of 0.025mS passes to average
FLASH_COUNT	equ 40						;flash threshold, number of 0.025mS pulses
NO_SIGNAL_COUNT	equ 66						;number of 0.015mS delays in 1 second
START_THRESHOLD	equ AVERAGE_COUNT * 2				;beginning count threshold
OFF_THRESHOLD	equ START_THRESHOLD - ( AVERAGE_COUNT / 2 )	;below this, turn output off
ON_THRESHOLD	equ START_THRESHOLD + ( AVERAGE_COUNT / 2 )	;above this, turn output on
#if ( DEBUG == 1 )
  DELAY15MS	equ 255		;short delay for debug
  DELAY500US	equ 255
  OK_COUNT	equ 1
#else  
  DELAY15MS	equ 21		;normal delays
  DELAY500US	equ 248
  OK_COUNT	equ 133
#endif

state_count  	equ 0x10	;RAM 
pulse_count	equ 0x12
flash_counter	equ 0x13	;number of 0.025 (nominal) pulses received so far
flash_state	equ 0x14	;1 if flash output on, 0 if off
no_signal_count	equ 0x15	

	ORG 0x3ff
	movlw	0x40		;RC calibration value
	ORG 0x400		
	dw	_CP_OFF & _WDT_ON & _MCLRE_ON & _IntRC_OSC	;configuration fuses for PEP programmer

	ORG     0x0000
start:	movwf	OSCCAL		;load RC cal value
	movlw	b'11011001'
	tris	GPIO		;set GP1, GP2, GP5 for output, rest are inputs
	movlw	0x85		;/64 clk prescaler, weak pullups, int clk
	option			;load option reg from W reg
	bcf	GPIO,GP5	;turn off switch
	bsf	GPIO,GP1	;turn on inverted switch
	call	CLEAR_COUNTS
	clrf	flash_counter
	
	;BLINK FLASHER FOR TWO SECONDS TO SIGNAL THAT PROCESSOR IS OK
	movlw	OK_COUNT	;do 30mS square wave for two seconds
	movwf	pulse_count
s1:	bsf	GPIO,GP2	;turn on flasher output
	call	DELAY_15MS	;delay
	bcf	GPIO,GP2	;turn off flasher output
	call	DELAY_15MS	;delay another 0.5 second
	decfsz	pulse_count
	goto	s1		;not done yet
			
	;LOSS-OF-SIGNAL CONTROL - LOCKED HIGH FOR AT LEAST ONCE SECOND
loop:	movlw	NO_SIGNAL_COUNT	;get count
	movwf	no_signal_count	;save it
l_1:	movlw	DELAY15MS	;15mS count
	movwf	TMR0		;set timer
l_2:	movf	TMR0,W		;get count, set flags
	clrwdt			;keep watchdog alive
	btfss	GPIO,GP0	;jump if still high
	goto	h_0		;found low, jump to find rising edge
	btfss	STATUS,Z	;skip if zero flag is set from timer
	goto	l_2		;timer not zero yet, loop
	decfsz	no_signal_count	;decrement, skip out if zero
	goto	l_1		;do again
	goto	lost		;signal lost
	
	;LOSS-OF-SIGNAL CONTROL - LOCKED LOW FOR AT LEAST ONCE SECOND	
h_0:	movlw	NO_SIGNAL_COUNT	;get count
	movwf	no_signal_count	;save it
d_1:	movlw	DELAY15MS	;15mS count
	movwf	TMR0		;set timer
d_2:	movf	TMR0,W		;get count, set flags
	clrwdt			;keep watchdog alive
	btfsc	GPIO,GP0	;jump if still low? 
	goto	d_3		;found rising edge, jump (approx every 25mS)
	btfss	STATUS,Z	;skip if zero flag is set from timer
	goto	d_2		;timer not zero yet, loop
	decfsz	no_signal_count	;decrement, skip out if zero
	goto	d_1		;do again
	goto	lost		;signal lost

	;FOUND RISING EDGE OF PULSE
d_3:	call	DELAY_500US	;wait 0.5mS
	btfss	GPIO,GP0	;is pulse still there?
	goto	loop
	call	DELAY_500US	;delay another 1.0mS
	call	DELAY_500US

	;CHECK AT 50% POINT IN PULSE TIME
	btfss	GPIO,GP0	;skip if pulse still there
	goto	a0		;pulse is gone
	incf	state_count,F	;request turn on
	goto	cont1
a0:	decf 	state_count,F	;request turn off
cont1:	incf	pulse_count,F	;increment the number of 0.025mS counts
	call	DELAY_15MS
	incf	flash_counter
	
	btfss	GPIO,GP5	;skip if output is on
	goto	cont3		;output is off
	movlw	FLASH_COUNT	;output is on, get flash count
	subwf	flash_counter,W	;check if above 40 counts yet
	btfss	STATUS,C	;skip if enough passes to toggle flash state
	goto	cont2		;not enough passes
	movlw	b'00000100'	;mask on GP2
	xorwf	GPIO,F		;toggle GP2
cont3:	clrf	flash_counter
	
	;AVERAGING AND VALID THRESHOLDS
cont2:	movlw	AVERAGE_COUNT	;check if enough pulses to average
	subwf	pulse_count,W	;pulse_count minus average_count
	btfss	STATUS,C	;jump if enough passes (no borrow)
	goto 	loop		;not enough passes yet
	movlw	OFF_THRESHOLD	;check if state_count is below off threshold
	subwf	state_count,W	;state_count minus off_threshold
	btfss	STATUS,C	;jump if no borrow, maybe above on threshold ?
	goto 	toff		;below off threshold, turn output off
	movlw	ON_THRESHOLD	;check if state_count is above on threshold
	subwf	state_count,W	;state_count minus on_threshold
	btfss	STATUS,C	;jump if no borrow, it's above on threshold
	goto 	loop		;not reached either off or on threshold yet, loop
	
	;TURN ON OR OFF
ton:	bsf	GPIO,GP5	;turn on output switch
	bcf	GPIO,GP1	;turn off inverted switch
	call	CLEAR_COUNTS
	goto	loop
toff:	bcf	GPIO,GP5	;turn off output
	bsf	GPIO,GP1	;turn on inverted switch
	bcf	GPIO,GP2	;turn off flasher output
	call	CLEAR_COUNTS
	goto	loop	
lost:	btfss	GPIO,GP4	;jump if want to turn on if signal lost
	goto	toff
	goto	ton

;===============================
DELAY_15MS:
	movlw	DELAY15MS	;15mS count
	goto	dhm_1
DELAY_500US:
	movlw	DELAY500US	;500uS count
dhm_1:	movwf	TMR0		;set timer
dhm_2:	movf	TMR0,W		;get count, set flags
	clrwdt			;keep watchdog alive
	btfss	STATUS,Z	;skip if zero flag is set
	goto	dhm_2		;loop
	retlw	0
    
;===============================
CLEAR_COUNTS:
	movlw	START_THRESHOLD
	movwf	state_count	;preset start threshold
	clrf	pulse_count
	retlw	0

	END


