	list b=4
;************************************************************************
; Philips RC5(X) Infrared protocol implemented with 14 pin Microchip 	*
; microcontrollers from 16F / 16C series.								*
;																		*
;    Filename:	    RC5x.asm                                        	*
;    StartDate:     27/12/2010                                        	*
;    LastUpdate:    27/12/2010											*
#define	VersionMajor	0x01	;										*
#define	VersionMinor	0x02	;										*
;                                                                     	*
; 16F684 / 16F630														*
;	   - 9 byte RAM from 0x20 - 0x28 used only							*
;	   - no more than 256 word of program memory needed					*
;	   - EEProm lookup table is only an option							*
;	   - 4MHz internal clock used										*
;																		*
; - Visible LED output - flashes as many timas as the selected page num.*
; - Handles 16 addresses : 0x10 .. 0x1F									*
; - Handles 64 commands  : 0x00 .. 0x3F									*
; - Handles 24 buttons including:										*
;    Address button : Hold down and press button 0..15 to set address	*
;		New address will be 0x10 | (button_code & 0x0F)					*
;    Shift   button : Each press rolls command page						*
;																		*
; - Command codes can be translated using on chip EEPROM memory			*
;		Can be changed without reprogramming the program memory			*
;																		*
; - Two command set on controllers having EEprom capacity >64			*
;       Hold down Address button when powering up to activate set #1	*
;																		*
;************************************************************************
;																		*
;	Pins:																*
;		PORTA:															*
;			0..5: Keyboard raw    sense  inputs							*
;																		*
;		PORTC:															*
;			0..3: Keyboard column select outputs						*
;			4: Visible LED drive output	active LOW						*
;			5: IR LED drive output		active HIGH						*
;																		*
;************************************************************************

	errorlevel -302

	ifdef	__16F684
	list	p=16f684             ; 16F684 can be used
	#include <p16f684.inc>       ; processor specific variable definitions
	__CONFIG _CP_OFF & _CPD_OFF & _WDT_OFF & _BOD_ON& _PWRTE_ON & _MCLRE_OFF & _INTOSCIO & _IESO_OFF & _FCMEN_OFF
EEPROM_SIZE = .256
	endif

	ifdef	__16F630
	list	p=16f630             ; 16F630 can be used
	#include <p16f630.inc>       ; processor specific variable definitions
	__CONFIG _CP_OFF & _CPD_OFF & _WDT_OFF & _BODEN & _PWRTE_ON & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT
#define	CMCON0	CMCON
EEPROM_SIZE = .128
	endif

	ifndef	PORTA
		error	"Invalid processor type selected"
	endif

FOSC	EQU	.4000000			; 4.00 MHz clock frequency used

								; Default address to use afther power up
#define	DefaultAddress		0x1D
#define	UsePage4				; Uncomment this line to use 4 command page mode 
#define	Send5Times				; Comment this line to send a command only once

    if EEPROM_SIZE >= .64
#define	UseLookUpTable			; Comment this line to send raw key codes
	endif

#define	ADDRESS_KEY	.23
#define	SHIFT_KEY	.22

	__idlocs		0xC000 | (VersionMajor << 8) | (VersionMinor)

	cblock	0x20
		comcode					; Command code to send
		address					; Address to send  -  bit 5: toggle bit
		cntr					; Delay counter
		bitcnt					; Bit counter
		comset					; Current page number 0..2 or 0..3
		commandset				; Command set to use if EEProm's capacity >64 
	endc

	if commandset > 0x2F
		error "Too many variable used"
	endif

		org		0x000
POWERON
		clrf	commandset
		movlw	0x07
		movwf	CMCON0
		GOTO	Main

		org		0x004
IT
		retfie

Delay226ms
		call	Delay113ms

Delay113ms
		movlw	.113
Delayms
		MOVWF	cntr
lDel1
		clrf	FSR
lDel2
		NOP  
		DECFSZ	FSR,1
		GOTO	lDel2
		DECFSZ	cntr,1
		GOTO	lDel1
		RETURN

SendBits
		movwf	FSR				; Save data to send
		movlw	.6
		movwf	bitcnt			; Save bit count
SendBits_loop
		btfsc	FSR,5			; Test bit
		CALL	SendBit1		; if bit==1 send a one
		btfss	FSR,5
		CALL	SendBit0		; if bit==0 send a zero
		RLF		FSR,f			; All bits to send are in the FSR, C is don't care
		decfsz	bitcnt,f		; Loop for all bits
		GOTO	SendBits_loop
		return

SendBit1
		call	SendPause

SendBit
		movlw	.32
		movwf	cntr
SendBit_loop
		BSF		PORTC,5			;1
		call	RET				;4
		call	RET				;4
		call	RET				;4
		BCF		PORTC,5			;1
		call	RET				;4
		call	RET				;4
		goto	$+1				;2
		decfsz	cntr,f			;1
		GOTO	SendBit_loop	;2
RET
		return

SendBit0
								;  890us with 36kHz modulation
								; 27.7us Periodtime - 32 Impulses
		call	SendBit

SendPause
		MOVLW	.32
		movwf	cntr
SendPause_loop
		call	RET				;4
		call	RET				;4
		call	RET				;4
		call	RET				;4
		call	RET				;4
		call	RET				;4
		decfsz	cntr,f			;1
		GOTO	SendPause_loop	;2
		return


; RC5 InfraRed Command Packet
;
;		Packet length: 24.899 ms, repeation time: 113.792 ms 		
;
;		Bit 1: 889uS - no pulses, 889us - 32 pulses 
;		Bit 0: 889uS - 32 pulses, 889us - no pulses 
;
;		 S  s   T   A4  A3  A2  A1  A0  C5  C4  C3  C2  C1  C0
;		|1 |s  |tg |a4 |a3 |a2 |a1 |a0 |c5 |c4 |c3 |c2 |c1 |c0 |
;
;		RC5  version s: second start bit : 1
;		RC5X version s: inverted bit 6 of command : !c6
;
;		a4..a0:	address
;		d6..d0: command
;		tg: toggle bit: send the same bit for repeated packet sending
;						send opposite bit for a new command

	ifdef	Send5Times
SendOnePacket
		SUBWF	comcode,W
		BTFSC	STATUS,C
	endif

SendOnePacket0
		BCF		PORTC,4			; Turn on  visible LED
		CALL	SendIrPacket
		BSF		PORTC,4			; Turn off visible LED
		return

	ifdef	Send5Times
SendTwoPackets
		SUBWF	comcode,W
		BTFSC	STATUS,C
SendTwoPackets0
		BCF		PORTC,4			; Turn on  visible LED
		CALL	SendIrPacket
		BSF		PORTC,4			; Turn off visible LED
	endif

SendIrPacket
		CALL	SendBit1		; First  Startbit = 1
		btfss	comcode,6
		CALL	SendBit1		; Second Startbit = inv of bit 6 of command code 
		btfsc	comcode,6
		CALL	SendBit0

		movf	address,w		; toggle bit and 5 Bit address
		call	SendBits

		movf	comcode,w		; 6 Bit command code
		call	SendBits
		BCF   	PORTC,5			; Turn off IR LED

		GOTO  	Delay113ms		; Delay 64 Bits @ 1,78ms = 113ms

Main
;;;		clrf	commandset		; Done at reset
;;;		movlw	0x07
;;;		movwf	CMCON0

		BSF		STATUS,RP0		; Bank1
	ifdef	ANSEL
		clrf	ANSEL
	endif

		bcf		STATUS,RP0		; Bank0

		movf	PORTA,w
		movwf	FSR

		BSF		STATUS,RP0		; Bank1
		MOVLW	0x01 			; Pullups on, Prescaler 1:4 and int. Clock	
		MOVWF	OPTION_REG
		MOVLW	0xFF
		MOVWF	TRISA			; Bit 5..0 Raw sense input
		clrf	TRISC			; Bit 5..0 Column drive

	ifdef	OCSCON
;		movlw	0x60			; 4MHz is the default clock
;		movwf	OSCCON
	endif

	ifdef	OSCCAL
		call	0x03FF
		movwf	OSCCAL
	endif

		BCF		STATUS,RP0
		clrf	PORTC			; Enable all raws, Turn on Visible Led

		movlw	0x40
		btfss	FSR,0			; If address button down
		addwf	commandset,f	;  use command set #1
		btfss	FSR,1			; If address button down
		addwf	commandset,f	;  use command set #2
		btfss	FSR,2			; If address button down
		addwf	commandset,f	;  use command set #3
		btfss	FSR,3			; If address button down
		addwf	commandset,f	;  use command set #4

		CALL	Delay226ms
		btfsc	commandset,6
		bsf		PORTC,4
		CALL	Delay226ms
		btfsc	commandset,6
		bcf		PORTC,4
		CALL	Delay226ms
								; address = default address
		MOVLW	DefaultAddress
		MOVWF	address			; Clear toggle bit

		CLRF	comset			; Command page 0

		bsf		PORTC,4			; Turn off both Leds

MainLoop

		BSF		INTCON,RAIE		; Enable Port Change Int
		BCF   	INTCON,RAIF		; Clear Port Change Int Flag 
		SLEEP					; Goto sleep
		NOP  					; PortB change will wake up
;		MOVF	PORTA,W			; Get a copy of PORTA
;		MOVWF	addrbutton		; Bit 4 == 0 means Address button down
		BSF		comcode,7		; Command code invalid
		BCF   	INTCON,RAIE		; Disable Port Change Int
		BCF   	INTCON,RAIF		; Clear Port Change Int Flag

								; Scan of keymatrix		Normal raws
								; PORTA.0==0 	 		Buttons  0.. 4
		CLRW
		BTFSS	PORTA,0
		CALL	FindRow
								; PORTA.1==0			Buttons  4.. 7
		MOVLW	.4
		BTFSS	PORTA,1
		CALL	FindRow
								; PORTA.2==0			Buttons  8..11
		MOVLW	.8
		BTFSS	PORTA,2
		CALL	FindRow
								; PORTA.3==0			Buttons 12..15
		MOVLW	.12
		BTFSS	PORTA,3
		CALL	FindRow
								; PORTA.4==0			Buttons 16..19
		MOVLW	.16
		BTFSS	PORTA,4
		CALL	FindRow
								; PORTA.5==0			Buttons 20..23
		MOVLW	.20
		BTFSS	PORTA,5
		CALL	FindRow

		BTFSC	comcode,7		; Test for key detected
		GOTO	KeyUp			; No -> Wait for all key up

		MOVLW	ADDRESS_KEY
		XORWF	comcode,W		;
		BTFSS	STATUS,Z
		GOTO	NotAddress		; No -> Test for Shift

		movf	comcode,w
		xorwf	address,w
		andlw	0x0f
		btfsc	STATUS,Z
		goto	KeyUp

		MOVLW	0x0F			; Combine key code and address:4
		ANDWF	comcode,W
		IORLW	0x10
		MOVWF	address			; Set new address, clear toggle bit

		GOTO	KeyUp			; No -> Wait for all key up

NotAddress
								; Test for shift button
		MOVF	comcode,W
		XORLW	SHIFT_KEY
		BTFSS 	STATUS,Z
		GOTO	NormalButton	; No -> Test for normal button

								; Shift button pressed - roll command page
		MOVLW	.24
		ADDWF	comset,f
		MOVF	comset,W
		XORLW	.72
		BTFSC 	STATUS,Z
		CLRF	comset
								; Sign active set on visible led

		call	FlashLED0
		MOVLW	.24
		call	FlashLED
		MOVLW	.48
		call	FlashLED
		goto	KeyUp			; Wait for all keys up

FlashLED
		SUBWF	comset,W
		BTFSC 	STATUS,C
FlashLED0
		BCF		PORTC,4
		CALL	Delay113ms
		BSF		PORTC,4
		goto	Delay113ms

NormalButton					; Test for normal buttons
		MOVLW	.24
		SUBWF	comcode,W
		BTFSC	STATUS,C
		GOTO	KeyUp
								; Normal button pressed

		MOVF	comset,W
		IORWF	comcode,f

								; If comcode < 64 send it
		MOVLW	0x40
		SUBWF	comcode,W
		BTFSC 	STATUS,C
		goto	KeyUp			; Wait for all keys up

	ifdef	UseLookUpTable
		movf	comcode,w		; Read look-up table
	  if EEPROM_SIZE > .64
		iorwf	commandset,w
	  endif
		call	EpromRead
		movwf	comcode			; 0x80..0xFF means No command assigned for this key 
		btfsc	comcode,7
		goto	KeyUp
	endif

SendPackets
	ifdef	Send5Times
								; Send command for 5 times
		call	SendTwoPackets0
		MOVLW	.24
		call	SendTwoPackets
		MOVLW	.48
		call	SendOnePacket
	else
								; Send command only 1 time
		call	SendOnePacket0
	endif

		movlw	0x20
		xorwf	address,f		; Invert toggle bit

KeyUp							; Wait for all keys up
		MOVLW	0xF0			; Enable all raws
		ANDWF	PORTC,F
		MOVF	PORTA,W
		andlw	0x3F
		XORLW	0x3F
		BTFSS 	STATUS,Z
		goto	KeyUp

NoKeyDown
		MOVLW	.50
		CALL	Delayms

		GOTO	MainLoop

FindRow							; Find a key in raw number in w
		movwf	comcode			; Store raw number
		MOVWF	FSR
		MOVLW 	0x11			; Init mask
		MOVWF	PORTC			; Drive only one column

FindRaw_loop

		MOVF	PORTA,W			; Check if all columns high
		andlw	0x3F
		XORLW	0x3F
		BTFSC 	STATUS,Z
		goto	RawFound		; Key found - comcode holds it's code

		INCF	comcode,f		; Move comcode

		BCF		STATUS,C
		RLF		PORTC,w			; Shift column drive
		xorwf	PORTC,f
		andlw	0x0F
		xorwf	PORTC,f
		movf	PORTC,w
		BTFSS	STATUS,Z		; All columns read
		GOTO	FindRaw_loop	; Loop for all columns

RawFound
		movlw	0x0F0
		iorwf	PORTC,f
		RETURN

  ifdef	UseLookUpTable
;******************************************************************************
; Reads keycode from EEProm memory at w - returns with Bank0 selection

EpromRead
	if  ((EEADR & 0x80)!=(PORTA & 0x80))
	  if ((EEADR & 0x80)==0x80)
		bsf		STATUS,RP0
	  else
		bcf		STATUS,RP0
	  endif
	endif
	if  ((EEADR & 0x100)!=(PORTA & 0x100))
	  if ((EEADR & 0x100)==0x100)
		bsf		STATUS,RP1
	  else
		bcf		STATUS,RP1
	  endif
	endif

		movwf	EEADR

	if  ((EECON1 & 0x80)!=(EEADR & 0x80))
	  if ((EECON1 & 0x80)==0x80)
		bsf		STATUS,RP0
	  else
		bcf		STATUS,RP0
	  endif
	endif
	if  ((EECON1 & 0x100)!=(EEADR & 0x100))
	endif

		bsf		EECON1,RD		; read character from EEProm

	if ((EEDATA & 0x80)!=(EECON1 & 0x80))
	  if ((EEDATA & 0x80)==0x80)
		bsf		STATUS,RP0
	  else
		bcf		STATUS,RP0
	  endif
	endif
	if ((EEDATA & 0x100)!=(EECON1 & 0x100))
	  if ((EEDATA & 0x100)==0x100)
		bsf		STATUS,RP1
	  else
		bcf		STATUS,RP1
	  endif
	endif

		movf	EEDATA,w		; Get next character

	if ((PORTA & 0x180)!=(EEDATA & 0x180))
	  if ((PORTA & 0x80)==0x80)
		bsf		STATUS,RP0
	  else
		bcf		STATUS,RP0
	  endif
	endif
	if ((PORTA & 0x180)!=(EEDATA & 0x180))
	  if ((PORTA & 0x100)==0x100)
		bsf		STATUS,RP1
	  else
		bcf		STATUS,RP1
	  endif
	endif
		return
  endif

  ifdef	OSCCAL
	org		0x03FF
	retlw	0xA8
  endif

	org		0x2100

; Key code - command code look up table

; 0x00 - 0x7F : Command code to send for 
; 0x80 - 0xFF : No command assigned for this key 

; Command Set #0

; Page 1
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F
; Page 2
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F
; Page 3
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F
; Page 4
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F

	if EEPROM_SIZE > .64
; Command Set #1

; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F
	endif

	if EEPROM_SIZE > .128
; Command Set #2

; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F

; Command Set #3

; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F
; Button   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
	DE	0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F
	endif

	END


