;*******************************************************************
;* TSA5511 & HD44780 based PLL * (C) 2009 Pira CZ * http://pira.cz *
;*******************************************************************
;* Please read the license text before use this file !!! * V. 1.3  *
;*******************************************************************

        LIST P=16F627A
        include <p16f627a.inc>

 	__CONFIG _DATA_CP_OFF & _CP_OFF & _WDT_ON & _BOREN_ON & _PWRTE_ON & _INTOSC_OSC_NOCLKOUT & _MCLRE_OFF & _LVP_OFF

;*************************************************************
;********** USER DEFINED PARAMETERS **************************
;*************************************************************

#DEFINE	INIFRQ	D'8750'		;initial frequency [10*kHz]
#DEFINE BOTFRQ	D'8750'		;bottom frequency [10*kHz]
#DEFINE	TOPFRQ	D'10790'	;top frequency [10*kHz]
#DEFINE	STEP	D'10'		;tuning step [10*kHz]
#DEFINE	PLLXTL	D'640'		;PLL xtal frequency [10*kHz]
;Note: STEP should be an integral multiple of (PLLXTL/64)

#DEFINE USRCHR1	' '		;user defined characters
#DEFINE USRCHR2	'P'		;showed in bottom-right corner
#DEFINE USRCHR3	'i'		;of 16x2 display
#DEFINE USRCHR4	'r'
#DEFINE USRCHR5	'a'
#DEFINE USRCHR6	' '
#DEFINE USRCHR7	'C'
#DEFINE USRCHR8	'Z'

;***** VARIABLE DEFINITIONS **********************************

BYTE	EQU	0x70
FREQL	EQU	0x71
FREQH	EQU	0x72
INDBITS	EQU	0x73
W_TEMP	EQU	0x74
S_TEMP	EQU	0x75

DISP	EQU	0x20
WORK	EQU	0x21
WORK2	EQU	0x22
KLCNT	EQU	0x23
INLCNT	EQU	0x24
DIVL	EQU	0x25
DIVH	EQU	0x26
MODL	EQU	0x27
MODH	EQU	0x28
KEYCNT	EQU	0x2A
KEYW	EQU	0x2B
KEYPER	EQU	0x2C
INKEY	EQU	0x2D
TUNCNTL	EQU	0x2E
TUNCNTH	EQU	0x2F

#DEFINE ZERO    INDBITS,0
#DEFINE I2CERR  INDBITS,1
#DEFINE KEYFL   INDBITS,2
#DEFINE	TND	INDBITS,3
#DEFINE	INLOCK	INDBITS,4
#DEFINE	KEYLOCK	INDBITS,5
#DEFINE	CURRENT	INDBITS,6

#DEFINE	I2CPWR	PORTB,0
#DEFINE CLK	PORTB,1
#DEFINE DOWN	PORTB,6
#DEFINE UP	PORTB,7
#DEFINE SCL	PORTA,3
#DEFINE SDA	PORTA,4
#DEFINE	LED	PORTA,1
#DEFINE LOOPLCK PORTA,2

;******************* START ***********************************

	ORG     0x000             ; processor reset vector
	goto    main              ; go to beginning of program

	ORG     0x004             ; interrupt vector location
	movwf   W_TEMP            ; save off current W register contents
	movf	STATUS,W          ; move status register into W register
	movwf	S_TEMP       	  ; save off contents of STATUS register

	; isr code can go here or be located as a call subroutine elsewhere

	movf    S_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

;******************** INI ***********************************

main
	bsf	CMCON,CM0
	bsf	CMCON,CM1
	bsf	CMCON,CM2

	bcf	SCL
	bcf	SDA
	bcf	LED
	bcf	LOOPLCK
	clrf	KEYCNT
	clrf	INKEY
	clrf	INDBITS	
	movlw	D'5'
	movwf	INLCNT

	bsf	STATUS,RP0	;bank 1

	movlw	0x03
	movwf	PCON		;48 kHz osc. (It's more than enough! Fuck off Windows Vista!)

	movlw	B'11000000'
	movwf	TRISB

	movlw	B'11100001'
	movwf	TRISA

	bcf	OPTION_REG,7
	movlw	B'1111'
	iorwf	OPTION_REG,F

	bcf	STATUS,RP0	;back to bank 0

	call	dispini
	call	usrchr
	call	read		;read FREQ from EEPROM

	movf	FREQH,W		;FREQ ok?
	sublw	HIGH(TOPFRQ)
	btfss	STATUS,C
	goto	ini1

	movlw	HIGH(BOTFRQ)
	subwf	FREQH,W
	btfsc	STATUS,C
	goto	ini2
ini1
	movlw	HIGH(INIFRQ)
	movwf	FREQH
	movlw	LOW(INIFRQ)
	movwf	FREQL
ini2	
	movlw	D'27'
	movwf	TUNCNTH

	call	tuning
	call	tuning

	movlw	D'0'
	movwf	DISP
	call	address
	call	frq2lcd
	movlw	' '
	movwf	DISP
	call	char		
	movlw	'M'
	movwf	DISP
	call	char		
	movlw	'H'
	movwf	DISP
	call	char		
	movlw	'z'
	movwf	DISP
	call	char		

	movlw	0x48
	movwf	DISP
	call	address
	movlw	USRCHR1
	movwf	DISP
	call	char		
	movlw	USRCHR2
	movwf	DISP
	call	char		
	movlw	USRCHR3
	movwf	DISP
	call	char		
	movlw	USRCHR4
	movwf	DISP
	call	char		
	movlw	USRCHR5
	movwf	DISP
	call	char		
	movlw	USRCHR6
	movwf	DISP
	call	char		
	movlw	USRCHR7
	movwf	DISP
	call	char		
	movlw	USRCHR8
	movwf	DISP
	call	char		

;******************* LOOP **********************************

loop

;------- in-lock detection ----------

	movlw	D'15'
	subwf	TUNCNTH,W
	btfss	STATUS,C
	goto	loop1

	movf	TUNCNTL,F
	btfss	STATUS,Z
	goto	loop1
	
	decf	INLCNT,F
	btfss	STATUS,Z
	goto	loop1

	movlw	D'5'
	movwf	INLCNT

	btfsc	INLOCK
	goto	loop1

	call	tuned
	btfss	TND
	goto	loop1
	bsf	INLOCK

	bcf	CURRENT
	call	frq2pll

	movlw	0x41
	movwf	DISP
	call	address
	movlw	D'2'
	movwf	DISP
	call	char 
	bsf	LED
	bsf	LOOPLCK

;------------ buttons ---------------

loop1
	incf	KEYCNT,F
	movlw	D'126'
	btfsc	KEYCNT,7
	movwf	KEYCNT

	movlw	0x00
	btfss	UP
	movlw	0x01
	btfss	DOWN
	movlw	0x02

	movwf	KEYW
	movf	KEYW,F
	btfss	STATUS,Z
	goto	kpress
nokey
	clrf	KLCNT
	movlw	D'2'
	subwf	KEYCNT,W
	btfss	STATUS,C
	goto	keyend

	btfsc	KEYFL
	goto	nokey2
	movlw	D'124'
	movwf	KEYPER
	movlw	D'126'
	movwf	KEYCNT
	goto	keyend
nokey2
	bcf	KEYFL
	clrf	KEYCNT
	goto	keyend
kpress
	movf	KEYCNT,W
	subwf	KEYPER,W
	btfsc	STATUS,C
	goto	keyend		

	movlw	D'10'
	btfsc	KEYFL
	movwf	KEYPER
	clrf	KEYCNT
	bsf	KEYFL
	movf	KEYW,W
	movwf	INKEY
	btfsc	KEYLOCK
	goto	keyend
	clrf	TUNCNTH
	clrf	TUNCNTL
keyend
	clrwdt
	btfss	KEYLOCK
	goto	mkkey
	clrf	INKEY
	incfsz	KLCNT,F
	goto	loop2
	bcf	KEYLOCK	
	movlw	0x40
	movwf	DISP
	call	address
	movlw	' '
	movwf	DISP
	call	char 
	bcf	KEYFL
	movlw	D'124'
	movwf	KEYPER
	clrf	KEYCNT
	clrf	INKEY
mkkey	
	movf	INKEY,F
	btfsc	STATUS,Z
	goto	loop2
	btfsc	INKEY,0
	call	tuneup
	btfsc	INKEY,1
	call	tunedn
	clrf	INKEY
	bcf	INLOCK

;----------- keylock ------------------------

loop2
	movlw	D'80'
	subwf	TUNCNTH,W
	btfss	STATUS,Z
	goto	loop3
	btfsc	KEYLOCK
	goto	loop3
	bsf	KEYLOCK
	movlw	0x40
	movwf	DISP
	call	address
	movlw	D'0'
	movwf	DISP
	call	char 

;----------- counter ------------------------

loop3
	incf	TUNCNTL,F
	btfsc	STATUS,Z
	incf	TUNCNTH,F
	movlw	D'253'
	btfsc	STATUS,Z
	movwf	TUNCNTH

;----------- tuning ------------------------

	movlw	D'4'
	subwf	TUNCNTH,W
	btfss	STATUS,Z
	goto	loop4
	incfsz	TUNCNTL,W
	goto	loop4
	bcf	LOOPLCK
	call	tuning

;----------- store ------------------------

loop4
	movlw	D'25'
	subwf	TUNCNTH,W
	btfss	STATUS,Z
	goto	loop
	incfsz	TUNCNTL,W
	goto	loop
	call	store
	movlw	0x42
	movwf	DISP
	call	address
	movlw	D'3'
	movwf	DISP
	call	char 
	goto	loop

;****************** BUTTONS ********************************

tuning
	bsf	LED
	bsf	CURRENT
	call	frq2pll	
	bcf	LED
	movlw	0x41
	movwf	DISP
	call	address
	movlw	D'1'
	movwf	DISP
	call	char 
	movlw	' '
	movwf	DISP
	call	char 
	return

tuneup
	movlw	(STEP)
	addwf	FREQL,F
	btfsc	STATUS,C
	incf	FREQH,F
	goto	tune
tunedn
	movlw	(STEP)
	subwf	FREQL,F
	btfss	STATUS,C
	decf	FREQH,F
tune
	bsf	LED
	clrf	INKEY
	movlw	HIGH((BOTFRQ)-(STEP))
	subwf	FREQH,W
	btfss	STATUS,Z
	goto	tune2
	movlw	LOW((BOTFRQ)-(STEP))
	subwf	FREQL,W
	btfss	STATUS,Z
	goto	tune2
	movlw	HIGH(TOPFRQ)
	movwf	FREQH
	movlw	LOW(TOPFRQ)
	movwf	FREQL
	goto	tune5
tune2
	movlw	HIGH((TOPFRQ)+(STEP))
	subwf	FREQH,W
	btfss	STATUS,Z
	goto	tune3
	movlw	LOW((TOPFRQ)+(STEP))
	subwf	FREQL,W
	btfss	STATUS,Z
	goto	tune3
	movlw	HIGH(BOTFRQ)
	movwf	FREQH
	movlw	LOW(BOTFRQ)
	movwf	FREQL
	goto	tune5
tune3
	movlw	HIGH(BOTFRQ)
	subwf	FREQH,W
	btfsc	STATUS,C
	goto	tune4
	movlw	HIGH(TOPFRQ)
	movwf	FREQH
	movlw	LOW(TOPFRQ)
	movwf	FREQL
	goto	tune5
tune4
	movf	FREQH,W
	sublw	HIGH(TOPFRQ)
	btfsc	STATUS,C
	goto	tune5
	movlw	HIGH(BOTFRQ)
	movwf	FREQH
	movlw	LOW(BOTFRQ)
	movwf	FREQL
tune5
	movlw	D'0'
	movwf	DISP
	call	address
	call	frq2lcd
	bcf	LED
	return

;***************** CONVERSIONS *******************************

frq2lcd				;FREQ to LCD
	movf	FREQH,W
	movwf	MODH
	movf	FREQL,W
	movwf	MODL
	bsf	ZERO

	movlw	D'39'		;10000
	movwf	DIVH
	movlw	D'16'
	movwf	DIVL
	call	divide
	movlw	'0'
	addwf	DISP,W
	btfsc	ZERO
	movlw	' '
	movwf	DISP
	call	char		

	movlw	D'3'		;1000
	movwf	DIVH
	movlw	D'232'
	movwf	DIVL
	call	divide
	movlw	'0'
	addwf	DISP,W
	btfsc	ZERO
	movlw	' '
	movwf	DISP
	call	char		

	movlw	D'0'		;100
	movwf	DIVH
	movlw	D'100'
	movwf	DIVL
	call	divide
	movlw	'0'
	addwf	DISP,F
	call	char		

	movlw	'.'
	movwf	DISP
	call	char

	movlw	D'0'		;10
	movwf	DIVH
	movlw	D'10'
	movwf	DIVL
	call	divide
	movlw	'0'
	addwf	DISP,F
	call	char		

	movlw	D'0'		;1
	movwf	DIVH
	movlw	D'1'
	movwf	DIVL
	call	divide
	movlw	'0'
	addwf	DISP,F
	call	char		

	return

divide
	clrf	DISP
divide1
	movf	DIVL,W
	subwf	MODL,F
	btfss	STATUS,C
	decf	MODH,F
	movf	DIVH,W
	subwf	MODH,F

	btfsc	MODH,7
	goto	divend

	incf	DISP,F
	bcf	ZERO
	clrwdt
	goto	divide1
divend
	movf	DIVL,W
	addwf	MODL,F
	btfsc	STATUS,C
	incf	MODH,F
	movf	DIVH,W
	addwf	MODH,F

	return

;****************** DISPLAY **********************************

dispini
	clrf	PORTB
	call	delaylo
	call	delaylo
	call	delaylo

	movlw	B'100100'		;to 8bit
	movwf	PORTB
	call	ticklng
	movlw	B'000000'		;dummy
	movwf	PORTB
	call	ticklng
	
	movlw	B'000100'		;to 4bit
	movwf	PORTB
	call	ticklng
	movlw	B'000100'		;ini 1/2
	movwf	PORTB
	call	ticklng
	movlw	B'001000'		;ini 2/2
	movwf	PORTB
	call	ticklng	

	movlw	B'000000'		;disp 1/2
	movwf	PORTB
	call	ticklng
	movlw	B'011000'		;disp 2/2
	movwf	PORTB
	call	ticklng
	movlw	B'000000'		;mov 1/2
	movwf	PORTB
	call	ticklng
	movlw	B'010100'		;mov 2/2
	movwf	PORTB
	call	ticklng
	movlw	B'000000'		;clr 1/2
	movwf	PORTB
	call	ticklng
	movlw	B'100000'		;clr 2/2
	movwf	PORTB
	call	ticklng

	return

address						;input in DISP
	movf	DISP,W
	movwf	WORK
	bsf	WORK,7
	swapf	WORK,F
	movlw	B'00001111'
	andwf	WORK,F
	call	bit2pin
	movf	WORK,W
	movwf	PORTB
	call	tick

	movf	DISP,W
	movwf	WORK
	movlw	B'00001111'
	andwf	WORK,F
	call	bit2pin
	movf	WORK,W
	movwf	PORTB
	call	tick
	return

cgaddr						;input in DISP
	movf	DISP,W
	movwf	WORK
	bcf	WORK,7
	bsf	WORK,6
	swapf	WORK,F
	movlw	B'00001111'
	andwf	WORK,F
	call	bit2pin
	movf	WORK,W
	movwf	PORTB
	call	tick

	movf	DISP,W
	movwf	WORK
	movlw	B'00001111'
	andwf	WORK,F
	call	bit2pin
	movf	WORK,W
	movwf	PORTB
	call	tick
	return

char						;input in DISP
	movf	DISP,W
	movwf	WORK
	swapf	WORK,F
	movlw	B'00001111'
	andwf	WORK,F
	call	bit2pin
	bsf	WORK,0
	movf	WORK,W
	movwf	PORTB
	call	tick

	movf	DISP,W
	movwf	WORK
	movlw	B'00001111'
	andwf	WORK,F
	call	bit2pin
	bsf	WORK,0
	movf	WORK,W
	movwf	PORTB
	call	tick
	return

usrchr
	clrf	DISP
	call	cgaddr

	movlw	B'00001110'
	movwf	DISP
	call	char
	movlw	B'00010001'
	movwf	DISP
	call	char
	movlw	B'00001110'
	movwf	DISP
	call	char
	movlw	B'00000100'
	movwf	DISP
	call	char
	movlw	B'00001100'
	movwf	DISP
	call	char
	movlw	B'00000100'
	movwf	DISP
	call	char
	movlw	B'00001100'
	movwf	DISP
	call	char
	movlw	B'00000000'
	movwf	DISP
	call	char

	movlw	B'00001000'
	movwf	DISP
	call	char
	movlw	B'00001100'
	movwf	DISP
	call	char
	movlw	B'00001110'
	movwf	DISP
	call	char
	movlw	B'00001111'
	movwf	DISP
	call	char
	movlw	B'00001110'
	movwf	DISP
	call	char
	movlw	B'00001100'
	movwf	DISP
	call	char
	movlw	B'00001000'
	movwf	DISP
	call	char
	movlw	B'00000000'
	movwf	DISP
	call	char

	movlw	B'00000010'
	movwf	DISP
	call	char
	movlw	B'00001111'
	movwf	DISP
	call	char
	movlw	B'00010010'
	movwf	DISP
	call	char
	movlw	B'00000000'
	movwf	DISP
	call	char
	movlw	B'00001001'
	movwf	DISP
	call	char
	movlw	B'00011110'
	movwf	DISP
	call	char
	movlw	B'00001000'
	movwf	DISP
	call	char
	movlw	B'00000000'
	movwf	DISP
	call	char

	movlw	B'00000000'
	movwf	DISP
	call	char
	movlw	B'00011010'
	movwf	DISP
	call	char
	movlw	B'00011011'
	movwf	DISP
	call	char
	movlw	B'00011111'
	movwf	DISP
	call	char
	movlw	B'00010001'
	movwf	DISP
	call	char
	movlw	B'00010001'
	movwf	DISP
	call	char
	movlw	B'00000000'
	movwf	DISP
	call	char
	movlw	B'00000000'
	movwf	DISP
	call	char

	return

bit2pin
	clrf	WORK2

	btfsc	WORK,0
	bsf	WORK2,5
	btfsc	WORK,1
	bsf	WORK2,2
	btfsc	WORK,2
	bsf	WORK2,4
	btfsc	WORK,3
	bsf	WORK2,3

	movf	WORK2,W
	movwf	WORK

	return

delay				;delay 2 ms
	call	delaysh
	call	delaysh
	call	delaysh
	call	delaysh
	call	delaysh
	return

delaysh				;delay 0.4 ms (short)
        nop
	return		

delaylo				;delay 10 ms (long)
	call	delay
	call	delay
	call	delay
	call	delay
	call	delay
	return

tick
	bsf	CLK
	bcf	CLK
	return

ticklng
	call	delay
	bsf	CLK
	call	delay
	bcf	CLK
	call	delay
	return

;********************* I2C *****************************

frq2pll					;FREQ to PLL
	clrf	DIVH
	clrf	DIVL
	movf	FREQL,W
	movwf	MODL
	movf	FREQH,W
	movwf	MODH
frq2pll1
	movlw	(PLLXTL*D'10'/D'32')
	subwf	MODL,F
	btfss	STATUS,C
	decf	MODH,F

	btfsc	MODH,7
	goto	frq2pl1a

	movlw	D'20'
	addwf	DIVL,F
	btfsc	STATUS,C
	incf	DIVH,F
	goto	frq2pll1
frq2pl1a
	movlw	(PLLXTL*D'10'/D'32')
	addwf	MODL,F
	incf	MODH,F
frq2pll2
	movlw	(PLLXTL/D'64')
	subwf	MODL,F
	btfss	STATUS,C
	decf	MODH,F

	btfsc	MODH,7
	goto	frq2pll3

	incfsz	DIVL,F
	goto	frq2pll2
	incf	DIVH,F
	goto	frq2pll2
frq2pll3
	call	i2cstart

	movlw	B'11000010'	;TSA5511 address - write
	movwf	BYTE
	call	i2csend

	movf	DIVH,W
	movwf	BYTE
	call	i2csend

	movf	DIVL,W
	movwf	BYTE
	call	i2csend

	movlw	B'10001110'	;charge-pump current and test bits
	movwf	BYTE
	btfsc	CURRENT
	bsf	BYTE,6
	call	i2csend

	movlw	B'00000000'	;I/O pins
	movwf	BYTE
	call	i2csend

	call	i2cstop
	return

tuned
	call	i2cstart

	movlw	B'11000011'	;TSA5511 address - read
	movwf	BYTE
	call	i2csend

	call	i2cread
	bcf	TND
	btfsc	BYTE,6
	bsf	TND

	call	i2cstop
	return

i2cstart
	bcf	SCL
	bsf	I2CPWR
	bsf	SDA
	bsf	SCL
	bcf	SDA
	bcf	SCL
	return

i2cstop
	bcf	SDA
	bsf	SCL
	bsf	SDA
	bcf	SCL
	bcf	SDA
	bcf	I2CPWR
	return

i2cread				;output in BYTE
	bsf	SDA	
	clrf	BYTE

	btfsc	SDA
	bsf	BYTE,7
	call	i2cclk

	btfsc	SDA
	bsf	BYTE,6
	call	i2cclk

	btfsc	SDA
	bsf	BYTE,5
	call	i2cclk

	btfsc	SDA
	bsf	BYTE,4
	call	i2cclk

	btfsc	SDA
	bsf	BYTE,3
	call	i2cclk

	btfsc	SDA
	bsf	BYTE,2
	call	i2cclk

	btfsc	SDA
	bsf	BYTE,1
	call	i2cclk

	btfsc	SDA
	bsf	BYTE,0
	call	i2cclk

	call	i2cclk
	return

i2csend				;input in BYTE
	btfsc	BYTE,7
	bsf	SDA
	btfss	BYTE,7
	bcf	SDA
	call	i2cclk

	btfsc	BYTE,6
	bsf	SDA
	btfss	BYTE,6
	bcf	SDA
	call	i2cclk

	btfsc	BYTE,5
	bsf	SDA
	btfss	BYTE,5
	bcf	SDA
	call	i2cclk

	btfsc	BYTE,4
	bsf	SDA
	btfss	BYTE,4
	bcf	SDA
	call	i2cclk

	btfsc	BYTE,3
	bsf	SDA
	btfss	BYTE,3
	bcf	SDA
	call	i2cclk

	btfsc	BYTE,2
	bsf	SDA
	btfss	BYTE,2
	bcf	SDA
	call	i2cclk

	btfsc	BYTE,1
	bsf	SDA
	btfss	BYTE,1
	bcf	SDA
	call	i2cclk

	btfsc	BYTE,0
	bsf	SDA
	btfss	BYTE,0
	bcf	SDA
	call	i2cclk

	bsf	SDA		;ACK
	bcf	I2CERR
	btfsc	SDA
	bsf	I2CERR

	call	i2cclk
	return

i2cclk
	bsf	SCL
	bcf	SCL
	return

;********************* EEPROM *****************************

store
	bsf	STATUS, RP0
	clrf	EEADR
	movf	FREQL,W
	movwf	EEDATA
	call	storeit
	incf	EEADR,F
	movf	FREQH,W
	movwf	EEDATA
	call	storeit
	bcf	STATUS, RP0
	return
storeit
	bsf	EECON1, WREN
	movlw	0x55
	movwf	EECON2
	movlw	0xAA
	movwf	EECON2
	bsf	EECON1,WR
storit2
	btfsc	EECON1,WR
	goto	storit2	
	bcf	EECON1, WREN
	return

read
	bsf	STATUS, RP0
	clrf	EEADR
	bsf	EECON1, RD
	movf	EEDATA,W
	movwf	FREQL
	incf	EEADR,F
	bsf	EECON1, RD
	movf	EEDATA,W
	movwf	FREQH
	bcf	STATUS, RP0
	return

	END                       ; directive 'end of program'
