;******************************************************
;Version LMX060331D
;
;PLL FM Transmitter control firmware for AT90S2313 or ATtin2313
;controlling a National Semiconductor LMX1601 PLL chip. Both chips		
;are clocked at 4 Mhz. Accuracy of the clock affects accuracy of the
;PLL frequency.
;
;
;The AUX PLL is used since its operational range includes
;the FM broadcast band. The 4 Mhz clock from the microcontroller
;is devided by 320 to make a 12.5 kHz reference frequency. The AUX
;prescaler devides by 8, so each count into the 16 bit AUX N Counter
;counts for 100 kHz. Therefore, a value of 1000 in the AUX N Counter 
;will result the PLL operating at 100.0 MHz. Each increase in the
;value written to the AUX N Counter will increase the oscillator frequency
;by 100 kHz. So, a value of 1001 will make the PLL drive the oscillator
;to 100.1 kHz. 
;
; The SendAUX_N: routine adds an offset of 825 to the contents of
;the register named "channel", and channel is a single 8 bit variable.
;Thus, the maximum range of the PLL is:
;Minimum frequency = 100 kHz X (825 + 0) = 82.5 kHz.
;Maximum frequency = 100 kHz x (825 + 255) = 108.0 MHz
;
;Tests in the Plusbutton and Minusbutton routines restirct the
;range of the PLL to 88.0 to 108.0 MHz.
;
;Read the LMX1601 datasheet carefully.
;
							
;******************************************************
.nolist							;Don't include 2313 defitiions in the
.include "2313def.inc"  		;listing file.
.list
;******************************************************



;************* MEMO OF I/O PORT ASSIGNMENTS *************

;Port B pin assignments
	;B0		Comparitor + input					INPUT	
	;B1		Comparitor - input					INPUT
	;B2		- Button							INPUT with pullup
	;B3		+ Button							INPUT with pullup
	;B4		Oscillator Enable					OUTPUT
	;B5		LMX1601 Clock Pin, Microwire		OUTPUT
	;B6		LMX1601 Data Pin, Microwire			OUTPUT
	;B7		LMX1601 Latch Enablem Microwire		OUTPUT 


;PORT D pin assignments
	;D0
	;D1
	;D2
	;D3
	;D4		Daignostic - high during interrupts	OUTPUT
	;D5		Status LED. 1 = ON					OUTPUT
	;D6		Beep output							OUTPUT
	;D7


;************* DEFINE FEGISTER USAGE *************

;Variables for shifting into LMX1601 AUX_N register.
	.def	AUX_Nr2		= r5			
	.def	AUX_Nr1		= r6			
	.def	AUX_Nr0		= r7

;Variables for VOX timer.
	.def	voxt0		= r8
	.def	voxt1		= r9

;Variables for button and EEPROM management.
	.def	temp 		= r16			;Scratch register.
	.def	temp2		= r17			;Scratch register.
	.def	delayone	= r18			;Used for button filtering delay.
	.def	delaytwo	= r19			;Used for button filtering delay.
	.def	Dreg		= r20			;PORTD data output buffer
	.def	EEPROMpntr	= r21			;Pointer to EEPOROM location to 
										;be read/written. Bits zero 

	.def	flagreg		= r22			;Flags for firmware.			
		
;LMX1601 loader Register assignments.															
	.def	delayc		= r2			;Variable for delay routine.
	.def	secondelay	= r3			;Variable for delay routine.
	.def	channel		= r4
	.def 	tempreg 	= r23			;General purpose working 
										;register for non-interrupt 			
										;times

;Notaitonal (dummy) register assignments.
	;def	XL			= r26			;Used for Channel calculation.
	;def	XH			= r27			;Used for Channel calculation.
	;def	YL			= r28			;AUX_N counter value calculation
	;def	YH			= 2r9			;AUX_N counter value calculation
	;def	ZL			= r30			;EEPROM write delay
	;def	ZH			= r31			;EEPROM write delay


	;flagreg bit assignments			Single bit flags.
	;0
	;1
	;2
	;3
	;4
	;5	Set in On mode to indicate that oscillator is on.
	;6	Set by comparitor interrupt routine.
	;7	If set, data in dreg needs to be written to EEPROM.


;************* ESTABLISH CONSTANTS ************* 



	.equ	DataPin 	= 6				;Bit 6 of PORTB is the Microwire 
										;data pin.
	.equ	ClockPin 	= 5				;Bit 5 of PORTB is the Microwire 
										;data pin.							
	.equ	LEPin 		= 7				;Bit 7 of PORTB is the Microwire 
										;data pin.

	.equ	waitL		= 0
	.equ	waitH		= 3						;waitH is the number of 10 
										;second increments.

										;Initial data for LMX1601 registgers
										;Make bit 7 of AuxR2 = 1 to look at 
										;Aux_R otuput, make it 1 to look at 		
										;Aux_N output. Maker 4 upper bits (FoLD) 
										;= 0101 for Aux lock with xtal.
	.equ	AuxR2	= 0b11000000		;Most significant byte. Upper two bits 
										;contain bits 16 and 17. The rest are ;			
										;unused.
	.equ	AuxR1	= 0b11000101		;Middle byte ;Div by 320 for 12.5 kHz 
										;from 4 MHz xtal
	.equ	AuxR0	= 0b00000000		;Least significant byte. Devide by 8.



	.equ	AUX_N2	= 0b00000000		;With a 12.5 kHz reference frequency, 
										;osc freq = 100 kHz X N
	.equ	AUX_N1	= 0b11111010		;Frequency can be changed by 		
										;incrementing and decrementing N.
	.equ	AUX_N0	= 0b10000001
										;The AUX_N vlaues	00000000	11111010	
										;10000001 get 100 MHz out of the 
										;synthesizer.
										
										;The AUX_N vlaues	$00  $FA	$81  get 
										;100 MHz out of the synthesizer
	.equ	Main_R2	= 0b11110000
	.equ	Main_R1	= 0b11000000
	.equ	Main_R0	= 0b00000010

	.equ	Main_N2	= 0b00000000		;These values should not matter.
	.equ	Main_N1	= 0b00000000
	.equ	Main_N0	= 0b00000011
		
	.equ	nominalchannel = 175		;Channel 175 corresponds to 100 MHz. 
										;82.5 MHz + (175 X 100 kHz) = 100 MHz.



;************* START EXECUTABLE CODE ************* 

.org $0000
		rjmp 	start
.org ACIaddr 		
		rjmp	ANA_COMP				;Analog Comparator Handle (Strictly 								
										;speaking, the jump is not needed this 
										;time.)
										   
ANA_COMP:	
		push	tempreg					;Save tempreg on stack
		in		tempreg,SREG			;Temporarily store the Status register.
		sbi		PORTD,4					;Diagnotic - set D4 high (So one can see 
										;the interrupts externally)
		sbi		PORTD,5					;Turn the status LED on. (Make sure it 
										;is on)
		sbr		flagreg,0b01000000		;Set comparitor tripped flag for 
										;foreground control of indicator light.
		cbi		PORTD,4					;Diagnotic - set D4 low.
		out		SREG,temp				;Restore Status register.
		pop		tempreg					;Restore tempreg.	
		reti

on:										;Turn oscillator on.
		push	tempreg					;Turn on the oscillator and reset the 
										;vox timer.							
		sbi		PORTB,4					;Turn oscillator poin on.
		sbr		flagreg,0b00100000		;Set oscillator running flag.
		sbi		PORTD,5					;Turn status LED on.
		clr		voxt0
										;Below is the vox timer, which shuts off 
										;the oscillator if a preset amount
										;of time passes without an analog 
										;comparitor interrupt occurring.

;test 									modify the code between "test" and "
										;endtest" to shorten timer period for 							
										;test pruposes.

;		ldi		tempreg,20				;This ine ACTIVE for TEST	(For 9 
										;second timeout, make this value 20)
;		mov		voxt0,tempreg			;This ine ACTIVE for TEST	
;		ldi		tempreg,1				;This ine ACTIVE for TEST	(For 9 second tiemout, make this value 1)
		ldi		tempreg,30				;This line NOT ACTIVE for test. About 10 
										;seconds per count.
;endtest
		mov		voxt1,tempreg			;Each count in voxt1 is worth about 9 
										;seconds.
		pop		tempreg
		ret


off:									;Turn oscillator off, clear indicators.
		cbi		PORTB,4					;Turn off oscillator.
		cbr		flagreg,0b00100000		;Clear oscillator running flag.
		cbi		PORTD,5					;Make sure power LED is in OFF state.
		cbi		ACSR,ACIE				;Disable Analog Comparator interrup.							
		ldi		tempreg,10				;Delay for a second or two so oscillator 
										;off glictch does not retrigger.
Powerup2:
		dec		tempreg
		breq	PowerupDone2
		rcall	DelayLoader				;Wait for power supply to settle before 
										;loading LMX1601
		rjmp	Powerup2	
PowerupDone2:
		sbi		ACSR,ACIE				;Enable Analog Comparator interrupt.
		ret		    	

start:			;COMES HERE AFTER POWER-ON RESET
		ldi 	r16,RAMEND      		;Init Stack Pointer
		out 	SPL,r16
		ldi		tempreg,10				;DelayLoader at startup.
Powerup:
		dec		tempreg
		breq	PowerupDone
		rcall	DelayLoader				;Wait for power supply to settle before 
										;loading LMX1601
		rjmp	Powerup	
		
PowerupDone:		
		ldi		tempreg,0b00001100		;Set Port B pullups.
		out		PORTB,tempreg	
		ldi 	tempreg,0b11110000		;Set port B direction bits.
		out 	DDRB,tempreg	
		rcall	DelayLoader				;DelayLoader before loading
		ldi		temp, AUX_N2			;Load variables for LMX AUX_N register.
		mov		AUX_Nr2,temp						
		ldi		temp, AUX_N1
		mov		AUX_Nr1,temp	
		ldi		temp, AUX_N0
		mov		AUX_Nr0,temp
										;Get the oscillator channel data from the EEPROM, bute $AA
		ldi		EEPROMpntr,$AA			;Get channel number from EEPROM.
		rcall	ReadEE		
		rcall	LoadLMX					;Load initial control bits into LMX1601 
										;registers.
		ldi 	temp,	00				;Set PORTD as output.
		out 	PORTD,	temp			;Set bits to initial value.
		ldi		temp,	$FF
		out		DDRD,	temp	
		clr		flagreg
		ldi		ZL,		waitL
		ldi		ZH,		waitH
		rcall	on						;Initially turn the oscillator on. It 
										;will time out if there is no stimulus.

		ldi		temp,(ACI<<1)			;Clear interrupt flag and ACIS1/ACIS0...
		out		ACSR,temp				;to select interrupt on toggle.
		sei								;Enablfsbre global interrupts.
		sbi		ACSR,ACIE				;Enable Analog Comparator interrupt.
		ldi		temp,255
		rcall	Buzzloop

Loadbuttons:							;********Main loop entry point.	******** 
										;Make LED blink, working with comparitor interrupt routine.
		sbrc    flagreg,5				;In in On mode, then turn on LED.
		sbi		PORTD,5
		sbrc    flagreg,5				;In in On mode, then turn on Oscillator.
		sbi		PORTB,4
		sbrs	flagreg,6				;Turn on if comparitor has beentripped, and reset comparitor flag.
		rjmp	NotTripped
		rcall	on
		cbi		PORTD,5 				;Turn off LED
		cbr		flagreg,0b01000000		;Clear the camparitor interrupt flag.
NotTripped:

		dec		ZL						;Wait some number of times through this loop before writing 					
										; channel number to EEPROM.
		brne	noprog
		dec		ZH
		brne	noprog
		ldi		ZL,	waitL
		ldi		ZH,	waitH
		sbrs	flagreg,7	
		rjmp	noprog
		ldi		EEPROMpntr,$AA
		rcall	WriteEE 				;Write cuttent channel number to EEPROM.
		andi	flagreg,0b01111111
noprog:
		dec		voxt0
		brne	NoShutoff
		dec		voxt1
		brne	NoShutoff
		rcall	off

NoShutoff:
		in		temp	,PINB			;Get the button status.
		andi	temp,0b00001100			;Only bits 2 and 3 can contain button 
										;information.
		rcall	delay					;Wait debounce period.
		in		temp2,	pinB			;If button changed, start over
		andi	temp2,0b00001100		;Only bits 2 and 3 can contain button 
										;information.
		cp		temp,	temp2
		brne	loadbuttons
		rcall	InterpretButtons
		rjmp 	loadbuttons

InterpretButtons:						;Interpret buttons routines called by 			
										;this must terminate with ret 			
										;instruction.
		cpi		temp,0b00000100			;Is "+" button down?
		breq	Plusbutton
		cpi		temp,0b00001000			;Is "-" button down?
		breq	Minusbutton
		ret								;Both butttns case taken care of in debounce.


Plusbutton:								;Increment channel number and send new 
										;AUX_N value.
		mov		temp,channel
		cpi		temp,$FF				;Skip if channel number is already 255 (corresponds to 108 MHz).
		breq	DontIncrement
		inc		channel					
		rcall	SendAUX_N
DontIncrement:
		rcall	on						;Assure that the oscillator is on while being adjusted.
		rjmp	Buttonack

Minusbutton:							;Increment channel number and send new 
										;AUX_N value.
		mov		temp,channel
		cpi		temp,55
		breq	DontDecrement			;Skip if channel value is already 55 
										;(corresponds to 88 MHz).
		dec		channel					
		rcall	SendAUX_N
DontDecrement:
		rcall	on						;Assure that the oscillator is on while being adjusted.
		rjmp	Buttonack				

delay:									;Delay for button debounce and noise 
										;filtering.
		ldi		delayone,200 			;Count of 200 @ 4 MHz should cuase about 
										;39 ms for electrical noise..
outerloop:								
		ldi		delaytwo,0	
innerloop:
		dec		delaytwo
		brne	innerloop

		dec		delayone
		brne	outerloop
		ret

writeEE:								;Write data in channel to location 
										;EEPROMpntr points to.
		cli								;Disable interrupts.
		sbic	EECR,EEWE				;If EEWE not clear,
		rjmp	writeEE					;Wait.
		out		EEAR,EEPROMpntr			;Output address
		out		EEDR,channel			;Output data
		sbi 	EECR,EEMWE 				;Set master write enable.
		sbi		EECR,EEWE				;Set EEPROM Write strobe,
		ldi		temp,100				;Set number of beep cycles,
		rcall	Buzzloop				;Beep the beeper.
		sei								;Enable interrupts.
		ret


ReadEE:									;Read from EERPOM. Enter with address in 
										;EEPROMpntr. Returns with data in 
										;channel.
		sbic	EECR,EEWE				;If EEWE not clear
		rjmp	ReadEE					;Wait more
		out		EEAR,EEPROMpntr			;Output address
		sbi		EECR,EERE				;Set EEPROM Read strobe
		in		channel,EEDR			;Get data
		ret




Buttonack:								;Emit a keyclick, set EEPROM write flag 
										;& timer, output PORTD, then wait for 
										;button up.
		ldi		ZL,		waitL			;Set waiting time until programming the 
										;EEPROM			
		ldi		ZH,		waitH			
		ori		flagreg,0b10000000		;Set flag to write to EEPROM after time 
										;is up.
		ldi		temp,50					;Atart keyclick code.
Buzzloop:								;Emit a tone out of D6, the number of 
		cbi		PORTD,5					;Turn the status LED off.										;cycles = contents of temp.	
		sbi		PORTD,6
		rcall	delay2
		cbi		PORTD,6
		rcall	delay2
		dec		temp
		brne	Buzzloop				;end keyclick code.

		out		PORTD,	dreg			;Output PORTD data to the output port.
		sbrs	dreg,	7				;Set DDRD,0 according to dreg,7
		rjmp	D0Low2				
		sbi		DDRD	,0			
		rjmp	D0Done2
D0Low2:
		cbi		DDRD,	0	
D0Done2:
		sbi		PORTD,5					;Turn the status LED on (if it beeped, it must be on).

Wait4ButtonsUp:
		in		temp,	PINB			;Wait for all buttons to be up before 
										;proceeding.
		andi	temp,0b00001100			;Buttons are only connected to bits 2 
 										;and 3.
		cpi		temp,0
		breq	Bothbuttons				;If both buttons are down, go interpret 
										;this special case.
		rcall	delay					;Debounce
		in		temp2,	PINB
		andi	temp2,0b00001100		;Buttons are only connected to bits 						
										;2 and 3.
		cp		temp,	temp2
		brne	Wait4ButtonsUp
		cpi		temp,0b00001100			;Both buttons up?
		brne	Wait4ButtonsUp;
		ret

Bothbuttons:
		rcall	on						;Assure that the oscillator is on while being adjusted.
		ldi		temp,nominalchannel		;Set channel number to nominal 
										;(Channel 175 gets 100 MHz 
										;operation)
		mov		channel,temp
		rcall	SendAUX_N
		rjmp	Buttonack				;Reset channel number to default and 
										;send new AUX_N value.
	

delay2:									;Setts the frequency for the beeps.
		ldi		delayone,1 				;Delay for 1/2 cycle of button click.
outerloop2:
		ldi		delaytwo,150	
innerloop2:
		dec		delaytwo
		brne	innerloop2

		dec		delayone
		brne	outerloop2
		ret



shiftout:								;Clock MSB of tempreg into LMX1601 
										;Microwire interface, exits with tempreg 		
										;shifted left one bit.
		cbi		PORTB,DataPin			;Clear PORTB,Datapin
		lsl		tempreg					;Get bit 7 into carry
		brcc		s1
		sbi		PORTB,DataPin			;If carry set, set PORTB,Datapin
s1:
		sbi		PORTB,ClockPin			;Toggle Clock high then low.
		nop
		cbi		PORTB,ClockPin
		nop
		ret

LoadData:
		sbi		PORTB,LEPin
		nop
		cbi		PORTB,LEPin
		ret



DelayLoader:							;Short delay routine
		clr		delayc					;used varioius places.
outerloop9:
		clr		secondelay	
innerloop9:
		dec		secondelay
		brne	innerloop9
		dec		delayc
		brne	outerloop9
ret





LoadLMX:								;Aux_N values are calculated in  SendAUX_N subroutine.			
	ldi		tempreg,AuxR2				;Load the values into AuxR into LMX1601
	rcall	shiftout
	rcall	shiftout

	ldi		tempreg,AuxR1
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout

	ldi		tempreg,AuxR0
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
		
	rcall	LoadData
		

	ldi		tempreg,Main_R2				;Load Main_R into LMX1601
	rcall	shiftout
	rcall	shiftout

	ldi		tempreg,Main_R1
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout

	ldi		tempreg,Main_R0
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout

	rcall	LoadData
	rcall	SendAUX_N


	ldi		tempreg,Main_N2				;Load AUX_N into LMX1601
	rcall	shiftout
	rcall	shiftout

	ldi		tempreg,Main_N1
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout

	ldi		tempreg,Main_N0
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout

	rcall	LoadData

	ret





SendAUX_N:								;Add counter to the offset then load 
										;into AUX_Nr2..AUX_Nr0, and then load 				
										;into LXM1601.
	ldi		XH,0
	mov		XL,channel
	ldi		YH,3
	ldi		YL,57
	clc
	adc		YL,XL						;Add the channel number to the constant 
										;offset (825)
	adc		YH,XH


	mov		temp,YH						;Get YH bits 3 and 4 into AUX_N2 bits 7 
										;and 6 respecitively. 
	lsl		YH
	lsl		YH
	lsl		YH
	lsl		YH
	andi	YH,0b11000000				;YH bits 0 and 1 are now in positions 7 
										;and 6 and other bits are zero.

	mov		tempreg,AUX_Nr2				;Get the current value of AUX_Nr2.
	andi	tempreg,0b00111111			;Clear bits 7 and 6.
	or		tempreg,YH					;Or new bits into it.
	mov		AUX_Nr2,tempreg				;Save AUX_Nr2 value with new values for 
										;bits 7 and 6.

	lsl		temp						;Work with copy of YH data. Move bits 1 
										;and 0 into positions 7 and 6.
	lsl		temp
	lsl		temp
	lsl		temp
	lsl		temp
	lsl		temp
	andi	temp,0b11000000				;Bits are now in poistions 7 and 6, and 
										;all other bits are zero.
	clr		AUX_Nr1						;Clear AUX_Nr1 because all bits for ;										
										;AUX_Nr1 will be replaced by oring in.	
	or		AUX_Nr1,temp				;YL bits 1 and 0 have been moved to 				
										;AUX_Nr1 bits 7 and 6					

										;Get the six upper bits of YL into the 
										;lower six bits of AUX_Nr1.
	mov		temp,YL						;Save a copy of YL in temp.
	lsr		temp
	lsr		temp
	andi	temp,0b00111111
	or		AUX_Nr1,temp				;Not the upper six bits of YL have been 
										;placed in the lower six bit positions 
										;of AUX_Nr1.
	
	lsl		YL							;Get two lower bits from YL into the two 
	lsl		YL							;uppper bits of AUX_Nr0.	
	lsl		YL
	lsl		YL
	lsl		YL
	lsl		YL
	andi	YL,0b11000000				;Bits are now in poistions 7 and 6, and 
										;all other bits are zero.
	mov		temp,AUX_Nr0				;Clear AUX_Nro bits 7 and 6 in 	
										;preparation to or new values into the 
										;register.	
	andi	temp,0b00111111
	or		temp,YL
	mov		AUX_Nr0,temp								
	

	mov		tempreg,AUX_Nr2		
	rcall	shiftout
	rcall	shiftout

	mov		tempreg,AUX_Nr1
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout

	mov		tempreg,AUX_Nr0
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout
	rcall	shiftout

	rcall	LoadData
	ret									;Finished calculating data and loading 
										;AUX_N register in LMX1601
	
	
.exit									;Assembler will stop at this line.


