


;*********************************
;Copyright(C)2002,2003,2004. 2005 by Richard Cappels projects@cappels.org
;http://www.projects.cappels.org
;USES 32 bit binary to packed BCD routine from Atmel applination note.
;Uses ATTiny2313 or similar operating at 20 MHz. Woudl work on AT90S2313 if you 
;could clock it that fast.
;Input is D5 

;*********************************

;nfmtr050524	20 MHz clock version, runs at 10 MHz. Measured 4 MHz clock at 4,000,082.
;nfmtr040916A	Turn on decimal point and zeros to right of it in modes 2 and 3.
;nfmtr040902A	Set input of counter 1 to clock on positive edge. Display ID with lcd.
;nfmtr040901I	Mode 0 (counting) eliminated. Max freq. is 5 MHz.
;nfmtr040901H	Readings limited to 2 times per second.
;nfmtr040901F	Problem printing commas and decimal points after zeros corrected.
;nfmtr040901G	Any char from terminal advances mode, except R resets in mode 4.
;nfmtr040901F	Decimal points in the right places.
;nfmtr040901E	Commas in the right places.
;nfmtr040901D	Leading zero suppression.
;nfmtr040901C	Working with 2 line LCD. All modes tested. Overflow works.
;nfmtr040901B	Counting added.
;nfmtr040901A	Label timebase.
;nfmtr040831E	With working 100 millisecond to 100 second. No user interface.
;nfmtr040831D	32 bit basic frequency meter. No user interface.
;nfmtr040831C	1 second delay almost correct.
;nfmtr040831B	Can send 9 1/2 digit number. Delay routine to 5.05 seconds at 10 MHz
;nfmtr040831	Some pieces of code in place. Not nearly working yet.

.include "TN2313def.inc"     

.equ     clock = 20000000          ;clock frequency
.equ     baudrate = 9600          ;choose a baudrate

.equ     baudconstant = (clock/(16*baudrate))-1


.def	delay0		= r2		;delay0..delay2 are delay counters.
.def	delay1		= r3
.def	delay2		= r4


.def	delayreload0	= r6		;delayreload0..delayreload2 are reload values for delay counters.
.def	delayreload1	= r7
.def	delayreload2	= r8

.def	iterations	= r9		;Number of 1 second iterations.

.def	temp		= r16		;General purpose scratch register.
.def	byteA0		= r18		;byteA0..byteA4 for binary to bcd conversion.
.def	byteA1		= r19
.def	byteA2		= r20
.def	byteA3		= r21
.def	byteA4		= r22

.def	flagreg		= r23




;Definition of flagregister bits
;0	lsb of mode select
;1	middle bit of mode select
;2	msb of mode select
;3	
;4	set to indicate that counting is to stop.
;5
;6	set to indicate no more leading zeros
;7	set if 32 bit overflow occured.




.org    $00
	rjmp	reset			;Power-on and reset vector
.org	$05
	rjmp	T1OFLW			;16 Bit timer overflow
.org	$07
	rjmp	UARTRCV			;UART receive interrupt


reset:     
	ldi     temp,low(ramend)
	out     spl,temp		;set spl.
	ldi     temp,baudconstant     
	out     ubrr,temp		;load baudrate.


	ldi	temp,$00
	out	DDRB,temp

	ldi	temp,$FF
	out	PORTB,temp
		 			 	
				 	
	ldi	temp,$00
	out 	ddrd,temp			


	ldi	temp,$01		;Weak pullup on UART receive pin.
	out	PORTD,temp

     	sbi     ucr,txen     		;Enable UART transmitter.
     	sbi	ucr,rxen		;Enable UART receiver.
	sbi	ucr,rxcie		;Enable UART receive interrupts.
			

; shoot rcall	Delay1Second		;Wait 2 seconds for display to come on.
; shoot rcall	Delay1Second


     rcall	crlf			;Start new line
	rcall	typemessage		;Send ID message for two seconds before starting.

	rcall	Delay1Second		;Display for 2 seconds before starting measurements.
	
	ldi	flagreg,1		;Set measurement mode to 1 second frequency measurement.


	sei				;Enable interrupts


	rjmp	Main			;Start main execution loop.




TypeSeconds:
	push	temp
	ldi	temp,'s'
	rcall	EmitUart
	pop	temp
	ret




Delay:					;Maximum delay is 50529045 cycles (5.05 seconds at 10 MHz).
	mov	delay2,delayreload2	
moretime3:	
	mov	delay1,delayreload1
moretime2:
	mov	delay0,delayreload0
moretime1:
	dec	delay0
	brne	moretime1
	dec	delay1
	brne	moretime2
	dec	delay2
	brne	moretime3
	ret


Delay1Second:
	ldi	temp,100	;50 is 1 second	
	mov	delayreload0,temp	
	ldi	temp,0
	mov	delayreload1,temp
	ldi	temp,255
	mov	delayreload2,temp
	rcall	Delay


	ldi	temp,143 ;225	
	mov	delayreload0,temp	
	ldi	temp,250
	mov	delayreload1,temp
	ldi	temp,2
	mov	delayreload2,temp
	rcall	Delay
	ret	


measurefreq:

	cbr	flagreg,0b10000000	;Clear 32 bit overflow flag.

	ldi	temp,$00		;Set TCCR1A (control of 16 bit counter) to all zeros.
	out	tccr1a,temp	
	
	ldi	temp,$00		;Clear 16 bit counter.
	out	tcnt1h,temp
	out	tcnt1l,temp
	
	clr	ZL			;Clear Z register, which is the overflow counter.
	clr	ZH
	
	ldi	temp,0b10000000		;Enable overflow interrupts from 16 bit timer.
	out	TIMSK,temp
	ldi	temp,0b00000111		;Enable input to 16 bit counter (Timer/Counter 1). .		
	out	tccr1b,temp

AnotherSecond:
	rcall	Delay1Second		;Delay 1 second.

	dec	iterations		;Measure more than 1 second if necessary/
	brne	AnotherSecond

	ldi	temp,$00		;Stop 16 bit counter.
	out	tccr1b,temp
	out	TIMSK,temp		;Disable overflow interrupts.
	
	ret







Meas1Sec:				;Label measurement as with 1 second timebase.
	ldi	temp,'1'
	rcall	EmitUart
	rcall	TypeSeconds
	rcall	crlf	
			
	ldi	temp,1			;Take 1 second measurement.
	mov	iterations,temp
	rcall	measurefreq
	rcall	displaycounter
	rjmp	main

Meas10Sec:				;Label measurement as with 1 second timebase.
	ldi	temp,'1'
	rcall	EmitUart
	ldi	temp,'0'
	rcall	EmitUart
	rcall	TypeSeconds
	rcall	crlf	
			


	ldi	temp,10			;Take 10 second measurement.
	mov	iterations,temp
	rcall	measurefreq
	rcall	displaycounter
	rjmp	main

Meas100Sec:				;Label measurement as with 1 second timebase.
	ldi	temp,'1'
	rcall	EmitUart
	ldi	temp,'0'
	rcall	EmitUart
	ldi	temp,'0'
	rcall	EmitUart
	rcall	TypeSeconds
	rcall	crlf

	ldi	temp,100		;Take 100 second measurement.
	mov	iterations,temp
	rcall	measurefreq
	rcall	displaycounter
	rjmp	main

Main:	



	mov	temp,flagreg
	andi	temp,0b00000111
	
	
	cpi	temp,1
	breq	Meas1Sec
	
	cpi	temp,2
	breq	Meas10Sec
	
	cpi	temp,3
	breq	Meas100Sec
	
	cpi	temp,4
	breq	counting
	

	rjmp	main




Delay100ms:
	ldi	temp,130			
	mov	delayreload0,temp	
	ldi	temp,254
	mov	delayreload1,temp
	ldi	temp,10
	mov	delayreload2,temp
	rcall	Delay

	ldi	temp,1			
	mov	delayreload0,temp	
	ldi	temp,1
	mov	delayreload1,temp
	ldi	temp,190
	mov	delayreload2,temp
	rcall	Delay
	ret


Counting:				;Count and display results until a flag goes true.
	andi	flagreg,0b01101111	;Make sure counting stop and overflow flags are not set.	
	ldi	temp,$00		;Set TCCR1A (control of 16 bit counter) to all zeros.
	out	tccr1a,temp	
	ldi	temp,$00		;Clear 16 bit counter.
	out	tcnt1h,temp
	out	tcnt1l,temp
	clr	ZL			;Clear Z register, which is the overflow counter.
	clr	ZH
	ldi	temp,0b10000000		;Enable overflow interrupts from 16 bit timer.
	out	TIMSK,temp
	ldi	temp,0b00000111		;Enable input to 16 bit counter (Timer/Counter 1). .
	out	tccr1b,temp

KeepCounting:				;Wait for flag to go high, signaling end of counting.
	sbrc	flagreg,4
	rjmp	CountingDone
	rcall	displaycounter
	rcall	Delay100ms		;Limit sampling to 2 times per second
	rcall	Delay100ms		;to make display smoother looking.
	rcall	Delay100ms
	rcall	Delay100ms
	rcall	Delay100ms
	rjmp	KeepCounting
CountingDone:
	ldi	temp,$00		;Stop 16 bit counter.
	out	tccr1b,temp
	out	TIMSK,temp		;Disable overflow interrupts.
	rjmp	main





displaycounter:				;Display contents of counter via UART.
	clr	XL			;Use XL to count the digits for placement of commans and decimal points
	andi	flagreg,0b10111111	;Clear nonzero flag.
	in	byteA0,tcnt1l		;move counter contents to input for number conversion
	in	byteA1,tcnt1h	
	mov	byteA2,ZL		;Move the upper 16 bits of count into the appropriate registers.
	mov	byteA3,ZH

	
	rcall	Conv32bit2bcd		;Get binary data in byteA0..byte 3 into byteA0..byteA4.
	
	mov	temp,byteA4
	rcall	bcdEmitUart
	mov	temp,byteA3
	rcall	bcdEmitUart
	mov	temp,byteA2
	rcall	bcdEmitUart
	mov	temp,byteA1
	rcall	bcdEmitUart
	mov	temp,byteA0
	rcall	bcdEmitUart
	
	sbrc	flagreg,7		;If flagreg bit 7 (32 bit overflow) is set, send warning.
	rcall	warnoflow
	rcall	crlf
	ret




bcdEmitUart:		;Emit two digite BCD in temp through UART.
	push	temp
	swap	temp
	andi	temp,0b00001111
	ori	temp,$30
	rcall	EmitNoLeadZero
	pop	temp

	andi	temp,0b00001111
	ori	temp,$30
	rcall	EmitNoLeadZero
	ret



PlaceMark:				;Place comma or period as appropriate. 
					;based on digit count (XL) and mode (flagreg 0..2)			
	push	temp			;Preserve temp.
	mov	temp,flagreg		;Make state byte from mode and digit counter
	andi	temp,0b00000111		;Mask off all but mode value from flagreg.
	cpi	temp,4
	brne	NotMode4		;If in mode 4, make it look like mode 1.
	ldi	temp,1
NotMode4:	

	swap	temp			;Get mode into upper nybble.
	or	temp,XL			;Get digit count into lower nybble.
	cpi	temp,$02
	breq	docomma
	cpi	temp,$05
	breq	docomma
	cpi	temp,$08
	breq	docomma
	cpi	temp,$11
	breq	docomma
	cpi	temp,$14
	breq	docomma
	cpi	temp,$17
	breq	docomma
	cpi	temp,$23
	breq	docomma
	cpi	temp,$26
	breq	docomma
	cpi	temp,$32
	breq	docomma
	cpi	temp,$35
	breq	docomma


	cpi	temp,$1A
	breq	dodpoint
	cpi	temp,$29
	breq	dodpoint
	cpi	temp,$38
	breq	dodpoint
		
	cpi	temp,$0A
	breq	doohdot
		
	
;shoot	

	pop	temp
	ret



docomma:
	ldi	temp,','
	rcall	EmitUart
	pop	temp
	ret

dodpoint:
	ldi	temp,'.'
	rcall	EmitUart
	pop	temp
	ret

doohdot:
	ldi	temp,'0'
	rcall	EmitUart
	ldi	temp,'.'
	rcall	EmitUart
	pop	temp
	ret



EmitNoLeadZero:				;Emit number if not a leading zero, otherwise a space.
	inc	XL			;Increment digit count
					;Make sure decimal point and zeros shown in modes 2,3.
	mov	XH,flagreg		;Make state byte from mode and digit counter
	andi	XH,0b00000111		;Mask off all but mode value from flagreg.	
	swap	XH			;Get mode into upper nybble.
	or	XH,XL			;Get digit count into lower nybble.
	cpi	XH,$29			;Set flag to show zeros to rt of decimal point
	brne	NO29			;in mode 2.
	ori	flagreg,0b01000000	;Set flagreg to allow sending leading zeros
NO29:
	cpi	XH,$38			;Set flag to show zeros to ret of decimal point
	brne	NO38			;In mode 3.
	ori	flagreg,0b01000000	;Set flagreg to allow sending leading zeros
NO38:
	cpi	temp,'0'
	breq	ItsAZero
	ori	flagreg,0b01000000	;Set nonzero flag.
	rcall	EmitUart
	rcall	PlaceMark
	ret
ItsAZero:
	sbrs	flagreg,6
	rjmp	QX21
	rcall	emitUart
	rcall	PlaceMark
	ret
QX21:
	cpi	XL,10
	breq	EmitUart
	ldi	temp,$20
	rjmp	EmitUart




EmitUart:				;Send RFChar via UART, preserves content of RFChar
	sbis    usr,udre		;Wait for data to arrive.
	rjmp    EmitUart     
	out     udr,temp		;Send byte.
	ret			

crlf:
	ldi	temp,$0A
	rcall	EmitUart
	ldi	temp,$0D
	rcall	EmitUart
	ret

TypeMessage:				;Type greeting
	push	ZL
	push	ZH
	ldi	ZH,high(2*Message)	;Load high part of byte address into ZH
	ldi	ZL,low(2*Message)	;Load low part of byte address into ZL
	rcall	SendRomstring		;Send it
	pop	ZH
	pop	ZL
	ret
	
	
	
Message:
	.db     "nfmtr040916A" 
	.db	$0A,$0D
	.db     $00,$00




SendRomstring:	;Send string pointed to by ZH,ZL.
	lpm				; Load byte from program memory into r0
	tst	r0			; Check if we've reached the end of the message
	breq	stringdone		; If so, return
	mov	temp,r0
	rcall	EmitUart
	adiw	ZL,1			; Increment Z registers
	rjmp	SendRomstring
stringdone:
    	ret



WarnOflow:				;Type "+" to indicate overflow
	push	temp
	ldi	temp,'+'
	rcall	EmitUart
	pop	temp
	ret





Conv32bit2bcd:				;Input: r18.19,20.21 (binary); out: r18,19,20,21,22 (bcd)
					;Listed above as lsB first, msB last.
		push	r16		;Save registers not needed for 
		push	r17
		push	r23		;input or output.
		push	r24
	

		rcall	Bin4BCD		;Do the conversion
	


		mov	r18,r20
		mov	r19,r21
		mov	r20,r22
		mov	r21,r23
		mov	r22,r24
		
		
		pop	r24		;Restore saved registers.
		pop	r23
		pop	r17
		pop	r16
		ret
		
		;code below not used.

	
		push	r24		;Re-arrange data in registers
		push	r23
		push	r22
		push	r21
		push	r20
		
		pop	r18
		pop	r19
		pop	r20
		pop	r21
		pop	r22
	
	
		pop	r24		;Restore saved registers.
		pop	r23
		pop	r17
		pop	r16
		ret





;******** 32 bit binary to packed BCD routines from Atmel application note *************
; Call bin4bcd			r18r19r20r21       >>>         r20r21r22r23r24


Bin2BCD20:      mov     r16,r20         ;for compatibility with Math32
                mov     r17,r21         ;
Bin2BCD16:      ldi     r22,0xff      ;initialize digit 4
binBCD_4:       inc     r22           ;
                subi    r16,low(10000);subiw fbin,10000
                sbci    r17,high(10000)
                brcc    binBCD_4        ;
                ldi     r21,0x9f      ;initialize digits 3 and 2


binBCD_3:       subi    r21,0x10      ;
                subi    r16,low(-1000);subiw fbin,1000
                sbci    r17,high(-1000)
                brcs    binBCD_3        ;
binBCD_2:       inc     r21           ;
                subi    r16,low(100)  ;subiw fbin,100
                sbci    r17,high(100) ;
                brcc    binBCD_2        ;
                ldi     r20,0xa0      ;initialize digits 1 and 0
binBCD_1:       subi    r20,0x10      ;
                subi    r16,-10       ;subi fbin,10
                brcs    binBCD_1        ;
                add     r20,r16     ;
binBCD_ret:     ret                     


;* r18:r19:r20:r21  >>>  r20:r21:r22:r23:r24  <== this one.
;*           hex                              dec
;*       r18r19r20r21       >>>         r20r21r22r23r24

Bin4BCD:  
	      	rcall   Bin2BCD20       ;
                clr     r23           ;initial highest bytes of result
                ldi     r24,0xfe      ;
binBCD_loop:    subi    r20,-0x33     ;add 0x33 to digits 1 and 0
                sbrs    r20,3         ;if bit 3 clear
                subi    r20,0x03      ;       sub 3
                sbrs    r20,7         ;if bit 7 clear
                subi    r20,0x30      ;       sub $30
                subi    r21,-0x33     ;add 0x33 to digits 3 and 2
                sbrs    r21,3         ;if bit 3 clear
                subi    r21,0x03      ;       sub 3
                sbrs    r21,7         ;if bit 7 clear
                subi    r21,0x30      ;       sub $30
                subi    r22,-0x33     ;add 0x33 to digits 5 and 4
                sbrs    r22,3         ;if bit 3 clear
                subi    r22,0x03      ;       sub 3
                sbrs    r22,7         ;if bit 7 clear
                subi    r22,0x30      ;       sub $30
                lsl     r18           ;
                rol     r19           ;shift lower word
                rol     r20           ;through all bytes
                rol     r21           ;
                rol     r22           ;
                rol     r23           ;
                rol     r24           ;
                brmi    binBCD_loop     ;7 shifts w/o correction of MSD
                rol     r17           ;since Bin2BCD r17 = 0xff
                brcc    binBCD_ret 	;shoot      ;  so as to do 16_lsl in total
                subi    r23,-0x33     ;add 0x33 to digits 7 and 6
                sbrs    r23,3         ;if bit 3 clear
                subi    r23,0x03      ;       sub 3
                sbrs    r23,7         ;if bit 7 clear
                subi    r23,0x30      ;       sub $30
                subi    r24,-0x03     ;add 0x03 to digit 8 only
                sbrs    r24,3         ;if bit 3 clear
                subi    r24,0x03      ;       sub 3
                rjmp    binBCD_loop     ;
                

T1OFLW:		push	temp		;Save temp then save status register in temp.
		in	temp,sreg
	
		adiw	ZL,1		;Increment 16 bit Z register upon overflow of 16 bit counter.

		brne	NoOverflow
		sbr	flagreg,0b10000000	;Set 32 bit overflow flag (very reare event)

NoOverflow:	out	sreg,temp	;Restore status regiser then restore temp.
		pop	temp
		reti

;************End of binary to bcd conversion routine**********



UARTRCV:
					;UART receive interrupt
	push	temp			;Save temp then save status register.
	in	temp,sreg
	push	temp
	in	temp,usr
	in	temp,udr		;Read the char.

	andi	temp,$DF		;Make upper-case ascii

	cpi	temp,'R'		;If 'R' reset counters if mode 4 (Counting mode).
	breq	resetcounter
conint2:
	
	
	mov	temp,flagreg		;Increment mode (flagret 0..2)
	andi	temp,0b00000111		;Maximum count is 4
	inc	temp
	cpi	temp,$05	
	breq	modemaxed	
contint:	
	andi	flagreg,0b11111000
	or	flagreg,temp	
	ori	flagreg,0b00010000	;Set flag to leave counting mode.
					;Set timer counters to time out early.

	ldi	temp,1	
	mov	iterations,temp		
	mov	delayreload0,temp	
	mov	delayreload1,temp
	mov	delayreload2,temp	

intdone:
	pop	temp			;Restore status regiser then restore temp.
	out	sreg,temp	
	pop	temp
	reti

modemaxed:
	ldi	temp,1			;Lowest mode number is 1.
	rjmp	contint
	
resetcounter:				;If in mode 4, counter mode, reset counter.

	mov	temp,flagreg
	andi	temp,0b00000111
	cpi	temp,4
	brne	conint2			;If not mode 4, continue as if any other key.

	ldi	temp,$00		;Clear 16 bit counter.
	out	tcnt1h,temp
	out	tcnt1l,temp
	clr	ZL			;Clear Z register, which is the overflow counter.
	clr	ZH
	rjmp	intdone	


.exit















