;Dick Cappels' Project Pages
;Contents ©2002 Richard Cappels All Rights Reserved. email projects@cappels.org
; Liability for use is assumed and no warraty given. See disclaimer at http://projects.cappels.org

;Use of information presented on this page is for personal, nonprofit educational
;and noncommercial use only. This material (including object files) is copyrighted
;by Richard Cappels and may not be republished or used directly for commercial
;purposes without explicit permission For commercial license,
click HERE.
;


;HOME
;Firmware for Digital RF Field Strength indicator ;with LED Display using Atmel AT90SA2313
;*********************************
; 7 Segment LED Display millivoltmeter
;*********************************

.include "2313def.inc"     


;***** 16 bit binary-to-packed-BDC Subroutine Register Variables

.equ	AtBCD0	=13		;address of tBCD0
.equ	AtBCD2	=15		;address of tBCD1

.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

;*****other register assignments************

.def	segmentvalue = r0
.equ	digit1 	 = $01		;store of first digit's binary value
.def	digit1val = r1		;storage of first digit's 7 segment image
.equ	digit2 	 = $02		;storage of second digit's binary value
.def	digit2val = r2		;storage of second digit's 7 segment image
.equ	digit3	   =$03		;storage of third ditit's binary value
.def	digit3val = r3		;storage of thrid digit's 7 segment image
.def 	offsetl = r4		;offsete to zero reading, low byte
.def	offseth	=r5		;offset to zero reading, high byte
.def	flagreg = r6		;bit 0 true enables measurement during interrupt
.def	intcountl = r7		;24 bit timer for power management
.def	intcountm =r8		;count low, middle, and high bytes
.def	intcounth = r9
.def	dimflag = r10		;if not zero, then dim the display
.def	rezeroedflag = r11	;nonzero if rezero has occured
.def	temp = r20	;general purpose variabl
.def	digitvalue = r21	;value of current digit
.def	digitpointer = r22	;pointer to digit to be written
.def	interruptcount = r23	;keeps track of which task to execute next
.def	slowdownmeasurements =r24	;counter to cause less frequent measurements
				;when I set this up at +5V and when operating from a 3V battery
.equ 	dimtime = $AE		;dim time 3 mintues
.equ	sleeptime =$E8		;shut down time at 4 minutes

.ORG $0000
	rjmp start
.ORG $0006
	rjmp timerservice
	

	

start:
	ldi temp,RAMEND      ;Init Stack Pointer
	out SPL,temp

	ldi	temp,$00	;clear measurement during interrupt enable flag
	mov	flagreg,temp

	ldi	temp,$FF	;segment drivers
	out	DDRD,temp
	
	ldi	temp,$FC	;digit drivers and power to the external circuit
	out	DDRB,temp
	
	ldi	temp,$3C	;digit drivers and power to the external circuit
	out	PORTB,temp

	ldi 	temp, $00		;set up comparitor
	out 	ACSR, temp  
	
	ldi	temp,$FF
	mov	dimflag,temp
	
		;set interrupt counter to zero
	clr	intcountl
	clr	intcountm
	clr	intcounth

	clr	rezeroedflag

	ldi 	temp, $03	; use temp to initaize prescaler 03
	out 	TCCR0, temp
	ldi 	temp,$00	; use temp to initialize counter
	out 	TCNT0,temp
	ldi 	temp, $02	; use temp to initialize TIMSK
	out 	TIMSK,temp
	
	ldi	temp,$FF	;clear measurement during interrupt enable flag
	mov	flagreg,temp
	
	sei			;ENABLE THE TIMER INTERRUPTS


loop:
	;check to see if its time to dim the display

	ldi	temp,dimtime	;skip dimming if intcountm is >< dimtime
	eor	temp,intcountm
	brne	dontdim
	clr	dimflag	;make register $FF if its time.
	com	dimflag
		
dontdim:

		
	;check if its past time to sleep the machine

	ldi	temp,sleeptime	;skip sleeping if intcountm is >< sleep
	eor	temp,intcountm
	brne	dontsleep
	
	;routine to sleep the machine

	cli			;disble all interrupts

	clr	temp
	out	TCCR1b,temp	;stop timer 1
	out	TCCR0,temp	;stop timer 0
	
	ldi	temp,$18	;make sure watch dog timer is disabled
	out	wdtcr,temp
	ldi	temp,$10
	out	wdtcr,temp
	
	cbi	ACSR,$07	;turn off comparitor
; 
	clr	temp		;make I/O pins inputs
	out 	DDRB,temp
	out 	DDRD,temp
	
	ldi temp, $30
	out MCUCR,temp		; set MCUCR bit 4 to set power down mode for sleep and
				; set MCUCR bit 5 to enable sleep
	sleep ; enter sleep. Reset to wake up.
	
dontsleep:

		;Set up rezeroing of A-to-D after running some short time.
		;rezeroiong only happens once and is done in the A-to-D routine inside
		;the T0 interrupt.
	tst	rezeroedflag
	brne	dontrezero
	ldi	temp,$02	
	eor	temp,intcountm
	brne	dontrezero
	ldi	temp,$5A
	mov	rezeroedflag,temp  ;$5A is the secret word to cause rezeroing

dontrezero:



	rjmp 	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		






	
	
	
	
putsegments:				; Enter with value (0..9) in digitvalue, 
					;  and destingation digit in digitpointer
     	ldi     ZH,high(2*ledlut)     	; Load high part of byte address into ZH
     	ldi     ZL,low(2*ledlut)     	; Load low part of byte address into ZL
     	add     ZL,digitvalue		; Add index (which is digitvalue) to LUT address
     	ldi	temp,$00	
     	adc   	ZH,temp  		
     	lpm                    		; Load byte from program memory into r0
     	mov	ZL,digitpointer		;Point Z register to the desination memroy location
     	ldi	ZH,$00
	st	Z,r0		;move contets of r0 to location pointed to by digitpointer
     	ret


	
dimifnecessary:
			;check if its past time to start dimming

	tst	dimflag
	breq 	donedim
dimit:	;routine to dim the display

dimmingtime:			;leave the chars on some some
	ldi	fbinL,$00
	ldi	fbinH,$03
dimmingtimeloop:
	dec 	fbinL
	cpi 	fbinL,$00
	brne 	dimmingtimeloop
	dec 	fbinH
	cpi 	fbinH,$00
	brne 	dimmingtimeloop
				;now that thime's up, blank display to dim.
	
	cbi	PORTB,2		;blank all digits
	cbi	PORTB,3		
	cbi	PORTB,4



donedim:
ret	

;************************


	
timerservice:		;service timer interrupt

;round-robin multitasking, different task on each interrupt
;Display digit1
;display digit2
;display digit3
;acquire new ADC value and put in digit registers
	push	temp
	push	xl
	push	xh
	lds	temp,$5F	;push status register onto stack
	push	temp
	push	fbinL
	push	fbinH	
	
	
	ldi	temp,$01	;keep timer for dimming and shut down
	add	intcountl,temp
	clr	temp
	adc	intcountm,temp
	adc	intcounth,temp

	inc	interruptcount	;point to the next task
	andi	interruptcount,$03 ;only four tasks allowed
	
	cbi	PORTB,2		;blank all digits
	cbi	PORTB,3		;by making all cathode drivers inputs
	cbi	PORTB,4

				;round-robin starts here
	cpi	interruptcount,$00
	breq	showdigit1
	cpi	interruptcount,$01
	breq	showdigit2
	cpi	interruptcount,$02
	breq	showdigit3
	cpi	interruptcount,$03
	breq	AtoDConversion
	rjmp	endoftimerservice				;go back and wait for another interrupt

showdigit1:
	out 	PORTD,digit1val ;copy digit value to output port
	sbi	PORTB,4	;enable this digit
	rcall	dimifnecessary
	rjmp	endoftimerservice


showdigit2:
	out 	PORTD,digit2val ;copy digit value to output port
	sbi	PORTB,3	;enable this digit
	rcall	dimifnecessary
	rjmp	endoftimerservice



showdigit3:
	out 	PORTD,digit3val ;copy digit value to output port
	sbi	PORTB,2	;enable this digit
	rcall	dimifnecessary
	rjmp	endoftimerservice



AtoDConversion:
;convert input voltage to a value, set flag for foreground routine to convert to BCD and store in digit memories
	sbrs	flagreg,$01		;if flag not set, don't do conversions
jrelay:	rjmp	finishedconverison

	inc	slowdownmeasurements
	cpi	slowdownmeasurements,$08
	brne	jrelay			;don't do it, didn't wait long enough
	clr	slowdownmeasurements	;set counter to zero for next time


	;set PORTB bit 0 to 0 to assure no current out of pins
	cbi	PORTB,0

	
	;set DDRB, bit 0 an output. This discharges the capacitor on the I/O pin.
	sbi	DDRB,0

	ldi 	fbinL,$00
	ldi 	fbinH,$08		
		
turnondelay:
	dec 	fbinL
	cpi 	fbinL,$00
	brne 	turnondelay
	
	;set DDRB bit 0 to input
	cbi	DDRB,$00

	
	ldi	xh,$00
	ldi	xl,$00
	
waitforramp:
	sbiw	xh:xl,1
	in 	temp,acsr
	sbis 	acsr,aco 		;branch back to waiting if comparitor not tripped
	rjmp 	waitforramp		
	
					;Zero ADC by measuring offset. Only happens once.
					; after running about a
	ldi	temp,$5A	
	eor	temp,rezeroedflag
	brne	dontrezeroagain
	mov	offsetl,xl   
	mov	offseth,xh	
	ldi	temp,$FF
	mov	rezeroedflag,temp	
	clr	dimflag
	
dontrezeroagain:
	
	sub	xl,offsetl	;subtract offset to zero reference reading
	sbc	xh,offseth
	brpl	positivenumber
	ldi	xh,0		;if we'r here, its negative, so set to zero;	
	ldi	xl,0		;negative field strength doesn't make any sense in the real world
	positivenumber:		;now we have the number, we just need to convert it.



	;	Check for overflow
	mov	temp,xh
	andi	temp,$0F
	subi	temp,$03
	brpl	overflow
	brne	defintelynotoverflow
	
	mov	temp,xl
	subi	temp,$E7
	brpl	overflow
	
defintelynotoverflow:
	
	mov	fbinL,xl	;move counter contents to input for number conversion
	mov	fbinH,xh	
	rcall	bin2BCD16	;Convert to 2.5-byte packed BCD format	
				;result: tBCD2:tBCD1:tBCD0 = $054321	

	mov	digitvalue,tBCD0	; now send it to the LED display
	andi	digitvalue,$0F
	ldi	digitpointer,$01
	rcall	putsegments

	mov	digitvalue,tBCD0
	lsr	digitvalue
	lsr	digitvalue
	lsr	digitvalue
	lsr	digitvalue
	ldi	digitpointer,$02
	rcall	putsegments

	mov	digitvalue,tBCD1
	andi digitvalue,$0F
	ldi	digitpointer,$03
	rcall	putsegments
	rjmp	finishedconverison
	
overflow:
	
		
	;set display for overflow indication
	ldi	digitvalue,$0A
	ldi	digitpointer,$01
	rcall	putsegments

	ldi	digitvalue,$0A
	ldi	digitpointer,$02
	rcall	putsegments

	ldi	digitvalue,$0A
	ldi	digitpointer,$03
	rcall	putsegments
	
	
	
finishedconverison:

endoftimerservice:
	pop	fbinH
	pop	fbinL
	pop	temp
	sts	$5F,temp ;restore status register
	pop 	xh
	pop	xl
	pop	temp

	reti



ledlut:		;LED Look-Up Table, Segent codes for 0..9 on 7 segment display
.db	$3F,$06,$5B,$4F,$66,$6D,$7d,$07,$7F,$6F,$09,$08




 

 

HOME

;Please see the liability disclaimer on the HOME page.

; Contents ©2002 Richard Cappels All Rights Reserved.;http://projects.cappels.org/