; *******************************************************************************
; *										*
; *	Si5351 Software to program LO 2 frequencies for Pic-A -Star rigs	*
; *	Modified from software by G8JVM by T Mowles VK5TM Feb/Mar 2015		*
; *										*
; *******************************************************************************
; *										*
; * This program will initialize Si5351A-B-GT with a default frequency of	*
; * 10.710MHz. Pulling pin 5 of the 12F629 low will change the frequency to	*
; * 10.715MHz. NOTE: this must be done before power-up				*
; * The 12F629 will then go to sleep						*
; *										*
; *******************************************************************************

; *******************************************************************************
; *				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			*
; *		MCLR/Vpp/--GP3 |4	5| GP2----Frequency select		*
; *				_________					*
; *										*
; *******************************************************************************

; *******************************************************************************
; *				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

	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	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	H'4'		; Enable pullup on GPIO,2
	movwf	WPU		; 
	call	0x3FF		; retrieve factory calibration value
	movwf	OSCCAL
	banksel	GPIO		; Switch to bank 0			
	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

; *******************************************************************************
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)
; _______________________________________________________________________________
	movlw	H'04'		; Register command - (10.710MHZ)
	btfss	GPIO,FREQ
	movlw	H'02'		; (10.715MHZ)
	movwf	ebyte
	call	putbyte		; Output byte and get ACK (26)
	movlw	H'E2'		; Register command - (10.710MHZ)
	btfss	GPIO,FREQ
	movlw	H'71'		; (10.715MHZ)
	movwf	ebyte
	call	putbyte		; Output byte and get ACK (27)
; _______________________________________________________________________________

	clrf	ebyte
	call	putbyte		; Output byte and get ACK (28)
;________________________________________________________________________________
	movlw	H'0C'		; Register command - (10.710MHZ)
	btfss	GPIO,FREQ
	movlw	H'0B'		; (10.715MHZ)
	movwf	ebyte
	call	putbyte		; Output byte and get ACK (29)
	movlw	H'23'		; Register command - (10.710MHZ)
	btfss	GPIO,FREQ
	movlw	H'B7'		; (10.715MHZ)
	movwf	ebyte
	call	putbyte		; Output byte and get ACK (30)
;________________________________________________________________________________

	clrf	ebyte
	call	putbyte		; Output byte and get ACK (31)
	clrf	ebyte
	call	putbyte		; Output byte and get ACK (32)
;________________________________________________________________________________
	movlw	H'9A'		; Register command - (10.710MHZ)
	btfss	GPIO,FREQ
	movlw	H'39'		; (10.715MHZ)
	movwf	ebyte
	call	putbyte		; Output byte and get ACK (33)
;________________________________________________________________________________

	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'1F'		; Register command - (10.710MHZ)
	btfss	GPIO,FREQ
	movlw	H'1E'		; (10.715MHZ)
	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

	sleep			; Put PIC to sleep

; *******************************************************************************
; 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
