;**********************************************************************
; IR_REC.asm
; IR-Transmission System - receiver
; Version 0.2
;
; Transmits the status of two Inputs (Switches) to the receiver-side 
; via an infrared link (unidirectional).
; Protocol is RC5-alike and uses a 36kHz carrier
; Carrier is implemented in software
;
; Transfer is repeated every 60 sec if status is unchanged, but
; only if at least one input is <>0.
; Purpose of this is that the receiver defaults to "off" within
; 70secs if no IR signal is received. (fault, fog, snow etc...)
; Status changes are transferred immediately.
; 
; Purpose of the system is the remote switching of illumination; 
; The receiver uses the received signal to trigger relays, which then 
; switch on two independant lights
;
;
; Usage of Pins:
; GP5 / Pin 2: Output 2
; GP4 / Pin 3: Output 1
; GP2 / Pin 5: Input from IR-receiver IC
;
; Assuming INTOSC f=4MHz
;-------------------------------
;Stephan Asmus, Germany
;(c) stephan.asmus@t-online.de
;
;4/2009
;-------------------------------
; 02.04.2009	Initial Version, running
; 22.04.2009	Mainloop modified to appropriate reaction to other 
;				(not korrekt) signals from other remotes
; 
; 
; 
;**********************************************************************
	LIST   P=PIC12F675

	#include <p12f675.inc>	; processor specific variable definitions 

errorlevel  -302			;No bank select warnings

	__CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT & _CPD_OFF & _MCLRE_OFF


;***** VARIABLE DEFINITIONS
w_temp		EQU	0x20	; used for context saving Register "W"
status_temp	EQU	0x21	; used for context saving Register "Status"

	cblock 0x22			; define a series of variables.
		instat			; status of inputs
		instat_old		; previous status	
		w_time_h		; timer-variable highbyte
		w_time_l		; timer lowbyte
		w_time_h_o		; timer old value 
		w_time_l_o		; timer old value
		address			; RC5 Address received
		error			; bit 0 holds Errorflag (=1 on receive-error)
		count			; bitcounter for receive
		d1				; Register to hold Delayloop	
		d2			; dto.
		
	endc
; ***********Constants definitions
rc5address	equ	d'29'	;RC5-Address ("Lighting")
wtimeh		equ	0x04	;wtime =0x042D =70sec (256 prescaler * 256 timer0 overflow)
wtimel		equ	0x2D

#define		out1	GPIO,4	;output 1
#define		out2	GPIO,5	;output 2
#define		irin	GPIO,2	;input from IR-receiver

; **********************************************************************
; **********************************************************************
	ORG		0x000		; processor reset vector
	goto	init		; go to beginning of program


	ORG		0x004		; interrupt vector location
	movwf	w_temp		; save off current W register contents
	swapf	STATUS,W	; move status register into W register
	clrf	STATUS		; select Bank 0
	movwf	status_temp	; save off contents of STATUS register
						; There's no need to save PCLATH since
						; i'll stick to Bank 0 of program memory.

	btfsc	INTCON,T0IF	; If Timer 0 caused the interrupt, handle it.
	call	timer0

	swapf	status_temp,w	; retrieve copy of STATUS register
	movwf	STATUS		; restore pre-isr STATUS register contents
	swapf	w_temp,f
	swapf	w_temp,w	; restore pre-isr W register contents
	retfie				;return from interrupt

; ------------------------------------------------------------------------------
;       Timer 0 Interrupt handler.
;       Timer 0 has overflowed
; ------------------------------------------------------------------------------
timer0
	movf	w_time_l,f	;decrement 16 bit timer
	skpnz
	decf	w_time_h,f
	decf	w_time_l,f

	BCF		INTCON,T0IF		; Clear Timer0 flag and continue.
		return


; ------------------------------------------------------------------------------
; START OF CODE to initialize the processor (Variables, I/O ...)
; ------------------------------------------------------------------------------
init
	bcf		STATUS,RP0	; Select Bank 0

	;initialise variables here...
	clrf	instat		
	clrf	instat_old
	movlw	wtimeh
	movwf	w_time_h
	movlw	wtimel
	movwf	w_time_l
	clrf	error

	clrf	GPIO		; Initialize I/O-Port by clearing the output latches.

	bsf		STATUS, RP0	;Switch to BANK 1
	clrf	ANSEL		;All digital I/O
	clrf	IOC			;No Interrupt on change
	movlw	b'00001100'	;0-1: Output; 2-3:Input 4-5:Output
	movwf 	TRISIO
	
	;calibrating the oscillator...
	call 	3FFh 		;Get the cal value
	movwf 	OSCCAL 		;Calibrate

	bcf		STATUS, RP0		;Back to BANK 0

; ------------------------------------------------------------------------------
; START OF CODE to initialize Timer 0
; Set up Timer 0 to generate interrupts every x us.  
; ------------------------------------------------------------------------------

	clrwdt				
	clrf	TMR0	
	;---
	bsf		STATUS, RP0	;Switch to BANK 1
	movlw 	b'11010111'	;timer 0, Prescaler to 256
	movwf 	OPTION_REG

	bcf		STATUS, RP0	;Back to Bank 0
	clrf 	TMR0

	clrf	INTCON		;initialise Interrupts
	bsf		INTCON,T0IE	;enable Timer0 interrupts
	bsf 	INTCON,GIE	;enable global interrupts
						;now timer 0 fires

; ******************************************************************************
; MAIN, here we go...
; 
; ******************************************************************************

mainloop
	btfss	irin		;check input if low then signal received
	goto	m_rec_1

	movf	w_time_h,w	;check if w_timer is 0 (70secs are over)
	skpz
	goto	mainloop	;not yet, nothing to do
	movf	w_time_l,w
	skpz
	goto	mainloop	;not yet, nothing to do

	;we get here if w_timer is 0
	
	clrf	instat		;clear status
	clrf	instat_old
	bcf		out1			;clear relays
	bcf		out2

	movlw	wtimeh		; and reload timer
	movwf	w_time_h
	movlw	wtimel
	movwf	w_time_l
	
	goto	mainloop

m_rec_1					;receive 1st message
	movf	w_time_h,w	;save old timer value (needed if error occurs)
	movwf	w_time_h_o
	movf	w_time_l,w
	movwf	w_time_l_o
	;now timer-var can be used for other purposes...
	
	clrf	error		;assuming no error
	call	receive_ir	;receive 1st datagram
	btfsc	error,0		;check if error detected
	goto	restore_tmr	;on error goto beginning with old timer value
	
	movf	instat,w	;save 1st message
	movwf	instat_old

	clrf	TMR0		;load timer for 1 sec delay
	movlw	0x00		;(maximum time to receive 2nd datagram)
	movwf	w_time_h
	movlw	0x10
	movwf	w_time_l

m_rec_10
	btfss	irin		;check input if low then signal received
	goto	m_rec_2

	movf	w_time_h,w	;check if w_timer is 0 (1sec is over)
	skpz
	goto	m_rec_10	;not yet, continue waiting
	movf	w_time_l,w
	skpz
	goto	m_rec_10	;not yet, continue waiting

	goto	restore_tmr	;no 2nd Datagram received within one second, 
						;so start over with old timer value

m_rec_2
	clrf	error		;assuming no error
	call	receive_ir	;receive 2nd datagram
	btfsc	error,0		;check if error detected
	goto	restore_tmr	;on error goto beginning with old timer value
	
	movf	instat,w	;check if two messages are the same
	xorwf	instat_old,w
	btfss	STATUS,Z	;zero=1 if they are identical
	goto	restore_tmr	;not equal, so start at the beginning with old timer

	btfss	instat,0	;check if bit0 = out1 is set
	bcf		out1		;clear output if not set
	btfsc	instat,0	;check if bit0 = out1 is clear
	bsf		out1		;set output if bit is set

	btfss	instat,1	;the same for Bit1 = out 2
	bcf		out2
	btfsc	instat,1
	bsf		out2

	movlw	wtimeh		; and reload timer
	movwf	w_time_h
	movlw	wtimel
	movwf	w_time_l
	clrf	TMR0		

	goto	mainloop	;ready, start over
	
restore_tmr
	movf	w_time_h_o,w
	movwf	w_time_h
	movf	w_time_l_o,w
	movwf	w_time_l
	
	goto	mainloop

; ------------------------------------------------------------------------------
; receive IR
; receives a complete status thru IR, sets data to instat, 
; checks correct address and sets errorflag if errors were found
; ------------------------------------------------------------------------------
receive_ir
	;we get here if 1st bit (startbit 1) is half over
	call 	delay444us	;to 3/4 of 1st startbit
	btfsc	irin		;check if it is still low
	goto	rec_err		;not low, was spike, probably

	call	delay889us	;to 1/4 of 2nd startbit
	call	wft			;wait for next high-low transition

	call	delay444us	;to 3/4 of 2nd startbit
	btfsc	irin		;check if it is still low
	goto	rec_err		;not low, so it is an error

	call	delay889us	;to 1/4 of toggle-bit (which is always 1 here)
	call	wft			;wait for next high-low transition

	call	delay444us	;to 3/4 of togglebit
	btfsc	irin		;check if it is still low
	goto	rec_err		;not low, so it is an error
	
	btfsc	error,0		;errors so far?
	goto	rec_end		;yes, then end immediately

	;here starts address transfer
	clrf	address		;clear old address
	movlw	d'5'		;5 Address bits
	movwf	count		;load counter

rec_addr
	call	delay889us	;to 1/4 of 1st address-bit
	rlf		address,f	;shift register to correct pos.
	call	wft			;wait for next transition high-low
						; or low-high
	call	delay444us	;to 3/4 of current bit
	btfsc	irin		;check ir
	bcf		address,0	;is set, so bit is 0
	btfss	irin
	bsf		address,0	;is clear, so bit is 1

	btfsc	error,0		;errors so far?
	goto	rec_end		;yes, then end immediately

	decfsz	count,f		;all bits read?
	goto	rec_addr	;no, next bit

	
	;here starts data transfer
	clrf	instat		;clear old status
	movlw	d'6'		;5 Address bits
	movwf	count		;load counter

rec_data
	call	delay889us	;to 1/4 of 1st data-bit
	rlf		instat,f	;shift register to correct pos.
	call	wft			;wait for next transition high-low
						; or low-high
	call	delay444us	;to 3/4 of current bit
	btfsc	irin		;check ir
	bcf		instat,0	;is set, so bit is 0
	btfss	irin
	bsf		instat,0	;is clear, so bit is 1

	btfsc	error,0		;errors so far?
	goto	rec_end		;yes, then end immediately

	decfsz	count,f		;all bits read?
	goto	rec_data	;no, next bit


	;all received, so do some checks...
	
	movf	address,w	;received address to w
	andlw	0x1f		;mask 6 bits
	xorlw	rc5address
	skpz				;if zero then they are equal
	goto	rec_err		;else it is an error

	movf	instat,w	;received data to w
	andlw	b'11111100'	;mask
	skpz				;if zero then no data in bits 3..7
	goto	rec_err		;else it is an error

	;if we go here all seems to be well...

	goto	rec_end
rec_err
	bsf		error,0		;set error flag
rec_end
	call	delay889us	;to be sure we are past last bit
	return	

; ------------------------------------------------------------------------------
; waitfortransition. Wait for a transition to accour on IR-In Port
; either high->low or low->high.
; timeout of ~500ms is implemented
; ------------------------------------------------------------------------------
wft
	clrf	TMR0		;load timer for 0,5 sec delay
	movlw	0x00		;(maximum time to wait for transition)
	movwf	w_time_h
	movlw	0x08
	movwf	w_time_l


	btfsc	irin		;is port set (1)?
	goto	wait_highlow	;yes, wait for high to low trans.
	btfss	irin		;is port clear (0)?
	goto	wait_lowhigh	;yes, wait for low to high trans.
	goto	wft_end		

wait_highlow
	btfss	irin
	goto	wft_end

	movf	w_time_l,w	;check if w_timer is 0 (0,5sec is over)
	skpz
	goto	wait_highlow	;not yet, continue waiting
	goto	wft_error

wait_lowhigh
	btfsc	irin
	goto	wft_end

	movf	w_time_l,w	;check if w_timer is 0 (0,5sec is over)
	skpz
	goto	wait_lowhigh	;not yet, continue waiting
	goto	wft_error
	
wft_error
	bsf		error,0		;set error flag

wft_end
	return
; ------------------------------------------------------------------------------
; delay: several delay routines used in the program
; ------------------------------------------------------------------------------
delay889us				;half bit RC5
						;888 cycles
	movlw	0xB1
	movwf	d1
	movlw	0x01
	movwf	d2
d889_1
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	d889_1
			;1 cycle
	nop

	return
;-----
delay444us				;quarter bit RC5

						;442 cycles
	movlw	0x93
	movwf	d1
d444_1
	decfsz	d1, f
	goto	d444_1

			;2 cycles
	goto	$+1

	return

; *****************************************************************
	END					;'end of program''
