;+---------------------------------------------------------------------------------+
;
;	dds.asm		Program for Software-DDS with AT90S1200
;	
;	Version 1.0
;
;	fq = 13 MHz
;
;	Historie:	18.08.2001	Start of Development
;			19.08.2001	Hardmeasurement shows that VCXO middle	
;					frequency is 10,0033 MHz -> Adder adjusted
;					10MHz VCXO locks perfectly after adjustment
;			15.09.2001	remote control from master CPU with 3wirebus
;					implementation started (ext. interrupt based)
;					status LED on PD5 introduced
;			20.09.2001	First tests on adder-changing with
;					interrupt service routine successful
;			21.09.2001	Data download algorithm in isr implemented
;			23.09.2001	Bug fixed: LSL in isr was after bit reading
;					  and is no moved before reading
;					Extensive testing -> adder download from
;					from master processor works perfectly
;					Version 1.0 released
;
;+---------------------------------------------------------------------------------+

; Remote control via 3wire bus - Description:
; The master sends a positive pulse to INT0 which triggers the external interrupt.
; This stops the main loop and the slave (which is running this software) awaits
; the adder data. Each bit is put to the data pin, then a positive pulse on the
; clock pin orders the slave to take over that bit. After all bits have been received
; (the slave simply counts the number of clock pulses), the interrupt service routine
; is left automatically and the DDS starts to run the new values.
; (add3 and add2 are fix for the full operating range are are not send from the master;
; add1 is loaded first, then add0, each with the MSB first, LSB last)

; Hardware is a AT90S1200 running on 5Volts Vcc with 13MHz clock frequeny
.include "1200def.inc"

; This is the 4Byte accu which is holding the 'phase-information' 
.def accu0 = r16
.def accu1 = r17
.def accu2 = r18
.def accu3 = r19

; This is the adder. At every main loop cycle this value is added
; to the 4Byte accu (see above)
.def add0 = r20
.def add1 = r21
.def add2 = r22
.def add3 = r23

; The dummy register is used for all kind of procedures which require a
; temporary variable to shift some data through etc.
.def dummy = r24

; For counting the bits in the data transfer algorithm
.def counter = r25

; Another dummy register from the the lower 16 registers. Has limited
; usage by commands, e.g. ldi does not work with this
.def lrdummy = r1

.equ twb_clock = 0b00000010	; 3wirebus (PORTD) Bit1 is the clock signal
.equ twb_data  = 0b00000001	; 3wirebus (PORTD) Bit0 is the data signal

.equ led       = 0b00100000	; status LED bit on PORTD

;------------------- all definitions done, lets go -----------------------

	; This software used interrupts, so the interrupt service routines
	; must be listed at the fix interrupt vectors 0x0000 to 0x0003
	
	rjmp reset	; 0x0000 interrupt vector for a reset
	rjmp ext_int0	; 0x0001 interrupt vector for the external interrupt
	reti		; 0x0002 interrupt vector for Timer 0 overflow (not used)
	reti		; 0x0003 interrupt vector for Analog Comparator (not used)

;--------------------------- main loop -----------------------------------
loop:
	add accu0, add0		; add 4Byte adder to 4Byte accu
	adc accu1, add1
	adc accu2, add2
	adc accu3, add3
	
	out PORTB, accu3	; send highest Byte of accu to Port B
	
	rjmp loop		; next loop

;------------ end of main loop, subroutines from here on -----------------


;++++++++++++++++++++++++++ reset handling routine +++++++++++++++++++++++
reset:

	;set Port B, MSB as output for the DDS signal
	;(other Port B pins are not connected anyhow)
	ldi dummy, 0b10000000
	out DDRB, dummy
	
	;set Port D pins for input/output
	;PD0 	data	input
	;PD1	clock	input
	;PD2	int0	input
	;PD3	NC	input
	;PD4	NC	input
	;PD5	LED	output
	;PD6	NC	input
	;PD7 does not exist
	ldi dummy, 0b00100000
	out DDRD, dummy

	;clear acuu to start at zero
	clr accu0
	clr accu1
	clr accu2
	clr accu3
	
	;to have a default startfrequency that is within the pulling range
	;of the VCXO we preset the adder with a valid value
	;setup adder registers for an output frequency of 10MHz/256 = 39,0625kHz
	;main loop has 7 cycles -> mainloop frequency is 1,857 MHz (fq=13MHz)
	;=> max. DDS output frequency is 928,571 kHz
	
	;for 10MHz VCXO frequency without shift the
	;adder value for 39,0625kHz is 39,0625kHz * 2^31/928,571kHz = 90338897
	;in Hex: 05 62 76 51
	
	;for 10MHz + 117ppm (which is the max. necessary shift that occurs when
	;a 850MHz carrier shall be shifted for 100kHz) the adder value is
	;90349520, in Hex: 05 62 9F D0
	
	;=>for all cases add3 and add2 are always 05 and 62 hex respectively
	;only add1 and add0 
	
	ldi add0, 0x51		;preset the variable adder bytes
	ldi add1, 0x76		;add0 and add1 for zero frequency shift
	
	ldi add2, 0x62		;set the fixed adder bytes add2 and add3
	ldi add3, 0x05
		
	ldi dummy, 0b01000000	;switch on the external interrupt
	out GIMSK, dummy
	
	ldi dummy, 0b00000011   ;set the external interrupt to positive edge sensitive
	out MCUCR, dummy
		
	ldi dummy, 0x80		;global interrupt enable (from now on interrupts can happen
	out SREG, dummy 	;if the specific interrupt is switched on)

	rjmp loop		;now lets go to the main loop and start the DDS-work


;++++++++++++++++++ ext. interrupt service routine +++++++++++++++++++++++
; this subroutine is triggered by a high-level on PD2 (int0)
; it reads two times 8 bit as the new adder value (first add1 then add0)
; the serial bitstream for both byts starts with the MSB and ends with the LSB
ext_int0:
		
	
	clr add1		;start with 0 as new value
	ldi counter, 8		;prepare for reading of 8 bits
byte_one:
	lsl add1		;shift the value one bit left
	rcall wait_clk		;wait for a full clock pulse
	in dummy, PIND		;read from Port D
	andi dummy, twb_data	;mask out the data bit
	or add1, dummy		;add the read bit to the new value
	dec counter		;decrease the counter
	brne byte_one		;  and go up to read next bit if not zero

	clr add0		;same procedure again for the adder byte zero
	ldi counter, 8

byte_zero:
	lsl add0	
	rcall wait_clk
	in dummy, PIND
	andi dummy, twb_data
	or add0, dummy
	dec counter
	brne byte_zero

	;cpi add0, 0x90		; for debugging -> check if data is received correctly
	;brne isr_end		;   by comparing it with a fixed value and sending this value from master
	;ldi dummy, led		;switch on status LED if condition was true
	;out PORTD, dummy 

isr_end:
	reti			;return to DDS loop 
				;(also enables again the global interrupt)


;++++++++++++++++++ wait for a full clock pulse ++++++++++++++++++++++++
wait_clk:

wait_clk_high:
	in dummy, PIND		;read the PORTD
	andi dummy, twb_clock	;check for the clock-bit
	breq wait_clk_high	;repeat loop until clock-bit is high

wait_clk_low:
	in dummy, PIND		;read the PORTD
	andi dummy, twb_clock	;check for the clock-bit
	brne wait_clk_low	;repeat loop until clock-bit is low

	ret			;return to calling routine
	