;**** A P P L I C A T I O N N O T E A V R 4 0 1 ************************ ;* ;* Title: 8-bit precision A/D converter ;* Version: 1.03 ;* Last Updated: 97.07.17 ;* Target: AT90Sxxxx (All AVR Devices with analog comparator) ;* ;* Support E-mail: avr@atmel.com ;* ;* DESCRIPTION ;* This Application note shows how to perform dual-slope-alike ;* A/D conversion utilizing the on-chip analog comparator and a few ;* external components. Included is a test program that performs ;* conversions in a eternal loop, outputting the result to eight LEDs. ;* ;*************************************************************************** ;***** Registers used by mpy9u multiplication routine .def mc9u =r0 ;multiplicand used by multiplication routine .def mp9u =r1 ;multiplier used by multiplication routine .def m9uL =r1 ;result Low byte .def m9uH =r2 ;result High byte ;***** Registers used by div17u division routine .def didL =r1 ;Dividend .def didH =r2 .def dresL =r1 ;Holds the result of the division .def dresH =r2 .def divL =r3 ;Divisor .def divH =r4 .def remL =r5 ;Reminder variables used by division routine .def remH =r6 .def TinH =r14 ;Time to reach input voltage .def TinL =r15 .def Tref =r16 ;Time to reach reference voltage .def TH =r17 ;Timer variable .def Vref =r18 ;Computed Vref .def temp =r19 .def temp2 =r20 ;Port B pins .equ AIN0 =0 .equ AIN1 =1 .equ Ref1 =2 .equ Ref2 =3 .equ LED =4 .equ T =7 .equ PRESC =2 ;Timer clocked at CK/8 .equ VrefAddr=0 ;EEPROM Address holding Vref .include "1200def.inc" .cseg .org 0 rjmp reset ;Reset handler reti .org OVF0addr ;** Timer/counter 0 overflow interrupt ****************************** T0_int: inc TH ;Increase timer high-byte reti ;*** Reset handler ************************************************** reset: sbi DDRB,LED ;PB4 and ser temp ;Port D as output, used to out DDRD,temp ;drive LEDs ldi temp,(1<<TOIE0) ;Enable timer interrupt out TIMSK,temp sei ;Enable global interrupt sbi PORTB,T ;Turn on pull-up on T-pin rcall delay sbic PINB,T ;If pin not pulled low rjmp main ; jump to main ;else calibrate: sbis PINB,T ; wait for pin release rjmp calibrate cbi PORTB,T ;Turn off pull-up on T-pin rcall delay ;Let calibration voltage stabilize rcall reference ;Measure Tref rcall delay rcall input ;Measure Tcal clc mov mp9u,Tref ;Tref -> multiplier ldi temp,128 ;128 -> multiplicand ( = 2.5 volts) mov mc9u,temp rcall mpy9u ;Tref x 128 clr divH ;(Tref x 128) mov divL,TinL ; ----------- rcall div17u ; Tcal ldi temp,VrefAddr ;Store Vref out EEAR,temp out EEDR,dresL sbi EECR,EEWE calibrate1: sbic EECR,EEWE rjmp calibrate1 ;Main program main: cbi PORTB,T ;Turn off pull-up on T-pin ldi temp,VrefAddr ;Read Vref from EEPROM out EEAR,temp sbi EECR,EERE in Vref,EEDR loop: rcall reference ;Measure Tref rcall delay ;A small delay to let ; the capacitor discharge, rcall input ;Measure Tin brts error ;If Vin > Vcc calc: lsr TinH ;TinH -> C (multiplier) mov mp9u,TinL ;TinL -> multiplier mov mc9u,Vref ;Tref -> multiplicand rcall mpy9u ;Tin x Tref clr divH ;(Tin x Vref) mov divL,Tref ;------------ rcall div17u ; Tref tst dresH breq write error: ldi temp,255 ; Vin = 255 mov dresL,temp write: com dresL ;Show the value on the LEDs rcall long_delay rcall long_delay rcall long_delay out PORTD,dresL rol dresL brcs wr1 cbi PORTB,LED rjmp loop wr1: sbi PORTB,LED rjmp loop ;*** Subroutine delay ****************************************************** delay: ldi temp,$FF d1: dec temp brne d1 ret long_delay: ser temp2 ld1: rcall delay dec temp2 brne ld1 ret ;*** Subroutine reference ************************************************** ;* Measures Tref reference: sbi DDRB,AIN0 ;Discharge the capacitor sbi DDRB,Ref1 ;Turn on Vref sbi PORTB,Ref1 sbi DDRB,Ref2 rcall delay ;Let the capacitor discharge completely clr TH ;Reset timer out TCNT0,TH cbi DDRB,AIN0 ;AIN0 as input ldi temp,PRESC ;Start timer out TCCR0,temp sbi DDRB,T ;Turn on the transistor ref_wait: sbic ACSR,ACO ;If Capacitor voltage > reference voltage rjmp ref_ok ; conversion conplete cpi TH,1 ;Continue if timer overflow brlo ref_wait ref_ok: in Tref,TCNT0 ;Store Tref clr temp ;Stop timer out TCCR0,temp cbi DDRB,T ;Turn off transistor sbi DDRB,AIN0 ;Discharge capacitor cbi PORTB,Ref1 ;Turn off Vref cbi DDRB,Ref1 cbi DDRB,Ref2 ret ;*** subroutine input ************************************************** ;* Measures Tin ;* Returns with the T-flag set if timer overflow (i.e. Vin > Vcc) input: cbi DDRB,AIN0 ;Tri-state AIN0 clt ;Clear error flag clr TH ;Clear timer out TCNT0,TH ldi temp,PRESC ;Start timer out TCCR0,temp sbi DDRB,T ;Turn on transistor input_wait: sbic ACSR,ACO ;If Capacitor voltage > Vin rjmp input_ok ; charging complete cpi TH,2 brlo input_wait set ;T=1 indicates Vin > Vcc input_ok: in TinL,TCNT0 ;Store Tin mov TinH,TH input_exit: clr temp ;Stop timer out TCCR0,temp cbi DDRB,T ;Turn off transistor sbi DDRB,AIN0 ;Discharge capacitor ret ;******************************************************************** ;* ;* This routine divides a 17 bit number (carry:didH:didLL) on ;* a 16 bit number (divH:divL). ;* The result is placed in (dresH:dresL) ;* ;* The carry flag must contain the 17th bit of the divident before ;* the routine is executed. ;* ;* The routine is based on the div16u - 16/16 Bit Unsigned Division ;* routine found in the avr200.asm application note file ;* ;******************************************************************** div17u: clr remL ;clear remainder Low byte clr remH ;clear remainder High byte ldi temp,17 ;init loop counter d17u_1: rol remL ;shift dividend into remainder rol remH sub remL,divL ;remainder = remainder - divisor sbc remH,divH ; brcc d17u_2 ;if result negative add remL,divL ; restore remainder adc remH,divH clc ; clear carry to be shifted into result rjmp d17u_3 ;else d17u_2: sec d17u_3: rol didL ;shift left dividend rol didH dec temp ;decrement counter brne d17u_1 ;if done ret ; return with value in didL ;*************************************************************************** ;* ;* "mpy9u" - 9x8 Bit Unsigned Multiplication ;* ;* This subroutine multiplies the two register variables (carry:mp9u) and mc9u. ;* The result is placed (carry:m8uH:m8uL) ;* ;* Number of words :11 (return included) ;* Number of cycles : (return included) ;* Low registers used :3 (mp9u,mc9/m9uL,m9uH) ;* High registers used :1 (temp) ;* ;* Note: Result Low byte and the multiplier share the same register. ;* This causes the multiplier to be overwritten by the result. ;* ;*************************************************************************** mpy9u: clr m9uH ;clear result High byte ldi temp,9 ;init loop counter ror mp9u m9u_1: brcc m9u_2 ;if bit 0 of multiplier set add m9uH,mc9u ; add multiplicand to result High byte m9u_2: dec temp ;decrement loop counter brne m9u_3 ;if not done, loop more ret m9u_3: ror m9uH ;shift right result High byte ror m9uL ;rotate right result L byte and multiplier rjmp m9u_1