ad9850_1
;
;20130302
;SM7UCZ, Johnny Apell
;130308 / SM5EUF
;130313 / SM7UCZ lagt till BFO funktionen
;130317 / UCZ prov med 30.72MHz kristall 50% mindre ström
;ingång RC3 skiftar delningstalen mellan 125/30.72MHz kristallerna
;
;Program för att styra en adomvandlare AD9850
;med hjälp av en potentiometer för frekvensen och en potentiometer för BFO
;Behöver endast frekvenserna 3500....3600, 3600...3700 och 3700...3800 kHz
;Potentiometrarna läses in med 10 bitar (1024 steg)
;VFO potentiometern har fått en upplösning av 100Hz/steg
;vilket ger 100kHz inställningsområde
;BFO potentiometern har fått en upplösning på 10Hz/steg
;vilket ger -5kHz...0...+5kHz inställningsområde
;BFO potentiometern adderas eller subtraheras från VFO potentiometern
;En ingång "TX" nollställer BFO funktionen
;
;AD9850 kan styras i steg om 0.0291Hz... inte jämt delbart med 2 precis!
;
;Logaritmen är till AD9850
;code till AD = freq × 2³²/125  allt i MHz
;eller i siffror  code= frek × 4294967296/125
;och det skall placeras i 32 bitar och klockas in till AD9850
;plus 8 bitar som skall vara "0"
;Det är 125MHz klocka på AD9850-kortet
;Test gjord med 30.72MHz klocka  mycket lägre strömförbrukning

;**********************************************************************************
;*****[ Data Storage Reg's ]*****
        CBLOCK  0x20        	; Tilldela registerna olika namn från adressen 0x20
		bandreg		
		BitCount	
		Temp		
		TEMP		
		A0			
		A1			
		A2		
		A3			
		B0			
		B1			
		B2			
		B3			
		R0			
		R1			
		R2			
		R3			
		R4
		C0
		C1
		D0
		D1
		AD2Lo
		AD2Hi
		temp0
		temp1
		temp2
		temp3
		tempLo
		tempHi
		resultat
		aLO
		aHI
		bLO
		bHI
		flag
		flag2
		VFOlow
		VFOnew
		VFOold
		BFOnew
		BFOold		
		ENDC
								;Tilldela enkilda bitar ett unikt namn

#define		FData				PORTC,2
#define		WClk				PORTC,0
#define		FQud				PORTC,1
#define		TX					PORTA,5
#define		band0				PORTC,5		
#define		band1				PORTC,4
#define		crystal				PORTC,3
#define 	ADx   				B0,1    		; Encoder inputs
#define 	ADy   				B0,2
#define		lika				resultat,2		;efter subtraktion
#define		negativt			resultat,1
#define		positivt			resultat,0

;***************************************************************************************
					; MACRO DEFINITIONS för att banka till de två minnessidorna
BANK0	MACRO
		BCF 		STATUS,RP0 		;Bank 0
		ENDM

BANK1	MACRO
		BSF 		STATUS,RP0 		;Bank 1
		ENDM

;*******************************************************************
					;Macro för multiplikation

; 					Double Precision Multiplication
;
; 					A1 : A0 * B1 : B0 -> R3:R2:R1:R0
;
; 					Standard shift and add.
;					Execution time: 215 to 295 clock cycles.
; 					Code space: 22 locations
;		
; 					Cleaned up and corrected version from Microchip Ap note by BF.
;					Note: Ap note has errors! Additional mods by Scott Dattalo.
; 					A×B=R

					;Macro A × B = R 
mpy16b16: macro
		local 	m1, m2
		clrf 	R3
		clrf 	R2
		clrf	R1
		clrf	R0
		bsf 	R1, 7
m1:
		rrf 	A1, f
		rrf 	A0, f
		skpc
		goto 	m2
		movf 	B0, w
		addwf 	R2, f
		movf	B1, w
		skpnc
		incfsz 	B1, w
		addwf 	R3, f
m2:
		rrf 	R3, f
		rrf 	R2, f
		rrf 	R1, f
		rrf 	R0, f
		skpc
		goto 	m1

		endm


;******************************************************************************************


;Här talar vi om för assemblerprogrammet vilken processor vi har och vilka adresser
;som vi ska starta ifrån

		list    p=16f684			;direktiv till assemblern om vilken process. som valts

		#include <p16f684.inc>  	; processor specific variable definitions
	
		ERRORLEVEL -302 			; suppress message because of page change
		;konfigureringsordet till processorn	
		__CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF &  _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT


		org     0	     			;adress vid power on eller reset
		goto    init   				;gå till initiering av kretsen.. i slutet av programmet


			
;************************************************************************************
									;Subrutin för att läsa in analogvärdet från potentiometern, frekvensen
									;Värdet läses in med 10 bitar.. 0..1023 steg
AD3								

		bsf		ADCON0,2			;välj ad-ingång 3
		bsf		ADCON0,3
		bcf		ADCON0,4
		bsf		ADCON0,GO			;starta ad-omvandlingen
		btfsc	ADCON0,GO			;kolla om ad-omvandlingen är färdig
		goto	$-1					;vänta tills ad-omvandlingen är färdig
		BANK1						;banka till sida 1
		movfw	ADRESL				;ladda det nya låga ad-värdet från AD-omvandlaren
		BANK0						;banka tebaks till sida 0
		movwf   B0					;placera låga delen i register B0...
		movwf	VFOlow	
		movfw	ADRESH				;och höga delen
		movwf	B1					;i B1
		clrf	B2					;rensa registren B2, B3
		clrf	B3
		call	hz100				;ladda in 100Hz steg i register "A" Potentiometern finns i register "B"
		mpy16b16					;multiplicera (A × B) potentiometern med 100Hz steg
									;Nu har vi potentiometern (0...1023) multiplicerad med antal 100Hz
		call	frekband			;kolla och ladda vilket frekvensband som skall köras
		call	add32				;svaret ovan ligger i "R" och frekvensbandet i "A"  addera ihop dem

		return

;************************************************************************************
									;Subrutin för att läsa in analogvärdet från beattonen
									;Värdet läses in med 10 bitar.. 0..1023 steg
									;Med beattonen kan vi ändra frekvensen -5...0...+5 kHz

AD2								
		btfss	TX					;kolla ingång TX om det är sändning = TXoffset
		return						;då ska vi inte ha någon beat-tone

		bcf		flag,0				;reseta en hjälpflagga om det är negativt värde nedan.
									
		movfw	R0					;först måste vi rädda värdet från AD2 beräkningen
		movwf	temp0				;i temporära register
		movfw	R1
		movwf	temp1
		movfw	R2
		movwf	temp2
		movfw	R3
		movwf	temp3
									;AD funktionen  läs beatpotentiometern
		bcf		ADCON0,2			;välj ad-ingång 2 från beat-tones potten
		bsf		ADCON0,3
		bcf		ADCON0,4
		bsf		ADCON0,GO			;starta ad-omvandlingen
		btfsc	ADCON0,GO			;kolla om ad-omvandlingen är färdig
		goto	$-1					;vänta tills ad-omvandlingen är färdig
		nop
		BANK1						;banka till sida 1	
		movfw	ADRESL				;ladda det nya låga ad-värdet från AD-omvandlaren
		BANK0						;banka tebaks till sida 0
		movwf   AD2Lo				;placera låga delen i register AD2Lo
		movfw	ADRESH				;och höga delen
		movwf	AD2Hi				;i AD2Hi
									
									;genom att minska värdet med 511 (01FF)
									;så får vi fram mitten på potten = 0
									;till höger har vi 0...+5kHz
									;till vänster -5kHz....0

		movfw	AD2Lo				;ladda in potentiometern i a
		movwf	aLO
		movfw	AD2Hi
		movwf	aHI
									
		movlw	0x20				;ladda in 511  =01FF i b... testade siffror!!
		movwf	bLO
		movlw	0x02
		movwf	bHI
		call	sub16				;ropa på subrutinen a=a-b   resultatet i a
		btfsc	negativt			;kolla om svaret blev negativa siffror??
		call	invert				;gå då och compl sifrorna till positiva värden och sätt en flagga

		movfw	aLO					;flytta till registren som passar multiplikationen
		movwf	B0
		movfw	aHI
		movwf	B1
		
		btfss	crystal				;kolla vilken kristall som användes
		goto	x5
		movlw	0x98				;30.72MHz crystal
		movwf	A0					;skall multipliceras med 10Hz... 1398h....
		movlw	0x13
		movwf	A1		
							
x5		movlw	0x57				;125MHz crystal
		movwf	A0					;skall multipliceras med 10Hz... 0157h.... 
		movlw	0x01
		movwf	A1
		mpy16b16					;multiplicera A × B = R


									;nu har vi svaret i R..från multi ovan, 
		movfw	temp0				;vilket skall adderas eller subtraheras med temp0...3 från AD3, VFO potten
		movwf	A0					;flytta över tempregistren till registren passande add eller sub
		movfw	temp1
		movwf	A1
		movfw	temp2
		movwf	A2
		movfw	temp3
		movwf	A3
		btfss	flag,0				;kolla om det är negativt värde...gå mot minus?
		goto	add32				;gå mot plus  R=R+A
		goto	sub32				;gå mot minus R=R-A
		return

;***********************************************************************************
									;vi fick negativa siffror... vänd de till positiva!
invert	comf    aLO,f           	;Complement all bytes
        comf    aHI,f
        incf    aLO,f          		;Inc. low byte always
        skpnz                   	;Skip if no carry to higher bytes
        incf    aHI,f        	 	;Carry to next byte
		bsf		flag,0				;sätt en hjälpflagga att det är negativa sidan
		return

;**********************************************************************************
khz3500	
		btfss	crystal					;kolla vilken kristall som användes
		goto	x1						;30.720 MHz kristall
		movlw	0xaa					;1B6244D9
		movwf	A0
		movlw	0xaf
		movwf	A1						;testade siffror mot frekvensräknare
		movlw	0x2a	
		movwf	A2
		movlw	0x1d
		movwf	A3
		return
										;125MHz kristall
x1										;ladda in frekvensen 3500kHz
		movlw	0x0c					;072B020Ch
		movwf	A0
		movlw	0x02
		movwf	A1
		movlw	0x2b	
		movwf	A2
		movlw	0x07
		movwf	A3
		return

;***********************************************************************************
khz3600									;kolla vilken kristall som användes
		btfss	crystal					;30.72 MHz kristall
		goto	x2						;ladda in frekvensen 3600kHz
		movlw	0x00					;testade siffror
		movwf	A0
		movlw	0x04
		movwf	A1
		movlw	0x00	
		movwf	A2
		movlw	0x1e
		movwf	A3
		return

x2									;125MHz kristall
		movlw	07h					;075F6FD2h   
		movwf	A3
		movlw	5fh
		movwf	A2
		movlw	6fh	
		movwf	A1
		movlw	0d2h
		movwf	A0
		return
;************************************************************************************
khz3700
								;ladda in frekvensen 3700kHz
		btfss	crystal					;kolla vilken kristall som användes
		goto	x3						;30.720MHz kristall
		movlw	0x7f					;testade siffror
		movwf	A0
		movlw	0x56
		movwf	A1
		movlw	0xd5	
		movwf	A2
		movlw	0x1e
		movwf	A3
		return

x3									;125MHz kristall
		movlw	07h					;0793DD98h
		movwf	A3
		movlw	93h
		movwf	A2
		movlw	0ddh	
		movwf	A1
		movlw	98h
		movwf	A0
		return

;***********************************************************************************
hz100
		btfss	crystal				;kolla vilken kristall som användes
		goto	x4	
		movlw	0x71				;30.720 MHz kristall
		movwf	A0					;
		movlw	0x35				;
		movwf	A1					;trimmade siffror /UCZ
		clrf	A2
		clrf	A3
		return
		
								;125MHz kristall
x4								;100Hz step.. men lite mindre... 1024 steg =100kHz
		movlw	0x30
		movwf	A0					;D60Ch
		movlw	0x0D				;0D4E ger 100hz/SM5EUF
		movwf	A1					;trimmade siffror /UCZ
		clrf	A2
		clrf	A3
		return



;***************************************************************************
									; 32-bit Addition  R=R+A
add32   movf    A0,W
        addwf   R0,F
        movf    A1,W
        btfsc   STATUS,C
        incfsz  A1,W
        addwf   R1,F
        movf    A2,W
        btfsc   STATUS,C
        incfsz  A2,W
        addwf   R2,F
        movf    A3,W
        btfsc   STATUS,C
        incfsz  A3,W
        addwf   R3,F
		return
;*****************************************************************************

sub32 	
								;32-bit subtraktion	;R=R-A
		movf    A0, W
        subwf   R0, F			
		movf    A1, W
        btfss	STATUS,C
        incfsz 	A1, W
        subwf	R1, F
        movf    A2, W
        btfss	STATUS,C
        incfsz	A2, W
        subwf 	R2, F
        movf    A3, W			;R3 till w. Ska vara A3  / SM5EUF
        btfss	STATUS,C
        incfsz 	A3, W
        subwf 	R3, F
		return
;********************************************************************
		;subrutin för subtrahering av 2 16-bitarstal

sub16			;Subrutin för 16 bitars subtraktion a=a-b
				;samt kontroll om resultatet blev 
				;lika, mindre eller större

		clrwdt					;mata hunden så han inte bits
		clrf	resultat		;rensa flaggbitarna
		movf	bHI,w			;ladda in höga b
		subwf	aHI,w			;minska med höga a
		movwf	tempHi			;spara i reg 25
		movf	bLO,w			;ladda in låga b
		subwf	aLO,w			;minska med låga a
		movwf	tempLo			;spara i 26
		btfss	STATUS,0		;testa om det blev negativt
		decf	tempHi,1		;minska isåfall reg slask
		iorwf	tempHi,w		
		btfss	STATUS,2		;kolla om lika
		goto	$+3
		bsf		lika			;om lika sätt flagga i reg posit
		goto	$+5
		btfsc	tempHi,7		;kolla om negativt
		bsf		negativt		;negativt
		btfss	tempHi,7		;kolla om positivt
		bsf		positivt		;positivt
		movfw	tempHi			;flytta över resultatet
		movwf	aHI				;till höga a
		movfw	tempLo
		movwf	aLO				;låga a
		return

;***********************************************************************************
SendSynth  		     			;Send Tx registers as a serial data to DDS
								;snipp hämtat från nätet
					
		clrf	R4				;cleara phase registret det skall vara 0
	
        movf    R0 , W			;skicka en byte i taget till AD9850 börja med R0
        call    Write9850
        movf    R1 , W			;skicka R1
        call    Write9850
        movf    R2 , W			;Skicka R2
        call    Write9850
        movf    R3 , W			;Skicka R3
        call    Write9850
        movf    R4 , W          ;Sist skickar vi Phase word R4 som skall vara nollor
        call    Write9850
 	    bsf     FQud      		;uppdatera frekvensen i AD9850
        nop						;vänta lite...
        bcf     FQud			;sänk signalen
        bcf     FData			;nollställ databiten
        nop
        return
;*****************
Write9850						;klocka ut datat till AD9850
								;snipp hämtat från nätet
		movwf	Temp			;skriv in data biten från "w" i Temp
        movlw   8				;ladda in "8"  antalet databitar i varje byte
        movwf   BitCount		;placera i biträknaren
Loop9850
        bcf     WClk       		;data changes with -ve edge of clock
        rrf     Temp,f			;rotera åt höger
        btfss   STATUS , C		;kolla om biten som ramlade över kanten var 0 eller 1
        bcf     FData			;om "0" reset databiten
        btfsc   STATUS , C
        bsf     FData			;om "1" sätt databiten
		nop						;vänta lite...
	    bsf     WClk     		;höj clockbiten
		nop						;vänta lite
		bcf		WClk			;sänk klocksignalen
		nop						;vänta lite
		bcf		FData			;sänk datasignalen
        decfsz  BitCount,f      ;räkna ner biträknaren ett steg   
        goto    Loop9850		;om räknaren inte är 0... kör ett varv till
        return					;om räknaren var 0... denna byte är nu sänt klart

;**************************************************************************************

frekband					;rutin för att utvärdera vilken banddel som skall användas
							;genom att titta på ingångarna 0 och 1 om de är byglade eller ej till 0V
		
				
		clrf	bandreg			;rensa bandregistret
		btfsc	PORTC,5			;kolla om första biten är satt på kretskortet
		bsf		bandreg,0		;sätt isåfall bit 0 i registret
		btfsc	PORTC,4			;kolla om andra biten är satt på kretskortet
		bsf		bandreg,1		;sätt i såfall bit  i registret
	
								;registret innehåller nu antingen 1,2 eller 3
	
	
		movlw	.1				;ladda in "1" i arbetsregistret	
		subwf	bandreg,w		;minska 1 med bandregistret
		btfsc	STATUS,Z		;om det blev "0" så skall band 1 användas
		call	khz3700			;gå till khz3700 rutinen
		movlw	.2				;ladda in "2" i arbetsregistret
		subwf	bandreg,w		;minska 2 med bandregistret
		btfsc	STATUS,Z		;om det blev "0" så skall band 2 användas
		call	khz3500			;gå till khz3500 rutinen
		movlw	.3				;ladda in "3" i arbetsregistret
		subwf	bandreg,w		;minska 3 med bandregistret
		btfsc	STATUS,Z		;om det blev "0" så skall band 3 användas
		call	khz3600			;gå till khz6000 rutinen
	
		return



;**********************************************************************
main
	
		call	AD3				;läs in potentiometerläget för VFO
		call	AD2				;läs in potentiometerläget för beat-tonen
		call	SendSynth		;skicka resultatet till AD9850
		goto	main

;********************************************************************
	
							;Initiering av kretsen
							;detta sker endast vid uppstart av processorn
init   
	
		BANK0		
		clrf	PORTA
		clrf	PORTC
	
		movlw	b'00000111'
		movwf	CMCON0			;comparators OFF
		movlw   b'10000001'  	;ladda 0000 0001 = vänsterjust, AD=on 
		movwf   ADCON0     		;placera i ADCON0	
		BANK1
		movlw	b'11111111'		;välj vilka pinnar som skall vara IN=1
		movwf	TRISA			
    	movlw	b'00111000'		;välj vilka pinnar som skall vara IN=1
		movwf   TRISC
		movlw   b'11110001'     ;4MHz Clk, IntOsc, SysClk via IntOsc
    	movwf   OSCCON
		movlw	b'01110000'		;aktivera AD-modulen ADCON1
		movwf	ADCON1			;placera i ADCON1
		movlw	b'00001100'		;välj vilka pinnar som skall vara analoga in 1=analog
		movwf	ANSEL
		bcf		OPTION_REG,7	;enable pullup
		movlw	b'00100000'		;pullup on RA5
		movwf	WPUA
		BANK0					;välj registersida
		clrf	PORTA			;rensa utgångarna
		clrf 	PORTC
	
		goto    main   			;gå till huvudprogrammet
	
		end

