; *******************************************************************************
; *										*
; *	Si5351 Software to program 4 frequencies for a Local Oscillator		*
; *	Copyright T Mowles VK5TM April 2015					*
; *										*
; *	This code may be used for personal, non-profit use only.		*
; *										*
; * Commercial use of this code or any derivatives of it is expressly forbidden.*
; *										*
; * Use in any profit making enterprise of any kind is also expressly forbidden,*
; * whether or not this software (or any derivatives of it) is provided free 	*
; * of charge, unless WRITTEN permission is obtained from the copyright owner.	*
; *										*
; *******************************************************************************
; *										*
; * This program will initialize an Si5351A-B-GT with 1 of 4 preprogammed	*
; * frequencies depending on the state of the GP1 & GP2 inputs.			*
; * It also allows switching of the frequencies without having to powerdown.	*
; * The 12F629 goes to sleep when not actively changing the frequency.		*
; *										*
; *******************************************************************************
;
; *******************************************************************************
; *				Device type and options				*
; *******************************************************************************
;
    processor   PIC12F629           ; directive to define processor
    #include    <P12F629.INC>       ; processor specific variable definitions
;
; *******************************************************************************

      __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

        errorlevel  -302            ; Turn off annoying Bank messages 
;
; *******************************************************************************
; *										*
; *				PIC12F629					* 
; *				_________					*
; *		+3.3V------Vdd |1	8| Vss----GND				*
; *		SCL--------GP5 |2	7| GP0----ICSPDAT			*
; *		SDA--------GP4 |3	6| GP1----ICSPCLK/Frequency select 1	*
; *		MCLR/Vpp/--GP3 |4	5| GP2----Frequency select 2		*
; *				_________					*
; *										*
; *******************************************************************************
;
; *******************************************************************************
; *				Assign names to PIC IO pins			*
; *******************************************************************************
; 
;   GPIO Port Assignment:
;
;FREQ	equ	0x02		; Frequency select input
SDA	equ	0x04		; I2C Data
SCL	equ	0x05		; I2C Clock
;
; *******************************************************************************
; *				DATA MEMORY					*
; *******************************************************************************
	CBLOCK	20h

	count			; Gen purpose counter
	temp			; Temporary work register
	bytcnt			; used in i2c driver
	ebyte			; I2C device data to be sent
	freq			; holds state of freq select pins
	ENDC
;
; *******************************************************************************
; *				PROGRAM START					*
; *******************************************************************************
	
	ORG	0x00		;Reset vector address
	goto	Start

	ORG	0x04		; Interrupt Vector address
	goto	Start
;
; *******************************************************************************
Start
;	clrf	INTCON		; Disable interrupts		
	movlw	H'07'		; Disable Comparator module
	movwf	CMCON
; Initialise GPIO port for i2c
; First set all as inputs, then clear individual bits as outputs
	banksel	TRISIO		; Switch to register bank 1
	movlw	b'00001110'	; GPIO,1,2,3 as inputs, 0,4,5 as outputs
;	movlw	H'FF'		; All inputs
	movwf	TRISIO
;	bcf	TRISIO,SCL	; SCL set as output
;	bcf	TRISIO,SDA	; Set SDA as output
	clrf	OPTION_REG	; General enable pullups (bit 7 clear)
	movlw	b'00000110'	; Enable pullups on GPIO,1 & 2
	movwf	WPU		; 
	call	0x3FF		; retrieve factory calibration value
	movwf	OSCCAL
;	Setup interupt on change pins
	movlw	b'00000110'	; Set GPIO,1 & 2 to interupt on change
	movwf	IOC		;
	banksel	GPIO		; Switch to bank 0
	bsf	INTCON,GPIE	; Enable Port Change Interupt			
	clrf	GPIO		; Resting state of i2c bus is SCL low, SDA high
	bsf 	GPIO,SDA	; Set SDA high
	bcf	GPIO,SCL	; Set SCL low
	call	wait_10ms	; Wait to be sure Si5351 completes power up
;
; *******************************************************************************
; Determine state of frequency select pins and store in 'freq'
begin
	movfw	GPIO		; Get state of GPIO pins
	andlw	b'00000110'	; Mask to read GPIO 1 & 2
	movwf	freq		; Move to register
	bcf	STATUS,C	; Clear 'C' ready for rotate
	rrf	freq,f		; Rotate bits into bits 0 & 1 of 'frequency'
;
;********************************************************************************
Sendsiregs

	call	openw		; Open for Write, signal start, send 0xC0, ask for ACK
	movlw	d'3'		; Address Register 3
	movwf	ebyte
	call	putbyte		; Output byte and get ACK
	movlw	H'FF'		; Register command - disable outputs
	movwf	ebyte
	call	putbyte		; Output byte and get ACK
	call	stop		; Send Stop to Si5351

	call	openw		; Open for Write, signal start, send 0xC0, ask for ACK
	movlw	d'187'		; Address Register 187
	movwf	ebyte
	call	putbyte		; Output byte and get ACK
	clrf	ebyte		; Register command - fanout enable
	call	putbyte		; Output byte and get ACK
	call	stop		; Send Stop to Si5351

	call	openw		; Open for Write, signal start, send 0xC0, ask for ACK
	movlw	d'16'		; Address Register 16
	movwf	ebyte
	call	putbyte		; Output byte and get ACK

	movlw	d'8'		; Loop count
	movwf	temp
	movlw	H'80'		; Register command - power down output drivers (16 -23)
	movwf	ebyte
	call	putbyte		; Output byte and get ACK
	decfsz	temp,f
	goto	$-4
	call	stop		; Send Stop to Si5351
;________________________________________________________________________________
	call	openw		; Open for Write, signal start, send 0xC0, ask for ACK
	movlw	d'183'		; Address Register 183
	movwf	ebyte
	call	putbyte		; Output byte and get ACK

	movlw	H'D2'		; Register command - set crystal capacitor 10pF
;	movlw	H'92'		; Register command - set crystal capacitor 8pF
;	movlw	H'52'		; Register command - set crystal capacitor 6pF
	movwf	ebyte
	call	putbyte		; Output byte and get ACK
	call	stop		; Send Stop to Si5351
;________________________________________________________________________________

	call	openw		; Open for Write, signal start, send 0xC0, ask for ACK
	movlw	d'15'		; Address Register 15
	movwf	ebyte
	call	putbyte		; Output byte and get ACK

	clrf	ebyte		; Command - PLL Input source
	call	putbyte		; Output byte and get ACK (15)
;________________________________________________________________________________
;	movlw	H'4C'		; Register command - 2mA output
;	movlw	H'4D'		; Register command - 4mA output
;	movlw	H'4E'		; Register command - 6mA output
 	movlw	h'4F'		; Register command - 8mA output
	movwf	ebyte
	call	putbyte		; Output byte and get ACK (16)17 - 23 already 0x80
	call	stop		; Send Stop to Si5351
;________________________________________________________________________________

	call	openw		; Open for Write, signal start, send 0xC0, ask for ACK
	movlw	d'24'		; Address Register 24
	movwf	ebyte
	call	putbyte		; Output byte and get ACK

	clrf	ebyte		; Register command - CLK 0-3 disable state
	call	putbyte		; Output byte and get ACK (24)

	clrf	ebyte		; Register command - CLK 4-7 disable state
	call	putbyte		; Output byte and get ACK (25)

;________________________________________________________________________________
;	Register 26
	movfw	freq		; move file to 'W' for test
	xorlw	3		; Test if 'freq' = 0
	btfsc	STATUS,Z	; Jump to next test if not 0
	goto	out1		; = 0 so set to 1st frequency
	movfw	freq
	xorlw	1		; Test if 'freq' = 1
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out2		; = 1 so set to 2nd frequency
	movfw	freq
	xorlw	2		; Test if 'freq' = 2
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out3		; = 2 so set to 3rd frequency
	movfw	freq
	xorlw	0		; Test if 'frequency' = 3
	btfsc	STATUS,Z	; Jump to next test if not 1  
	goto	out4		; = 3 so set to 4th frequency

out1
	movlw	H'00'		; 54MHZ
	goto	outend1
out2
	movlw	H'30'		; 53.9985MHz
	goto	outend1
out3
	movlw	H'30'		; 53.0015MHz
	goto	outend1
out4
	movlw	H'F4'		; 53.9993MHz
outend1
	movwf	ebyte
	call	putbyte

;	Register 27
	movfw	freq		; move file to 'W' for test
	xorlw	3		; Test if 'freq' = 0
	btfsc	STATUS,Z	; Jump to next test if not 0
	goto	out1a		; = 0 so set to 1st frequency
	movfw	freq
	xorlw	1		; Test if 'freq' = 1
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out2a		; = 1 so set to 2nd frequency
	movfw	freq
	xorlw	2		; Test if 'freq' = 2
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out3a		; = 2 so set to 3rd frequency
	movfw	freq
	xorlw	0		; Test if 'frequency' = 3
	btfsc	STATUS,Z	; Jump to next test if not 1  
	goto	out4a		; = 3 so set to 4th frequency

out1a
	movlw	H'19'		; 54MHZ
	goto	outend1a
out2a
	movlw	H'D4'		; 53.9985MHz
	goto	outend1a
out3a
	movlw	H'D4'		; 53.0015MHz
	goto	outend1a
out4a
	movlw	H'24'		; 53.9993MHz
outend1a
	movwf	ebyte
	call	putbyte
; _______________________________________________________________________________

	clrf	ebyte
	call	putbyte		; Output byte and get ACK (28)
;________________________________________________________________________________
	movlw	H'0A'		; Register command - (54MHZ)
	movwf	ebyte
	call	putbyte		; Output byte and get ACK (29)

;	Register 30
	movfw	freq		; move file to 'W' for test
	xorlw	3		; Test if 'freq' = 0
	btfsc	STATUS,Z	; Jump to next test if not 0
	goto	out1b		; = 0 so set to 1st frequency
	movfw	freq
	xorlw	1		; Test if 'freq' = 1
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out2b		; = 1 so set to 2nd frequency
	movfw	freq
	xorlw	2		; Test if 'freq' = 2
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out3b		; = 2 so set to 3rd frequency
	movfw	freq
	xorlw	0		; Test if 'frequency' = 3
	btfsc	STATUS,Z	; Jump to next test if not 1  
	goto	out4b		; = 3 so set to 4th frequency

out1b
	movlw	H'F5'		; 54MHZ
	goto	outend1b
out2b
	movlw	H'B8'		; 53.9985MHz
	goto	outend1b
out3b
	movlw	H'F5'		; 53.0015MHz
	goto	outend1b
out4b
	movlw	H'F5'		; 53.9993MHz
outend1b
	movwf	ebyte
	call	putbyte
;________________________________________________________________________________

	clrf	ebyte
	call	putbyte		; Output byte and get ACK (31)

;	Register 32
	movfw	freq		; move file to 'W' for test
	xorlw	3		; Test if 'freq' = 0
	btfsc	STATUS,Z	; Jump to next test if not 0
	goto	out1c		; = 0 so set to 1st frequency
	movfw	freq
	xorlw	1		; Test if 'freq' = 1
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out2c		; = 1 so set to 2nd frequency
	movfw	freq
	xorlw	2		; Test if 'freq' = 2
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out3c		; = 2 so set to 3rd frequency
	movfw	freq
	xorlw	0		; Test if 'frequency' = 3
	btfsc	STATUS,Z	; Jump to next test if not 1  
	goto	out4c		; = 3 so set to 4th frequency

out1c
	movlw	H'00'		; 54MHZ
	goto	outend1c
out2c
	movlw	H'14'		; 53.9985MHz
	goto	outend1c
out3c
	movlw	H'20'		; 53.0015MHz
	goto	outend1c
out4c
	movlw	H'AF'		; 53.9993MHz
outend1c
	movwf	ebyte
	call	putbyte
;________________________________________________________________________________
;	Register 33
	movfw	freq		; move file to 'W' for test
	xorlw	3		; Test if 'freq' = 0
	btfsc	STATUS,Z	; Jump to next test if not 0
	goto	out1d		; = 0 so set to 1st frequency
	movfw	freq
	xorlw	1		; Test if 'freq' = 1
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out2d		; = 1 so set to 2nd frequency
	movfw	freq
	xorlw	2		; Test if 'freq' = 2
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out3d		; = 2 so set to 3rd frequency
	movfw	freq
	xorlw	0		; Test if 'frequency' = 3
	btfsc	STATUS,Z	; Jump to next test if not 1  
	goto	out4d		; = 3 so set to 4th frequency

out1d
	movlw	H'13'		; 54MHZ
	goto	outend1d
out2d
	movlw	H'20'		; 53.9985MHz
	goto	outend1d
out3d
	movlw	H'9C'		; 53.0015MHz
	goto	outend1d
out4d
	movlw	H'0C'		; 53.9993MHz
outend1d
	movwf	ebyte
	call	putbyte
;________________________________________________________________________________

	movlw	d'9'
	movwf	temp
	clrf	ebyte
	call	putbyte		; Output byte and get ACK (34 - 42)
	decfsz	temp,f
	goto	$-3
	movlw	H'01'		; Register command - 
	movwf	ebyte
	call	putbyte		; Output byte and get ACK (43)
	clrf	ebyte
	call	putbyte		; Output byte and get ACK (44)

	movlw	H'04'		; Register command - (54MHZ)
	movwf	ebyte
	call	putbyte		; Output byte and get ACK (45)

	movlw	d'47'
	movwf	temp
	clrf	ebyte
	call	putbyte		; Output byte and get ACK (46 - 92
	decfsz	temp,f
	goto	$-3
	call	stop		; Send Stop to Si5351

	call	openw		; Open for Write, signal start, send 0xC0, ask for ACK
	movlw	d'149'		; Address Register 149
	movwf	ebyte
	call	putbyte		; Output byte and get ACK

	movlw	d'22'		; Loop counter
	movwf	temp
	clrf	ebyte
	call	putbyte		; Output byte and get ACK (149 - 170)
	decfsz	temp,f
	goto	$-3
	call	stop		; Send Stop to Si5351

	call	openw		; Open for Write, signal start, send 0xC0, ask for ACK
	movlw	d'177'		; Address Register 177
	movwf	ebyte
	call	putbyte		; Output byte and get ACK
	movlw	H'AC'		; Register command - soft reset
	movwf	ebyte
	call	putbyte		; Output byte and get ACK
	call	stop		; Send Stop to Si5351

	call	openw		; Open for Write, signal start, send 0xC0, ask for ACK
	movlw	d'3'		; Address Register 3
	movwf	ebyte
	call	putbyte		; Output byte and get ACK
	movlw	H'FE'		; Register command - enable CLK0 output
	movwf	ebyte
	call	putbyte		; Output byte and get ACK
	call	stop		; Send Stop to Si5351

	; Put PIC to sleep until a change on any input
	movf	GPIO,w		; clear the change condition (see 12F629 data sheet)
	bcf	INTCON,GPIF	; clear the interrupt flag
	sleep			; Night,night. Sweet dreams.
	call	wait_10ms	; 10mS delay to account for switch bounce,
				; increase as necessary
	call	wait_10ms
	goto	begin
;
; *******************************************************************************
; Open for write
openw
	call	i2cstart
	movlw	H'C0'		; Send Si5351 address
	movwf	ebyte
	call	putbyte		; Output Address byte and get ACK

	return
;
; *******************************************************************************
; *				I2C ROUTINES					*
; *******************************************************************************
; Start condition; data goes from hi to low with SCL high
; Normal state is: SCL low, data high
i2cstart
	banksel	TRISIO	
	bcf	TRISIO,SDA	; SDA is output
	banksel	GPIO
	bsf	GPIO,SDA	; Set SDA High	
	bsf	GPIO,SCL	; SCL hi
	nop
	bcf	GPIO,SDA	; SDA low
	nop
	bcf	GPIO,SCL	; SCL low
	nop	
	bsf	GPIO,SDA	; SDA High on exit		

        return
;
; *******************************************************************************	
; Output a byte in ebyte to Si5351 and then get an ACK
putbyte	
	banksel	TRISIO	
	bcf	TRISIO,SDA	; SDA is output
	banksel	GPIO	

	movlw	H'08'		; Counter
	movwf	bytcnt

	bcf	STATUS,C
senlp
	rlf	ebyte,f
	btfss	STATUS,C
	goto	zerb			
	bsf	GPIO,SDA	; SDA hi 
	goto	zerb1
zerb
	bcf	GPIO,SDA	; SDA low 	
zerb1
	bsf	GPIO,SCL	; SCL hi
	nop
	nop
	bcf	GPIO,SCL	; SCL low
	decfsz	bytcnt,f			
	goto	senlp
	bsf	GPIO,SDA	; Exit with data high
	call	getack

	return
;
; *******************************************************************************
; Read acknowledge signal from Si5351. SDA low with SCL high
getack
	banksel	TRISIO	
	bsf	TRISIO,SDA	; SDA is Input
	banksel GPIO	
	bsf	GPIO,SCL	; SCL hi  
	nop
	nop		
	bcf	GPIO,SCL	; SCL low 	
	return			; With C clear if ack received, set otherwise
;
; *******************************************************************************
; Stop condition; data goes from low to hi with SCL high
; Normal state is: SCL low, data high
stop
	nop
	banksel	TRISIO	
	bcf	TRISIO,SDA	; SDA is output
	banksel	GPIO	
	bcf	GPIO,SDA	; SDA low 
	nop
	nop
	bsf	GPIO,SCL	; SCL hi  
	nop
	nop
	bsf	GPIO,SDA	; SDA hi 
	nop
	nop
	bcf	GPIO,SCL	; SCL low

	return
;
; *******************************************************************************
; *										*
; * Purpose:	Wait for a specified number of milliseconds			*
; *										*
; *		Entry point wait_10ms : Wait for 10 msec			*
; *		Entry point wait_1ms						*
; *										*
; *   Input:	None								*
; *										*
; *  Output:	None								*
; *										*
; *******************************************************************************

wait_10ms   ; ****** Entry point ******     
	movlw	D'20'		; Set up outer loop counter to 20
	movwf	count
	goto	outer_loop	; Into the wait loops
wait_1ms
        movlw	D'2'		; Set up outer loop counter to 2
        movwf   count
;
; Wait loops used by other wait routines
;  - 1 microsecond per instruction (with a 4 MHz microprocessor clock)
;  - 498 instructions per inner loop
;  - (temp * 498) instructions (.498 msec) per outer loop
;  - Round off to .5 ms per outer loop
;
outer_loop                        
	movlw	H'A4'		; Set up inner loop counter to 165
	movwf	temp
inner_loop
	decfsz	temp,f		; Decrement inner loop counter
	goto	inner_loop	; If inner loop counter not down to zero, 
				;   then go back to inner loop again
	decfsz	count,f		; Yes, Decrement outer loop counter
	goto	outer_loop	; If outer loop counter not down to zero,
				;   then go back to outer loop again
	return			; Yes, return to caller

; *******************************************************************************
	END

; Move these tests into register setting area!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	movfw	freq		; move file to 'W' for test
	xorlw	0		; Test if 'freq' = 0
	btfsc	STATUS,Z	; Jump to next test if not 0
	goto	out1		; = 0 so set to 1st frequency
	movfw	freq
	xorlw	1		; Test if 'freq' = 1
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out2		; = 1 so set to 2nd frequency
	movfw	freq
	xorlw	2		; Test if 'freq' = 2
	btfsc	STATUS,Z	; Jump to next test if not 1
	goto	out3		; = 2 so set to 3rd frequency
	movfw	freq
	xorlw	3		; Test if 'frequency' = 3
	btfsc	STATUS,Z	; Jump to next test if not 1  
	goto	out4		; = 3 so set to 4th frequency

out1
	movlw
	goto	outend1
out2
	movlw
	goto	outend1
out3
	movlw
	goto	outend1
out4
	movlw
outend1
	movwf	ebyte
	call	putbyte