;ATMEGA8 10 BIT DPM	


;M8DPM091109A	

 .include "m8def.inc"        ;Include file is in the same directory as the project.
 
;	I/O Assignment


;	PORTB

;B0		Range control output	
;B1		Input with weak pullup
;B2		Input with weak pullup
;B3		Input with weak pullup
;B4		Input with weak pullup
;B5		Input with weak pullup
;B6		Input with weak pullup
;B7		Input with weak pullup


     .equ    PORTBdata    =0b11111110    ;Initial data    
	 .equ    DDRBdata     =0b00000000    ;Initial data
	

;	PORTC	Bits 1 - are cathodes for four digit common cathod LED display.
 ;C0    
 ;C1    Digit 1 (right-most digit)
 ;C2    Digit 2
 ;C3    Digit 3
 ;C4    Digit 4 (left-most digit)
 ;C5    
 ;C6    
 ;C7    
 
     .equ    PORTCdata    =0b11100000    ;Initial data    
	 .equ    DDRCdata     =0b11111110    ;Initial data



;LED display segment definition is below:
;      AAAAAA
;      F    B
;      F    B
;      GGGGGG
;      E    C
;      E    C
;      DDDDDD    DP      

ledlut:		;LED Look-Up Table, Segent codes for 0..9 on 7 segment display
;         0   1   2   3   4   5   6   7   8   9  blank minus
;.db	$3F,$06,$5B,$4F,$66,$6D,$7d,$07,$7F, $6F, $00, $40
;The look-up table above is only used as a reference.



;	PORTD Anodes for a four digit common cathode diapls
 ;D0    Segment a, driven through a resistor.
 ;D1    Segment	b, driven through a resistor
 ;D2    Segment	c, driven through a resistor
 ;D3    Segment	d, driven through a resistor
 ;D4    Segment e, driven through a resistor
 ;D5    Segment f, driven through a resistor
 ;D6    Segment g, driven through a resistor
 ;D7    Segment decimal point
 
     .equ    PORTDdata    =0b10000000    ;Initial data    
     .equ    DDRDdata     =0b11111111    ;Initial data

	 .equ	number_of_tasks	= 5; Number of tasks handled during interrupt.
 	 .equ	Scanspeed		= 252 ; Timer 0 reload value. Sets multitasking frequency. . 
	 .equ	Measurements_to_skip = 80 ; The number of display cycles per measuremnt

;	Register assignments
 	.def    temp	= r16	;General purpose scratch register.
	.def	segval	= r17	;General purpoe scratch register
	.def	innum = r18

	.def	digit1 = r19	;Images of the 7 segments + decimal point
	.def	digit2 = r20	;which are to be written to the four digits.
	.def	digit3 = r21
	.def	digit4 = r22
	.def	interruptcounter = r23	;Counts interrupts for multitasker.
	.def	Measurement_counter = r24; number of display refresh cycles per measurement.
	.def	flagreg = r25; Flags for intraprocess signaling


;flagregister bit assignments
;0	Range mode 0 = in low range, 1 = in high range
;1
;2
;3
;4
;5
;6
;7

.equ    FlagretInitial    =0b10000000    ;Initial data for flag register


 .cseg    
 .ORG $0000    
rjmp     start
 
 .org $0009    
rjmp    timer0service    
                       ;Initializaton code
.org $000E  			;ADC_complete interrupt
reti


 start:                    ;Entry point after reset -initialize everything

 	 ldi temp,high(ramend)     ;Initialize 16 bit Stack Pointer
     out sph,temp    
     ldi temp,low(ramend)    
     out spl,temp

	ldi	temp,DDRBdata
	out DDRB,temp
	ldi temp,PORTBdata
	out	PORTB,temp

	ldi	temp,DDRCdata
	out DDRC,temp
	ldi temp,PORTCdata
	out	PORTC,temp

	ldi	temp,DDRDdata
	out	DDRD,temp
	ldi	temp,PORTDdata
	out	PORTD,temp

	ldi	temp,DDRCdata
	out DDRC,temp
	ldi temp,PORTCdata
	out	PORTC,temp

                   ;8 BIT TIMER 0 SETUP
     ldi    temp,0b00000101            		;Set prescaler for clk/8
     out    TCCR0,temp 	
     out    TCNT0,temp
     in     temp,TIMSK
     ori    temp,0b00000001        ;Enable timer 0 oveflow interrupt.
     out    TIMSK,temp

                         ;ITINITALIZE A TO D CONVERTER
     ldi    temp,0b00010101        ;Set control and status register. 4 MHz clock.Clear int flag.
     out    ADCSR,temp


	ldi	interruptcounter,number_of_tasks			
	ldi	flagreg,FlagretInitial
	ldi	Measurement_counter,0
     sei                    		;Enable interrupts.
           
spin:
								;No foreground tasks; everythign happens durring the timer interrupt.
	rjmp	spin



  


;/////////////////////////////Interrupt 0 multitasker\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 timer0service:
    push	temp    
    in		temp,sreg
    push	temp
    ldi    temp, Scanspeed       ;Set number of pulses to count up after division by prescaler
    out    TCNT0,temp
  
  	dec		interruptcounter
	brne	select_task
	ldi	interruptcounter,number_of_tasks

select_task: 

	cpi	interruptcounter,1
	brne	not1
	rcall	dodigit1

not1:
	cpi	interruptcounter,2
	brne	not2
	rcall	dodigit2

not2:
	cpi	interruptcounter,3
	brne	not3
	rcall	dodigit3

not3:
	cpi	interruptcounter,4
	brne	not4
	rcall	dodigit4

not4:
	cpi	interruptcounter,5
	brne	not5
	rcall	doblank

	inc 	Measurement_counter		;Make measurements only evey Measurements_to_skip cycles.
	cpi		Measurement_counter ,Measurements_to_skip
	breq 	Take_a_measurement
	rjmp	not5




Take_a_measurement:
	clr	Measurement_counter	

	;A/D measurement    ///////////////////////////////////////////////////
	;Take 8 measurments and add them in X register, then shift to divide by 8.

	ldi		temp,0		;Set A/D converter channel to channel 0.
	rcall	Measure		;Go get the measurement, results are in YH,YL.
	mov		XH,YH
	mov		XL,YL

	ldi		temp,0		;Set A/D converter channel to channel 0.
	rcall	Measure		;Go get the measurement, results are in YH,YL.

	clc
	adc		XL,YL
	adc		Xh,Yh
	
	ldi		temp,0		;Set A/D converter channel to channel 0.
	rcall	Measure		;Go get the measurement, results are in YH,YL.

	clc
	adc		XL,YL
	adc		Xh,Yh
	
	ldi		temp,0		;Set A/D converter channel to channel 0.
	rcall	Measure		;Go get the measurement, results are in YH,YL.

	clc
	adc		XL,YL
	adc		Xh,Yh
	
	ldi		temp,0		;Set A/D converter channel to channel 0.
	rcall	Measure		;Go get the measurement, results are in YH,YL.

	clc
	adc		XL,YL
	adc		Xh,Yh
	
	ldi		temp,0		;Set A/D converter channel to channel 0.
	rcall	Measure	 	;Go get the measurement, results are in YH,YL.

	clc
	adc		XL,YL
	adc		Xh,Yh
	
	ldi		temp,0		;Set A/D converter channel to channel 0.
	rcall	Measure		;Go get the measurement, results are in YH,YL.

	clc
	adc		XL,YL
	adc		Xh,Yh
	

	ldi		temp,0	 	;Set A/D converter channel to channel 0.
	rcall	Measure	 	;Go get the measurement, results are in YH,YL.

	clc
	adc		XL,YL
	adc		Xh,Yh
						;Shift right three times to divide by 8.
	clc
	ror		XH
	ror		XL
	clc
	ror		XH
	ror		XL

	clc
	ror		XH
	ror		XL

	mov		YH,XH
	mov		YL,XL


 	andi	YH,0b0000011	;Mask off bits that should be low -its only a 10 bit A/D converter.

	rcall	Adjust_range

	rcall	volts_to_decimal ;Value in YH,YL made into numbers

not5:		

     pop    temp
     out    sreg,temp
     pop    temp
     reti                    ;Return from interrupt.


;******************** INTERRUPT TASKS CALLED BY MULTITASKER **********

dodigit1:
	rcall	doblank
	mov		temp,digit1
	rcall 	number_to_7_seg
	out 	PORTD,temp
	cbi		PORTC,1
ret

dodigit2:
	rcall	doblank
	mov		temp,digit2
	rcall 	number_to_7_seg
	out 	PORTD,temp
	sbrc	flagreg,0
	sbi		PORTD,7
	cbi		PORTC,2
ret

dodigit3:
	rcall	doblank
	mov		temp,digit3
	rcall 	number_to_7_seg
	out 	PORTD,temp
	sbrs	flagreg,0
	sbi		PORTD,7
	cbi		PORTC,3
ret

dodigit4:
	rcall	doblank
	mov		temp,digit4
	rcall 	number_to_7_seg
	out 	PORTD,temp
	cbi		PORTC,4
ret

	doblank:
	sbi	PORTC,1
	sbi	PORTC,2
	sbi	PORTC,3
	sbi	PORTC,4

ret


Adjust_range:				;If in low range mode, and if over 1020 ($03FC), switch to high mode
							;If in high range mode, and if under 100 ($64), switch to low mode.
	sbrc flagreg,0
	rjmp in_high_range

			;Must be in low range to be here.

	cpi YH,$03				
	brne Leave_Adjust_range
	cpi YL,$FC
	brlo Leave_Adjust_range

	sbr	flagreg,0b00000001	;Switch to high range mode.
	sbi DDRB,0
	rjmp	Leave_Adjust_range


in_high_range:		;Must be in high range to be here.
	cpi YH,0
	brne Leave_Adjust_range
	cpi YL,$64
	brlo set_to_low
	rjmp	Leave_Adjust_range

set_to_low:
	cbr	flagreg,0b00000001
	cbi DDRB,0


Leave_Adjust_range:
ret
;***************** convert 16 bit number to values for digits 1 through 4 ************
    
 volts_to_decimal:
                         ;Enter with input value in YH:YL. YH,YL,a1,a2,a3,temp,digit1, digit2, digit3, digit4
						 ; modified. The contents of digit1, digit2, digit3, and digit4 are the values for 
						 ; each digit, each with a range corresponding to the acceptable input vlaues of
						 ; the number_to_7_seg routine. Values in Y register are preerved.
  
 
	
	;If in high range mode, If YH,YL is at $03FF, then declare overflow.

	 sbrc	flagreg,0
	 rjmp	no_overflow

	 cpi	YH,$03			
 	 brne	no_overflow
	 cpi	YL,$FF
	 brne	no_overflow


	 ldi    digit4,0			;Number corresponding to the letter "O",actually a zero.
     ldi    digit3,11			;Number corresponding to the letter "F"
     ldi    digit2,12			;Number corresponding to the letter "L"
     ldi    digit1,13			;Number corresponding to the letter "o"
   	 rjmp	conversiondone

no_overflow:
; countdone:         
     		
     clr    digit4
     clr    digit3
     clr    digit2
     clr    digit1

MoreK:    
     inc    digit4   
     subi    YL,$E8            ;Find out how many 1000's
     sbci    YH,$03
     brcc    MoreK    
     ldi    temp,$E8            ;Subtracted one too many, add back on.
     add    YL,temp
     ldi    temp,$03
     adc    YH,temp;    
	 dec    digit4  


MoreH:    
     inc    digit3   
     subi    YL,100            ;Find out how many 100's
     sbci    YH,0
     brcc    MoreH    
     ldi    temp,100            ;Subtracted one too many, add back on.
     add    YL,temp
     ldi    temp,0
     adc    YH,temp;    
	 dec    digit3              

 MoreT:
     inc    digit2    
     subi    YL,10             ;Find out how many 10's
     sbci    YH,$0
     brcc    moreT        
     ldi    temp,10            ;Subtracted one too many, add back on.
     add    YL,temp
     ldi    temp,0
     adc    YH,temp
     dec    digit2

 MoreU:
     inc    digit1    
     subi    YL,1            ;Find out how many 1's
     sbci    YH,0
     brcc    moreT    
     ldi    temp,1            ;Subtracted one too many, add back on.
     add    YL,temp
     ldi    temp,0
     adc    YH,temp
     dec    digit1          
 	


conversiondone:
     ret



number_to_7_seg:		;Enter with number in temp, extis with the 7 segment pattern in temp.
	ldi	segval,$3F		;0
	cpi	temp,0
	breq segments_set
	
	ldi	segval,$06		;1
	cpi	temp,1
	breq segments_set
	
	ldi	segval,$5B		;2
	cpi	temp,2
	breq segments_set
	
	ldi	segval,$4F		;3
	cpi	temp,3
	breq segments_set
	
	ldi	segval,$66		;4
	cpi	temp,4
	breq segments_set
	
	ldi	segval,$6D		;5
	cpi	temp,5
	breq segments_set
	
	ldi	segval,$7d		;6
	cpi	temp,6
	breq segments_set
	
	ldi	segval,$07		;7
	cpi	temp,7
	breq segments_set
	
	ldi	segval,$7F		;8
	cpi	temp,8
	breq segments_set
	
	ldi	segval,$6F		;9
	cpi	temp,9
	breq segments_set
	
	ldi	segval,$40		;Negative sign
	cpi	temp,10
	breq segments_set
	
	ldi	segval,0b01110001	;Letter "F"
	cpi	temp,11
	breq segments_set

	ldi	segval,0b00111000	;Letter "L"
	cpi	temp,12
	breq segments_set

	ldi	segval,0b01011100	;Lower case letter "o"
	cpi	temp,13
	breq segments_set

	ldi	segval,$00		    ; Blank (default)


segments_set:
	mov	temp,segval
	ret


 Measure:   ;Measure A/D channel. Enter with channel number in temp. Exit with data in YH:YL
         	;Allowed range is 0..5
     push   temp
     sbi    ADCSR,ADEN            ;Enable A/D converter.
     andi   temp,0b00000111       ;Mask off upper bits to restrict range of channel selction.
     ori    temp,0b11000000       ;Set reference voltage bit to use internal bandgap reference
     out    ADMUX,temp            ;Select channel.

	sei
	sbi		ADCSRA,3				;Enable ADC interrupts
	ldi		temp,0b10010000
	out		mcucr,temp
	sleep

     sbi    ADCSR,ADIF             ;Clear interrupt flag.
     in     YL,ADCL            	   ;Get data into Y register.
     in     YH,ADCH				
     pop    temp
     ret    
     
.exit

/*
;flooby not used?
set_decimal_point:
	cbr		digit3,0b10000000									;Set the decimal point according to range flag (flagreg,0).
	cbr		digit4,0b10000000
	sbrs	flagreg,0
	sbr		digit3,0b10000000
	sbrc	flagreg,0
	sbr		digit4,0b10000000
	ret

*/


