;**********************************************************************
; IR_Trans.asm
; IR-Transmission System - sender
; Version 0.1
;
; 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 at the inputs are transferred immediately.
; 
; Purpose of the system is the remote switching of lighting; 
; The receiver uses the received signal to trigger relays, which then 
; switch on two independant lights
;
;
; Usage of Pins:
; GP5 / Pin 2: Input 2
; GP4 / Pin 3: Input 1
; GP2 / Pin 5: Driver IR-LED
;
; Assuming INTOSC f=4MHz
;-------------------------------
;Stephan Asmus, Germany
;(c) stephan.asmus@t-online.de
;
;4/2009
;-------------------------------
; 01.04.2009	Initial Version, working as expected
; 
; 
; 
;**********************************************************************
	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
		address			; RC5 Address running config
		d1				; Register to hold Delayloop	
		d2				; dto.
		
        endc
; ***********Constants definitions
rc5address	equ	d'29'	;RC5-Address ("Lighting")
wtimeh		equ	0x03	;wtime =0x0394 =60sec 
						;(256 prescaler * 256 timer0 overflow)
wtimel		equ	0x94

#define in1		GPIO,4	;input 1
#define	in2		GPIO,5	;input 2
#define	irled	GPIO,2	;output to IR-LED

; **********************************************************************
; **********************************************************************
	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
	clrf	instat			;old status=new status = all off
	clrf	instat_old
	movlw	wtimeh			;timer to repeat ir signal
	movwf	w_time_h
	movlw	wtimel
	movwf	w_time_l
	movlw	rc5address		;copy constant to working address
	movwf	address


	clrf	GPIO			; Init I/O-Port by clearing the outp. latches.

	bsf		STATUS, RP0		;Switch to BANK 1
	clrf	ANSEL			;All digital I/O
	clrf	IOC				;No Interrupt on change
	movlw	b'00111000'		;0-2: Output; 4-5:Input 3:always 1
	movwf 	TRISIO
	
	;calibrating the oscillator...
	call	3FFh 			;Get the cal value
	movwf	OSCCAL 			;Calibrate it

	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'		;Prescaler to timer 0, Prescaler to 256
	movwf	OPTION_REG

	bcf		STATUS, RP0		;Back to Bank 0
	clrf	TMR0			;start timer with 0

	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
	clrf	instat		;set status to 0
	btfsc	in1			;check inputs and set status
	bsf		instat,0
	btfsc	in2
	bsf		instat,1

	movf	instat,w	;check if old and new status are the same
	xorwf	instat_old,w
	skpz
	goto	m_sendit	;values are different, so send new code
	
	movf	instat,w	;check if status (new=old) is zero
	skpnz
	goto	mainloop	;yes, is zero, so nothing to do, startover

	movf	w_time_h,w	;check if w_timer is 0 (60secs are over)
	skpz
	goto	mainloop	;not yet, nothing to do
	movf	w_time_l,w
	skpz
	goto	mainloop	;not yet, nothing to do

	;if we get here, inputs are <>0 and w_timer is 0. 
	;so we have to send the current satus again

m_sendit				;send new code
	movf	instat,w
	movwf	instat_old	;make old status the new one
	
	call	send_ir		;send new code, 1st...
	call	delay100ms	;wait 100ms
	call	send_ir		;send new code, 2nd
	call	delay100ms	;wait again
						;purpose: reveiver has to receive two identically codes
						;to change status
	movlw	wtimeh		;set w_timer to wait 60sec again (to next transfer)
	movwf	w_time_h
	movlw	wtimel
	movwf	w_time_l
	clrf	TMR0		;clear timer 0 to be nearly exact

	goto mainloop		;

; ------------------------------------------------------------------------------
; Send IR
; sends a complete status thru IR
; ------------------------------------------------------------------------------
send_ir
	bcf	INTCON,T0IE		;disable Timer0 interrupts

	;first, send the header
	;
	call	bit1		;Startbit
	call	bit1		;inverted C6 (not used, so always use 1)
	call	bit1		;toggle bit (not used here, so always use 1)

	;now send Address (i know this is crude, but it works well)
	;timing: always 5 cycles per bit (including call)
	;
	btfss	address,4	;check if a4 is set
	call	bit0		;if not, send 0...
	btfsc	address,4	
	call	bit1		;...or 1 otherwise

	btfss	address,3	;do this for a3
	call	bit0
	btfsc	address,3
	call	bit1

	btfss	address,2	;do this for a2
	call	bit0
	btfsc	address,3
	call	bit1

	btfss	address,1	;do this for a1
	call	bit0
	btfsc	address,1
	call	bit1

	btfss	address,0	;do this for a0
	call	bit0
	btfsc	address,0
	call	bit1

	;now send Data(i know this is crude, but it works well)
	;
	btfss	instat,5	;check if c5 is set
	call	bit0		;if not, send 0...
	btfsc	instat,5	
	call	bit1		;...or 1 otherwise

	btfss	instat,4	;do it for c4
	call	bit0		
	btfsc	instat,4	
	call	bit1		

	btfss	instat,3	;do it for c3
	call	bit0		
	btfsc	instat,3	
	call	bit1		

	btfss	instat,2	;do it for c2
	call	bit0		
	btfsc	instat,2	
	call	bit1		

	btfss	instat,1	;do it for c1
	call	bit0		
	btfsc	instat,1	
	call	bit1		

	btfss	instat,0	;do it for c0
	call	bit0		
	btfsc	instat,0	
	call	bit1		
						;all sent

	bsf		INTCON,T0IE	;re-enable Timer0 interrupts

	return
; ------------------------------------------------------------------------------
; Bit1
; sends a "1" Bit thru IR
; timing: 889 + 880 + 4= 1773 us (5us left for caller) -> exact 1778us for RC5
; ------------------------------------------------------------------------------
bit1
	call	halfbit0	;RC5 "1"
	call	halfbit1

	return				;4 cycles (including call)

; ------------------------------------------------------------------------------
; Bit0
; sends a "0" Bit thru IR
; timing: 901 + 877 + 4= 1782 us (+5us left for caller) -> 9us more then the
; exact value of 1778us for RC5 (less than 1% difference)
; ------------------------------------------------------------------------------
bit0
	call	halfbit1	;RC5 "0"
	call	halfbit0

	return				;4 cycles (including call)

; ------------------------------------------------------------------------------
; HalfBit1
; sends a "1" HalfBit thru IR
; timing: 2 + 32*(14+11+3) -1 + 4 = 901us
; duty-cycle: t=28us --> f=35714 Hz --> less than 1% differenz from 36kHz
; ------------------------------------------------------------------------------
halfbit1
	movlw	d'32'		;2cycles
	movwf	d2
hb1_1
	bsf	irled			;led on		3 cycles
	movlw	d'3'		;waitcounter	
	movwf	d1				
	decfsz	d1,f		;3 cycles (2 in last pass)
	goto	$-1			;
	nop
	nop
	nop					;=14 cycles on

	bcf	irled			;led off	3 cycles
	movlw	d'2'		;waitcounter	
	movwf	d1				
	decfsz	d1,f		;3 cycles (2 in last pass)
	goto	$-1			;
	nop
	nop
	nop					;=11 cycles off

	decfsz	d2,f		;3 cycles (2 at the end)
	goto	hb1_1

	return				;4 cycles (including call)
; ------------------------------------------------------------------------------
; HalfBit0
; sends a "0" HalfBit thru IR
; timing: 877us
; ------------------------------------------------------------------------------
halfbit0
						;873 cycles
	movlw	0xAE
	movwf	d1
	movlw	0x01
	movwf	d2
hb0_1
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	hb0_1
						;3 cycles
	;goto	$+1
	;nop
						;4 cycles (including call)
	return
; ------------------------------------------------------------------------------
; delay: several delay routines used in the program
; ------------------------------------------------------------------------------

delay200ms
			;199993 cycles
	movlw	0x3E
	movwf	d1
	movlw	0x9D
	movwf	d2
delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	delay_0

			;3 cycles
	goto	$+1
	nop
			;4 cycles (including call)
	return
;----
delay100ms
			;99993 cycles
	movlw	0x1E
	movwf	d1
	movlw	0x4F
	movwf	d2
delay_1
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	delay_1
			;3 cycles
	goto	$+1
	nop
			;4 cycles (including call)
	return

; *****************************************************************
	END					;'end of program''
