;*********************************
;Lazy guy's resonant frequency meter for L and C measurement
;Copyright(C)2002,2003 by Richard Cappels projects@cappels.org
;http://www.projects.cappels.org

;Uses AT90S2313 or similar operating at 4 MHz.
;Input is D5 
;LED output is D6 - goes low during frequency measurements and high while sending data.

;*********************************

.include "2313def.inc"     

.equ     clock = 4000000          ;clock frequency
.equ     baudrate = 9600          ;choose a baudrate

.equ     baudconstant = (clock/(16*baudrate))-1


;***** 16 bit binary-to-packed-BDC Subroutine Register Variables
.equ	AtBCD0		=13		;address of tBCD0
.equ	AtBCD2		=15		;address of tBCD1

;***** other register assignments for frequency meter
.def 	reallybigloopcounter 	   =r1
.def	presetreallybigloopcounter =r2	;1, $0A number of seconds when loopmultiplier is = $64
.def	presetloopmultiplier	   =r3	;$64, $01 number of 10 ms increments
.def	presetdelaycounter 	   =r4	;$F4 at all times (for 4 MHz)
.def	presetdelaycounter1 	   =r5	;$27 st all times (for 4 MHz)
.def	dacvalue		   =r6	;Actuall pulse amplitude for pulse generator.
.def	daczero			   =r7
.def	tBCD0			   =r13	;BCD value digits 1 and 0
.def	tBCD1			   =r14	;BCD value digits 3 and 2
.def	tBCD2			   =r15	;BCD value digit 4
.def	fbinL			   =r16	;binary value Low byte
.def	fbinH			   =r17	;binary value High byte
.def	cnt16a			   =r18	;loop counter
.def	tmp16a			   =r19	;temporary value
.def	inbyteh 		   =r20 ;higher byte for asci-hex conversion	
.def	temp 			   =r21 ;general purpose register
.def 	delaycounter 		   =r22
.def 	delaycounter1 		   =r23
.def 	loopmultiplier 		   =r24
.def	inchar 			   =r25 ;char destined to go out the uart
;.def	XL 			   =r26 ;char coming in from the uart
;.def	XH 			   =r27	;lower byte for asci-hex conversion


.org     $00
	rjmp	reset
	rjmp	reset
	rjmp	reset
	rjmp	reset
	rjmp	reset
	rjmp	reset
	rjmp	reset
	rjmp	reset	
	rjmp	reset
	rjmp	reset
	rjmp	reset

hellomessage:
	.db     "LGM031227I 100ms"
crlfmessage:
	.db     $0A,$0D
	.db     00,00

kilohertz:
	.db     " KHz"
	.db     00,00

reset:     
	ldi     temp,low(ramend)
	out     spl,temp     		;set spl
	ldi     temp,baudconstant     
	out     ubrr,temp     		;load baudrate
		 	
	ldi	temp,$FF 		;Weak pullups on all pins on PORTD  
	out	portd,temp	 	
	ldi	temp,$40		;Make D6 an output
	out 	ddrd,temp			


	rcall	delaystart		;One second delay for display to start up.		
	rcall	TypeGreeting		;Display greeting.
	rcall	delaystart
		
	rcall	set100ms		;Measurement period			

measurefreuqency:			;Measeure and send forever.
	rcall	measurefreq
     	rjmp     measurefreuqency

set10ms:
	ldi	temp,$01
	mov	presetreallybigloopcounter,temp
	ldi	temp,$01
	mov	presetloopmultiplier,temp	 
	ldi	temp,$F4
	mov	presetdelaycounter,temp
	ldi	temp,$27
	mov	presetdelaycounter1,temp 	  
	ret


set100ms:
	ldi	temp,$01
	mov	presetreallybigloopcounter,temp
	ldi	temp,$0A
	mov	presetloopmultiplier,temp	 
	ldi	temp,$F4
	mov	presetdelaycounter,temp
	ldi	temp,$27
	mov	presetdelaycounter1,temp 	  
	ret
     
set1s:
	ldi	temp,$01
	mov	presetreallybigloopcounter,temp
	ldi	temp,$64
	mov	presetloopmultiplier,temp	 
	ldi	temp,$F4
	mov	presetdelaycounter,temp
	ldi	temp,$27
	mov	presetdelaycounter1,temp 	  
	ret
          
rs_send:
     	sbi     ucr,txen     		;set sender bit
     	sbis    usr,udre     		;wait till register is cleared
     	rjmp  	rs_send     
     	out     udr,XL     		;send the byte
     	cbi     ucr,txen     		;clear sender bit
     	ret               		;go back
     
crlf:                    ;send carriage return and line feed.
     	ldi     ZH,high(2*crlfmessage) 	;Load high part of byte address into ZH
     	ldi     ZL,low(2*crlfmessage)	;Load low part of byte address into ZL
     	rcall     sendstring
	ret


sendstring:          	;call with location of string in Z
	lpm                    		;Load byte from program memory into r0
	tst	r0			;Check if we've reached the end of the message
	breq	finishsendstering     	;If so, return
	mov	XL,r0
	rcall	rs_send
	adiw	ZL,1 			;Increment Z registers
	rjmp	sendstring
finishsendstering:
	ret


sendline:          	;send a string terminated in cariage return and line feed
               		;call with location of start of string in Z               
     	rcall 	sendstring
     	rcall	crlf
     	ret


TypeKHz:
	ldi     ZH,high(2*kilohertz)	;Load high part of byte address into ZH
	ldi     ZL,low(2*kilohertz)	;Load low part of byte address into ZL
	rcall	sendstring          	;sent it.
	ret


TypeGreeting:
	ldi     ZH,high(2*hellomessage)	;Load high part of byte address into ZH
	ldi     ZL,low(2*hellomessage)	;Load low part of byte address into ZL
	rcall	sendline          	;sent it.
	ret


byte_to_asciihex:     			;convert byte in XH to ascii in inbyteh,nbytel
	mov     inbyteh,XH
	lsr     inbyteh			;convert the high nybble to ascii byte
	lsr     inbyteh
	lsr     inbyteh
	lsr	inbyteh
	subi	inbyteh,$D0		;add $30
	cpi	inbyteh,$3A     
	brlo	PC+2			;If less than 9 skip next instruction
	subi	inbyteh,$F9		;add 8 to ASCII (if data greater than 9)
     					; byte in inbyteh represents upper nybble that was in XH at start
	andi	XH,0b00001111		;convert the lower nybble to ascii byte
	subi	XH,$D0			;add $30
	cpi 	XH,$3A     
	brlo 	PC+2 			;If less than 9 skip next instruction
	subi	XH,$F9			;add 8 to ASCII (if data greater than 9)
     	ret				;byte in inbyteh represents upper nybble that was in XH at start
	


sendbyte:      ;send byte contained in XH to terminal
	rcall 	byte_to_asciihex
     	mov     XL,inbyteh
     	rcall 	rs_send
     	mov     XL,XH
     	rcall  	rs_send
	ret
sendbinarybyte:
	ldi	temp,$08
stillsendingbinary:			;rotate byte through carry; send 0 or 1 depending on carry bit
	ldi	XL,$30
	rol	XH
	brcc	dontsendone
	ldi	XL,$31
dontsendone:
     	rcall  	rs_send
     	dec 	temp
     	brne	stillsendingbinary    	
	ret


;*******************
;*******************
;*******************	MEASURE FREQUENCY
;*******************
;*******************


measurefreq:
	ldi	temp,$90		;enable uart interrupts	-disabled when executed
	out	ucr,temp
	sei
	ldi	temp,$00		;set tccr1a (contorl of 16 bit counter) to all zeros 
	out	tccr1a,temp	
	ldi	temp,$00		;clear 16 bit counter
	out	tcnt1h,temp
	out	tcnt1l,temp
	ldi	temp,$06		;enable input to counter 1 
	out	tccr1b,temp
	mov	reallybigloopcounter,presetreallybigloopcounter
	cbi	PORTD,6			;set portD bit 6 low (LED Counting indicator on)
reallybigloop:										
	mov	loopmultiplier,presetloopmultiplier	;****10 ms = $01, 100 ms =  $0A, 1 second = $64	
bigloop:		
	mov	delaycounter,presetdelaycounter		; set values for delay of 10 ms. delaycounter = $F4, delaycounter1 = $27
	mov	delaycounter1,presetdelaycounter1		
		
dealylooproutine:		; 10 millisecond dealy loop

	dec	delaycounter
	cpi	delaycounter,$00
	brne	dealylooproutine	
	dec	delaycounter1
	cpi	delaycounter1,$00
	brne	dealylooproutine			
	nop	
	nop
	dec 	loopmultiplier
	brne	bigloop	
	dec	reallybigloopcounter
	brne	reallybigloop
	ldi	temp,$00		;stop 16 bit counter 
	out	tccr1b,temp
	cli				;disable interrupts
	sbi	PORTD,6			;set port d bit 6 high (turn LED off)
displaythedata:			
			;Display the data
			
	in	fbinL,tcnt1l		;move counter contents to input for number conversion
	in	fbinH,tcnt1h		
	rcall	bin2BCD16		;Convert to 2.5-byte packed BCD format
	rcall 	crlf		
	mov	XL,tBCD2
	ldi	temp,$30
	add	XL,temp
	rcall 	rs_send
	mov	XH,tBCD1
	rcall	sendbyte
	ldi	XL,'.'		;Put decimal point after first three digits
	rcall	rs_send
	mov	XH,tBCD0	;since leading digit on high byte is always zero, dont' sent it.
	rcall	sendbyte
	rcall	TypeKHz		;Write "Khz" after the frequency.
	ret	;return to calling routine to eventually go back to loop


;**** A P P L I C A T I O N   N O T E   A V R 2 0 4 ************************
;* Title:		BCD Arithmetics
;* Version:		1.1
;* Last updated:	97.07.04
;* Target:		AT90Sxxxx (All AVR Devices)
;*
;* Support E-mail:	avr@atmel.com
;* 

;* DESCRIPTION
;* This Application Note lists subroutines for the following Binary Coded
;* Decimal arithmetic applications:
;*
;* Binary 16 to BCD Conversion (special considerations for AT90Sxx0x)



;***** Code

bin2BCD16:
	ldi	cnt16a,16	;Init loop counter	
	clr	tBCD2		;clear result (3 bytes)
	clr	tBCD1		
	clr	tBCD0		
	clr	ZH		;clear ZH (not needed for AT90Sxx0x)
bBCDx_1:lsl	fbinL		;shift input value
	rol	fbinH		;through all bytes
	rol	tBCD0		;
	rol	tBCD1
	rol	tBCD2
	dec	cnt16a		;decrement loop counter
	brne	bBCDx_2		;if counter not zero
	ret			;   return

bBCDx_2:ldi	r30,AtBCD2+1	;Z points to result MSB + 1
bBCDx_3:
	ld	tmp16a,-Z	;get (Z) with pre-decrement
;----------------------------------------------------------------
;For AT90Sxx0x, substitute the above line with:
;
;	dec	ZL
;	ld	tmp16a,Z
;
;----------------------------------------------------------------
	subi	tmp16a,-$03	;add 0x03
	sbrc	tmp16a,3	;if bit 3 not clear
	st	Z,tmp16a	;	store back
	ld	tmp16a,Z	;get (Z)
	subi	tmp16a,-$30	;add 0x30
	sbrc	tmp16a,7	;if bit 7 not clear
	st	Z,tmp16a	;	store back
	cpi	ZL,AtBCD0	;done all three?
	brne	bBCDx_3		;loop again if not
	rjmp	bBCDx_1		



delaystart:
	rcall	set1s
	mov	reallybigloopcounter,presetreallybigloopcounter
Areallybigloop:										
	mov	loopmultiplier,presetloopmultiplier	;****10 ms = $01, 100 ms =  $0A, 1 second = $64	
Abigloop:		
	mov	delaycounter,presetdelaycounter		; set values for delay of 10 ms. delaycounter = $F4, delaycounter1 = $27
	mov	delaycounter1,presetdelaycounter1			
Adealylooproutine:		; 10 millisecond dealy loop
	dec	delaycounter
	cpi	delaycounter,$00
	brne	Adealylooproutine	
	dec	delaycounter1
	cpi	delaycounter1,$00
	brne	Adealylooproutine			
	dec 	loopmultiplier
	brne	Abigloop	
	dec	reallybigloopcounter
	brne	Areallybigloop
	ret	;return to calling routine




.exit

