
 
; (UHF receiver)


	ERRORLEVEL -302
	ERRORLEVEL -306

	list P=12F675
	#include P12F675.inc

;Program Configuration Register 
		__CONFIG    _CPD_OFF & _CP_OFF & _BODEN_ON & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT
	

; bank 0 RAM 

W_TMP			equ	H'20'	; storage of w before interrupt
STATUS_TMP		equ	H'21'	; status storage before interrupt

FLAG1			equ	H'23'	; interrupt flag with timer 0
TIMEOUT			equ	H'24'	; timeout counter
SW_FLAG			equ	H'25'	; switch closed flag
TRANS_COUNT		equ	H'26'	; transmission counter	
STORE1			equ	H'27'	; delay counter	
STORE2			equ	H'28'	; delay counter
RECEIVE0		equ	H'29'	; received data
RECEIVE1		equ	H'2A'	; received data
TIMER1L_WK		equ	H'2B'	; timer 1 ls byte working register
TIMER1H_WK		equ	H'2C'	; timer 1 ms byte working register
TIMER1L_VAL		equ	H'2D'	; timer 1 ls byte working register
TIMER1H_VAL		equ	H'2E'	; timer 1 ms byte working register
TIMER1L_2		equ	H'2F'	; timer 1 /2 ls byte working register
TIMER1H_2		equ	H'30'	; timer 1 /2 ms byte working register
FLAG			equ	H'31'   ; interrupt flag with timer 1
COUNT_BITS		equ	H'32'	; data count bits
ENCODE			equ	H'33'	; encode value
RECEIVEX		equ	H'34'	; stop bits
MOM_CNT0		equ	H'35'	; ms counter for momentary out period
MOM_CNT1		equ	H'36'	; ls counter for momentary out period

TIME0_CNT		equ	H'3A'	; timeout of timer 0
		
; ******************************************************************

; start at memory 0

	org		0			; reset vector
	goto	MAIN		; 
	org     4			; interrupt vector
	goto	INTERRUPT

MOM_LOOK ; momentary value lookup table
	addwf	PCL,f		; add w to program counter
	retlw	D'1'		; 0 200ms
	retlw	D'2'		; 1 400ms
	retlw	D'3'		; 2 600ms
	retlw	D'4'		; 3 800ms
	retlw	D'5'		; 4 1s
	retlw	D'6'		; 5 1.2s
	retlw	D'7'		; 6 1.4s
	retlw	D'8'		; 7 1.6s
	retlw	D'9'		; 8 1.8s
	retlw	D'10'		; 9 2.0s
	retlw	D'11'		; 10 2.2s
	retlw	D'12'		; 11 2.4s
	retlw	D'15'		; 12 3s
	retlw	D'20'		; 13 4s
	retlw	D'25'		; 14 5s
	retlw	D'30'		; 15 6s
	retlw	D'40'		; 16 8s
	retlw	D'50'		; 17 10s
	retlw	D'60'		; 18 12s
	retlw	D'75'		; 19 15s
	retlw	D'90'		; 20 18s
	retlw	D'105'		; 21 21s
	retlw	D'125'		; 22 25s
	retlw	D'135'		; 23 27s	
	retlw	D'149'		; 24 30s
	retlw	D'163'		; 25 32s
	retlw	D'177'		; 26 35s
	retlw	D'191'		; 27 38s
	retlw	D'206'		; 28 41s
	retlw	D'222'		; 29 44s
	retlw	D'255'		; 30 50s
	retlw	D'255'		; 31 50s
	retlw	D'255'		; 32 50s
	retlw	D'255'		; 33 50s

; ***********************************************************************

INTERRUPT
; Interrupt
; start interrupt by saving w and status registers  
	movwf	W_TMP		; w to w_tmp storage
	swapf	STATUS,w	; status to w
	movwf	STATUS_TMP	; status in status_tmp 
; page and banks	
	bcf		STATUS,RP0	; select memory bank 
	bcf		STATUS,RP1	; select memory bank 0

; Interrupt source
	btfss	PIR1,TMR1IF	; if timer 1 flag
	goto	TIME_0		; is it timer 0 
; preload timer for timer 1 interrupt rate
	bcf		T1CON,0		; timer 1 off	
	movf	TIMER1H_VAL,w
	addwf	TMR1H,f
	movf	TIMER1L_VAL,w
	addwf	TMR1L,f
	btfsc	STATUS,C
	incf	TMR1H,f		; increase ms byte if ls addition has carry
	bsf		T1CON,0		; timer 1 on
	bcf		PIR1,TMR1IF	; clear timer 1 overflow
	bsf		FLAG,0		; set flag bit to indicate an interrupt has occurred for data reception
	
TIME_0
	btfss	INTCON,T0IF
	goto	RECLAIM
	bcf		INTCON,T0IF
	bsf		FLAG1,0		; set flag bit to indicate an interrupt has occurred for data reception
	movf	TIME0_CNT,w	; decrease if not 0
	btfss	STATUS,Z
	decf	TIME0_CNT,f

; momentary timeout delay
	movf	MOM_CNT0,w	; ms counter
	sublw	D'191'		; for 196ms
	btfss	STATUS,C	; when negative clear
	goto	CLEAR_MOM
	incf	MOM_CNT0,f
	goto	RECLAIM

CLEAR_MOM
	clrf	MOM_CNT0
	movf	MOM_CNT1,w
	btfsc	STATUS,Z	; when zero do not decrease
	goto	RECLAIM
	decfsz	MOM_CNT1,f
	goto	RECLAIM
PROCESS
	btfsc	GPIO,5		; when momentary check for timeout
	bcf		GPIO,1		; clear output
	
; end of interrupt restore status and w 
RECLAIM
	swapf	STATUS_TMP,w; status temp storage to w
	movwf	STATUS		; w to status register
	swapf	W_TMP,f		; swap upper and lower 4-bits in w_tmp
	swapf   W_TMP,w		; swap bits and into w register
	retfie				; return from interrupt

;******************************************************************************************* 

MAIN

	bcf		STATUS,RP0	; select memory bank 0

; set inputs/outputs
	clrf	GPIO		; outputs low
	movlw	B'00000111'	; comparators off
	movwf	CMCON
	bsf		STATUS,RP0	; select memory bank 1
	movlw	B'00100000'	; pullups
	movwf	WPU
	movlw	B'00111101'	; outputs/inputs set 
	movwf	TRISIO		; port data direction register
	movlw	B'00000001'	; settings (pullups enabled) timer0/4
	movwf	OPTION_REG
; calibrate internal oscillator
	call	H'3FF'	
	movwf	OSCCAL
; analog inputs, A/D
	movlw	B'01011001'	; AN0 & AN3 analog input 
	movwf	ANSEL
	bcf		STATUS,RP0	; select memory bank 0
	movlw	B'00001100'	; channel 3 etc
	movwf	ADCON0
	bsf		ADCON0,ADON	; A/D on

; initial conditions
	movlw	B'00000000'	; port low
	movwf	GPIO	

 	movlw	H'FB'		; preload timer 1
	movwf	TIMER1H_VAL
	movwf	TMR1H
	movlw	H'FF'
	movwf	TIMER1L_VAL
	movwf	TMR1L
	bcf		T1CON,5		; /1 prescaler
	bcf		T1CON,4
	bsf		T1CON,0		; timer 1 on	

	call	DELAYms		; add small delay

; allow interrupts
ALL_INTERRUPTS
	bsf		STATUS,RP0	; select memory bank 1		
	bsf		PIE1,TMR1IE	; timer 1 overflow interrupt
	bcf		STATUS,RP0	; select memory bank 0
	bcf		PIR1,TMR1IF	; timer 1 interrupt flag
	bsf		INTCON,T0IE	; timer 0 enable
	bcf		INTCON,T0IF	; timer 0 interrupt flag
	bsf		INTCON,PEIE	; enable periperal interrupts 	 
	bsf 	INTCON,GIE	; set global interrupt enable 

; check for a high at RA5
INPUT_SIG
	btfss	GPIO,2		; when high begin to read data
	goto	INPUT_SIG
	clrf	TMR0		; clear timer
	nop
	nop
	nop
	clrf	FLAG1		; 
	movlw	D'10'		; transmission counter
	movwf	TRANS_COUNT
TRANS_LOOP
	btfss	FLAG1,0		; count flags (set in interrupt) for a 10ms transmission from transmitter 
	goto	TRANS_LOOP
	btfss	GPIO,2		; check if still high
	goto	INPUT_SIG	; not high so out
	decfsz	TRANS_COUNT,f; count
	goto	QUIETING_LOOP
	goto	LOCK_START
QUIETING_LOOP
	clrf	FLAG1
	goto	TRANS_LOOP
	
LOCK_START
; use start bits to lock the transmission frequency

	bsf		STATUS,RP0	; select memory bank 1		
	bcf		PIE1,TMR1IE	; stop timer 1 overflow interrupt
	bcf		STATUS,RP0	; select memory bank 0

	bcf		T1CON,0		; stop timer 1
	clrf	TMR1L		; clear timer 1
	clrf	TMR1H
	clrf	FLAG		; interrupt occurred flag
	bcf		PIR1,TMR1IF	; clear interrupt flag
	movlw	D'64'
	movwf	TIME0_CNT	; timer counter
	movlw	D'24'
	movwf	COUNT_BITS	; number of bits of data
	
; count with timer 1 for GP2 to go low, high, low corresponding to a 0,1 start bits

WAIT_LOW
	movf	TIME0_CNT,w
	btfsc	STATUS,Z	; timer overflow if zero
	goto	INPUT_SIG
	btfsc	GPIO,2		; wait till low 
	goto	WAIT_LOW

WAIT_HIGH
	movf	TIME0_CNT,w
	btfsc	STATUS,Z	; timer overflow if zero
	goto	INPUT_SIG
	btfss	GPIO,2		; wait for a high 
	goto	WAIT_HIGH

; start of 16ms reference period from transmitter
	bsf		T1CON,0		; timer1 on to count and check for overrange
WAIT_LOW1
	movf	TIME0_CNT,w
	btfsc	STATUS,Z	; timer overflow if zero
	goto	INPUT_SIG
	btfsc	GPIO,2		; wait till low
	goto	WAIT_LOW1

; read timer 1 and divide by 16 to get the required timer 1 offset (take from FFFF to get preload value)
; if outside range then ignore. Typically should be 1024 but 2048 to 512 allowable

	btfsc	PIR1,TMR1IF	; timer 1 overflow flag if timed out then ignore
	goto	INPUT_SIG
	bcf		T1CON,0		; stop timer 1
	movf	TMR1L,w		; read timer 1
	movwf	TIMER1L_WK	; working registers
	movf	TMR1H,w
	movwf	TIMER1H_WK

; divide by 16 to get average count between each edge
	bcf		STATUS,C
	rrf		TIMER1H_WK,f
	rrf		TIMER1L_WK,f	; /2
	bcf		STATUS,C
	rrf		TIMER1H_WK,f
	rrf		TIMER1L_WK,f	; /4
	bcf		STATUS,C
	rrf		TIMER1H_WK,f
	rrf		TIMER1L_WK,f	; /8
	bcf		STATUS,C
	rrf		TIMER1H_WK,f
	rrf		TIMER1L_WK,f	; /16

; compare with over range/under range
	movf	TIMER1H_WK,w	; ms byte
	andlw	B'11111110'		; 512 and greater
	btfsc	STATUS,Z		; if zero then value too small 
	goto	INPUT_SIG		; value off range
	movf	TIMER1H_WK,w	; ms byte
	andlw	B'11111000'		; 2048 and greater
	btfss	STATUS,Z		; if not zero then value too large 
	goto	INPUT_SIG

; take from FFFF to get preset for interrupt rate	
	movf	TIMER1H_WK,w	; ms byte
	sublw	H'FF'			; maximum
	movwf	TIMER1H_VAL
	movf	TIMER1L_WK,w	; ls byte
	sublw	H'FF'			; maximum
	movwf	TIMER1L_VAL
	btfss	STATUS,C		; if carry reduce ms byte
	decf	TIMER1H_VAL,f

; add compensation for preload losses

	movlw	D'7'			; compensate for loss during preloading at interrupt
	addwf	TIMER1L_VAL,f	; add compensation
	btfsc	STATUS,C
	incf	TIMER1H_VAL,f	; if overrange increase ms byte

; preload counter 1 to sync with transmission rate 
	movf	TIMER1H_VAL,w
	movwf	TMR1H
	movf	TIMER1L_VAL,w
	movwf	TMR1L
	clrf	FLAG		; clear flag
	bsf		T1CON,0		; timer1 on start count	

	bsf		STATUS,RP0	; select memory bank 1		
	bsf		PIE1,TMR1IE	; timer 1 overflow interrupt restart
	bcf		STATUS,RP0	; select memory bank 0

; wait for one interrupt interval
WAIT_FLG_R
	btfss	FLAG,0
	goto	WAIT_FLG_R
	clrf	FLAG

; wait for 1/2 interrupt interval to centre measurement of level on high or low transmission

; calculate 1/2 interval
	bcf		STATUS,C	; carry cleared
	rrf		TIMER1H_WK,w; divide by 2
	movwf	TIMER1H_2	; place in secondary register
	rrf		TIMER1L_WK,w
	movwf	TIMER1L_2
	
; take from FFFF to get shift in time to read data centred on a 1 or 0 value
	bcf		INTCON,GIE	; stop interrupts
	bcf		T1CON,0		; stop timer 1

	movf	TIMER1H_2,w	; ms byte
	sublw	H'FF'		; maximum
	movwf	TMR1H
	movf	TIMER1L_2,w	; ls byte
	sublw	H'FF'		; maximum
	movwf	TMR1L
	btfss	STATUS,C	; if carry reduce ms byte
	decf	TMR1H,f

	bcf		PIR1,TMR1IF	; clear interrupt flag
	bsf		T1CON,0		; run timer 1
	clrf	FLAG
	bsf		INTCON,GIE	; allow interrupts

; start of data
; shift into RECEIVE

WAIT_FLG3
	btfss	FLAG,0		; wait for interrupt flag change
	goto	WAIT_FLG3

	clrf	FLAG		; clear interrupt flag
	bcf		STATUS,C	; clear carry 
	btfsc	GPIO,2		; check level
	bsf		STATUS,C	; set carry when GPIO,2 is set
	rlf		RECEIVEX,f	; stop bits value
	rlf		RECEIVE0,f	; on or off signal
	rlf		RECEIVE1,f	; encode

	decfsz	COUNT_BITS,f
	goto	WAIT_FLG3	; continue loading bits

; check stop bits
	movf	RECEIVEX,w	; check if stops bits are correct
	xorlw	D'240'		; stop bits code
	btfss	STATUS,Z	; if status is zero value is correct
	goto	INPUT_SIG 


; received encode must equal receiver encode
; find encode value from A/D

; set TEMP 
	call	ACQUIRE_AD
	movf	ADRESH,w
	sublw	D'51'		; if less than 51 then a low setting
	btfsc	STATUS,C	; if positive then less or equal to 51
	goto	SET_1
	movf	ADRESH,w
	sublw	D'102'		; if less than 102 then a next setting
	btfsc	STATUS,C	; if positive then less or equal to 102
	goto	SET_2 
	movf	ADRESH,w
	sublw	D'153'		; if less than 153 then a mid setting
	btfsc	STATUS,C	; if positive then less or equal to 153
	goto	SET_3
	movf	ADRESH,w
	sublw	D'204'		; if less than 204 then a next setting
	btfsc	STATUS,C	; if positive then less or equal to 204
	goto	SET_4  
	movlw	D'128'		; max setting
	goto	LD_ENCODE
SET_1
	movlw	D'8'	
	goto	LD_ENCODE
SET_2
	movlw	D'16'
	goto	LD_ENCODE
SET_3
	movlw	D'32'	
	goto	LD_ENCODE
SET_4
	movlw	D'64'

LD_ENCODE
	movwf	ENCODE
	movf	RECEIVE1,w	; encoder
	xorwf	ENCODE,w
	btfss	STATUS,Z
	goto	INPUT_SIG

; check on/off signal
	movf	RECEIVE0,w	; on/off
	xorlw	D'120'		; on/off signal
	btfss	STATUS,Z
	goto	INPUT_SIG	; not equal so bypass

; check if toggle or momentary
	btfss	GPIO,5
	goto	TOGGLE

; momentary	
	bcf		STATUS,GIE	; stop interrupt
	nop 
	bsf		GPIO,1		; output on

; read momentary delay at Ch0

	bcf		ADCON0,2
	bcf		ADCON0,3	; ch0
	call	DELAYms		; wait
	call	ACQUIRE_AD
	clrf	MOM_CNT0	; ms momentary counter
	
; divide by 8 for ~156mv resolution
	bcf		STATUS,C
	rrf		ADRESH,f
	bcf		STATUS,C
	rrf		ADRESH,f
	bcf		STATUS,C
	rrf		ADRESH,f

	movlw	D'2'		; take 2 away, so start changes at 312mV,
						; compensates for trimpot resolution at low end
	subwf	ADRESH,w
	btfss	STATUS,C
	clrw				; clear if negative 

	call	MOM_LOOK	; momentary timeout lookup value
	movwf	MOM_CNT1	; momentary counter
	
	nop
	bsf		STATUS,GIE	

; channel 3 
	bsf		ADCON0,2
	bsf		ADCON0,3	; ch3
	call	DELAYms		; wait
	goto	INPUT_SIG

TOGGLE
; Alternate output
	btfss	GPIO,1		
	goto	SET1
	bcf		GPIO,1		; if high set low 	
	goto	END1
SET1
	bsf		GPIO,1		; if low set high
END1
	bsf		ADCON0,2
	bsf		ADCON0,3	; ch3
	call	DELAYms		; wait

	goto	INPUT_SIG

; ***************************************************************************	

; Subroutines

; switch delay
SW_DELAY 
	movlw	D'80'
SW_DELAY1
	movwf	SW_FLAG
SW_LOOP
	call	DELAYms
	decfsz	SW_FLAG,f	; decrease til zero
	goto	SW_LOOP
	return

; delay loop 

DELAYms
	movlw	D'23'		; delay value
DELAYX
	movwf	STORE1		; STORE1 is number of loops value
LOOP8	
	movlw	H'B0'
DELDSP
	movwf	STORE2		; STORE2 is internal loop value	
LOOP9
	decfsz	STORE2,f
	goto	LOOP9
	decfsz	STORE1,f
	goto	LOOP8
	return

; subroutine to wait for A/D conversion
ACQUIRE_AD
	bsf		ADCON0,GO_DONE	; GO/DONE bit start conversion
WAIT_CONV
	btfsc	ADCON0,GO_DONE	; conversion complete when cleared ~11 cycles
	goto	WAIT_CONV
	return



	end
