;************************************************************************
; Philips RC5(X) Infrared protocol implemented with 18 pin Microchip 	*
; microcontrollers from 16F / 16C series.								*
;																		*
; 16F627(A), 16F628(A), 16F648A, 16F716, 16C83, 16F83, 16C84, 16F84(A)	*
; 16F818, 16F819, 16F87, 16F88											*
;   many other, one time programmable 18 pin 16Cxxx device may be used	*
;	   - 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 clock used												*
;																		*
; - Visible LED output - flashes as many timas as the selected page num.*
; - Handles 16 addresses : 0x10 .. 0x1F									*
; - Handles 48 commands  : 0x00 .. 0x2F	- using 3 command pages			*
; - Handles 64 commands  : 0x00 .. 0x3F	- using 4 command pages			*
; - Handles 16 buttons with up to 4 + 1 extra:							*
;    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						*
;    Option1 button : Not used yet										*
;    Option2 button : Not used yet										*
;    Option3 button : Not used yet										*
;																		*
; - Command codes can be translated using on chip EEPROM memory			*
;		Can be changed without reprogramming the program memory			*
;		Not avaible on 16F716 and 16Cxxx devices with no data EEProm	*
;																		*
; - Two command set on controllers having EEprom capacity >64			*
;       Hold down Address button when powering up to activate set #1	*
;																		*
; - Matrix can be extended with two more raws (8 buttons)				*
;		Driven by RA2 and RA3	not implemented yet						*
;	  With devices having 4MHz on board oscillator						*
;		ex. 16F628(A), 16F648A, 16F87, 16F88							*
;       even more two extra raws (8 buttons)							* 
;		Driven by RA6 and RA7	not implemented yet						*
;************************************************************************
;																		*
;	Pins:																*
;		PORTA:															*
;			0: IR LED drive output										*
;			1: Visual LED output										*
;			2: Not used													*
;			3: Not used													*
;			4: Address button input										*
;			5: MCLR, not used											*
;			6-7: Oscillator 4 Mhz, can be freed up using internal osc.	*
;																		*
;		PORTB:															*
;			0..3: Keyboard column select outputs						*
;			4..7: Keyboard raw    sense  inputs							*
;																		*
;************************************************************************


	ifdef	__16F627
		list	p=16f627             ; 16F627 can be used
		#include <p16f627.inc>       ; processor specific variable definitions
EEPROM_SIZE	EQU	128
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
    endif
	ifdef	__16F627A
		list	p=16f627A            ; 16F627A can be used
		#include <p16f627A.inc>      ; processor specific variable definitions
EEPROM_SIZE	EQU	128
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
    endif
	ifdef	__16F628
		list	p=16f628             ; 16F628 can be used
		#include <p16f628.inc>       ; processor specific variable definitions
EEPROM_SIZE	EQU	128
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
    endif
	ifdef	__16F628A
		list	p=16f628A            ; 16F628A can be used
		#include <p16f628A.inc>      ; processor specific variable definitions
EEPROM_SIZE	EQU	128
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
    endif
	ifdef	__16F648A
		list	p=16f648A            ; 16F648A can be used
		#include <p16f648A.inc>      ; processor specific variable definitions
EEPROM_SIZE	EQU	256
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
	endif
	ifdef	__16F716
		list	p=16f716             ; 16F716 can be used
		#include <p16f716.inc>       ; processor specific variable definitions
EEPROM_SIZE	EQU	0
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC
    endif
	ifdef	__16C83
		list	p=16C83              ; 16C83 can be used
		#include <p16c83.inc>        ; processor specific variable definitions
EEPROM_SIZE	EQU	64
	__config 0x3FF1
    endif
	ifdef	__16F83
		list	p=16f83              ; 16F83 can be used
		#include <p16f83.inc>        ; processor specific variable definitions
	__config 0x3FF1
	endif
	ifdef	__16C84
		list	p=16C84              ; 16C84 can be used
		#include <p16c84.inc>        ; processor specific variable definitions
EEPROM_SIZE	EQU	64
	__config 0x3FF1
    endif
	ifdef	__16F84
		list	p=16f84              ; 16F84 can be used
		#include <p16f84.inc>        ; processor specific variable definitions
EEPROM_SIZE	EQU	64
	__config 0x3FF1
    endif
	ifdef	__16F84A
		list	p=16f84A             ; 16F84A can be used
		#include <p16f84.inc>        ; processor specific variable definitions
EEPROM_SIZE	EQU	64
	__config 0x3FF1
    endif
	ifdef	__16F818
		list	p=16F818             ; 16F88 can be used
		#include <p16F818.inc>       ; processor specific variable definitions
EEPROM_SIZE	EQU	128
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _LVP_OFF
    endif
	ifdef	__16F819
		list	p=16F819             ; 16F88 can be used
		#include <p16F819.inc>       ; processor specific variable definitions
EEPROM_SIZE	EQU	128
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _LVP_OFF
    endif
	ifdef	__16F87
		list	p=16f87              ; 16F87 can be used
		#include <p16f87.inc>        ; processor specific variable definitions
EEPROM_SIZE	EQU	256
		__CONFIG _CONFIG1, _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
		__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF 
    endif
	ifdef	__16F88
		list	p=16F88              ; 16F88 can be used
		#include <p16F88.inc>        ; processor specific variable definitions
EEPROM_SIZE	EQU	256
		__CONFIG _CONFIG1, _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
		__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF 
    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	VersionMajor	0x02
#define	VersionMinor	0x03

	__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
		addrbutton				; Temp store of PORTA  -  Bit 4: address button
		commandset				; Command set to use if EEProm's capacity >64 
	endc

	if commandset > 0x2F
		error "Too many variable used"
	endif

		org		0x000
POWERON
		CLRWDT
		clrf	commandset
		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		PORTA,0			;1
		call	RET				;4
		call	RET				;4
		call	RET				;4
		BCF		PORTA,0			;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

SendOnePacket
		SUBWF	comcode,W
		BTFSC	STATUS,C
SendOnePacket0
		BCF		PORTA,1			; Turn on  visible LED
		CALL	SendIrPacket
		BSF		PORTA,1			; Turn off visible LED
		return

SendTwoPackets
		SUBWF	comcode,W
		BTFSC	STATUS,C
SendTwoPackets0
		BCF		PORTA,1			; Turn on  visible LED
		CALL	SendIrPacket
		BSF		PORTA,1			; Turn off visible LED

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   	PORTA,0			; Turn off IR LED

		GOTO  	Delay113ms		; Delay 64 Bits @ 1,78ms = 113ms

Main
;;;		CLRWDT					; Done at reset
;;;		clrf	CommandPage

	if EEPROM_SIZE>64
		btfss	PORTA,4			; If address button down
		bsf		commandset,6	;  use command set #1
	endif

		BSF		STATUS,RP0
		MOVLW	0x81 			; Pullups off, Prescaler 1:4 and int. Clock	
		MOVWF	OPTION_REG
		MOVLW	0xF0
		MOVWF	TRISA			; Bit 3..0 Output 3: Extra 1 row drive, 2: Extra 0 row drive, 0: Visible Led 0: IR Led
		MOVWF	TRISB			; Bit 7..4 Key sense (port change hw) 3..0 Column drive
		BCF		STATUS,RP0
		MOVWF	PORTB			; Enable all raws
		CLRF	PORTA			; Turn on Visible Led
		CALL	Delay226ms
		btfsc	commandset,6
		bsf		PORTA,1
		CALL	Delay226ms
		btfsc	commandset,6
		bcf		PORTA,1
		CALL	Delay226ms
								; address = default address
		MOVLW	DefaultAddress
		MOVWF	address			; Clear toggle bit

		CLRF	comset			; Command page 0

	ifdef	CMCON				; If controller has comparator - disable it
	 if ((CMCON & 0x180)!=(PORTA & 0x180))
	  if ((CMCON & 0x100)==0x100)
		bsf		STATUS,RP1
	  else
		bcf		STATUS,RP1
	  endif
	  if ((CMCON & 0x80)==0x80)
		bsf		STATUS,RP0
	  else
		bcf		STATUS,RP0
	  endif
	 endif
		movlw	7
		movwf	CMCON
	 if ((CMCON & 0x180)!=(PORTA & 0x180))
		clrf	STATUS
	 endif
	endif

	ifdef	ADCON1				; If controller has A/D - disable it
	 if ((ADCON1 & 0x180)!=(PORTA & 0x180))
	  if ((ADCON1 & 0x100)==0x100)
		bsf		STATUS,RP1
	  else
		bcf		STATUS,RP1
	  endif
	  if ((ADCON1 & 0x80)==0x80)
		bsf		STATUS,RP0
	  else
		bcf		STATUS,RP0
	  endif
	 endif
		movlw	7
		movwf	ADCON1
	 if ((ADCON1 & 0x180)!=(PORTA & 0x180))
		clrf	STATUS
	 endif
	endif

	ifdef	ANSEL				; If controller has A/D - disable it
	 if ((ANSEL & 0x180)!=(PORTA & 0x180))
	  if ((ANSEL & 0x100)==0x100)
		bsf		STATUS,RP1
	  else
		bcf		STATUS,RP1
	  endif
	  if ((ANSEL & 0x80)==0x80)
		bsf		STATUS,RP0
	  else
		bcf		STATUS,RP01
	  endif
	 endif
		clrf	ANSEL
	 if ((ANSEL & 0x180)!=(PORTA & 0x180))
		clrf	STATUS
	 endif
	endif

		bsf		PORTA,1			; Turn off both Leds

MainLoop

		BSF		INTCON,RBIE		; Enable Port Change Int
		BCF   	INTCON,RBIF		; 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,RBIE		; Disable Port Change Int
		BCF   	INTCON,RBIF		; Clear Port Change Int Flag

								; Scan of keymatrix		Normal raws			Extra raw
								; PORTB.4==0 	 		Buttons  0.. 3		Option 1
		CLRW
		BTFSS	PORTB,4
		CALL	FindRow
								; PORTB.5==0			Buttons  4.. 7		Option 2
		MOVLW	.4
		BTFSS	PORTB,5
		CALL	FindRow
								; PORTB.6==0			Buttons  8..11		Option 3
		MOVLW	.8
		BTFSS	PORTB,6
		CALL	FindRow
								; PORTB.7==0			Buttons 12..15		Shift
		MOVLW	.12
		BTFSS	PORTB,7
		CALL	FindRow

		BTFSC	comcode,7		; Test for key detected
		GOTO	KeyUp			; No -> Wait for all key up

		MOVLW	0x10
		SUBWF	comcode,W		;
		BTFSS	addrbutton,4	; Test for Address key down
		BTFSC	STATUS,C
		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	0x4C
		BTFSS 	STATUS,Z
		GOTO	NormalButton	; No -> Test for normal button

								; Shift button pressed - roll command page
		MOVLW	0x10
		ADDWF	comset,f
		MOVF	comset,W
	ifdef	UsePage4
		XORLW	0x40
	else
		XORLW	0x30
	endif
		BTFSC 	STATUS,Z
		CLRF	comset
								; Sign active set on visible led

		call	FlashLED0
		MOVLW	.16
		call	FlashLED
		MOVLW	.32
		call	FlashLED
	ifdef	UsePage4
		MOVLW	.48
		call	FlashLED
	endif
		goto	KeyUp			; Wait for all keys up

FlashLED
		SUBWF	comset,W
		BTFSC 	STATUS,C
FlashLED0
		BCF		PORTA,1
		CALL	Delay113ms
		BSF		PORTA,1
		goto	Delay113ms

NormalButton					; Test for normal buttons
		MOVLW	.16
		SUBWF	comcode,W
		BTFSC	STATUS,C
		GOTO	KeyUp
								; Normal button pressed

		ANDWF	comset,W		; Combine comcode and comset
		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	.16
		call	SendTwoPackets
		MOVLW	.32
	  ifdef	UsePage4
		call	SendTwoPackets
		MOVLW	.48
	  endif
		call	SendOnePacket
	else
								; Send command only 1 time
		call	SendOnePacket0
	endif

KeyUp							; Wait for all keys up
;	movlw	.10
;	CALL	Delayms
		MOVLW	0xF0			; Enable all raws
		MOVWF	PORTB
		ANDWF	PORTB,W
		XORLW	0xF0
		BTFSS 	STATUS,Z
		goto	KeyUp
;	BTFSS 	STATUS,Z
;	GOTO	SendPackets

NoKeyDown
		movlw	0x20
		xorwf	address,f		; Invert toggle bit

		MOVLW	.50
		CALL	Delayms

		GOTO	MainLoop

FindRow							; Find a key in raw number in w
		movwf	comcode			; Store raw number
		MOVWF	FSR
		MOVLW 	.1				; Init mask
		MOVWF	PORTB			; Drive only one column

FindRaw_loop

		MOVF	PORTB,W			; Check if all columns high
		andlw	0xF0
		XORLW	0xF0
		BTFSC 	STATUS,Z
		goto	RawFound		; Key found - comcode holds it's code

		INCF	comcode,f		; Move comcode

		BCF		STATUS,C
		RLF		PORTB,f			; Shift column drive

		movf	PORTB,w
		andlw	0x0F
		BTFSS	STATUS,Z		; All columns read
		GOTO	FindRaw_loop	; Loop for all columns

		MOVLW	0x40			; There was no key down in matrix
		IORWF	FSR,W
		MOVWF	comcode			; Must be an extra key
RawFound
		movlw	0xF0
		movwf	PORTB
		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

	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

; 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

	endif

	END


