;**** A P P L I C A T I O N N O T E A V R 4 0 0 ************************ ;* ;* Title: Low Cost A/D Converter ;* Version: 1.0 ;* Last updated: 97.07.18 ;* Target: AT90Sxxxx (All AVR Devices) ;* ;* Support E-mail: avr@atmel.com ;* ;* Code Size :37 words ;* Low Register Usage :0 ;* High Register Usage :2 ;* Status Flag Usage :1 (t flag) ;* Interrupt usage :Timer/Counter0 overflow interrupt, ;* Analog comparator interrupt ;* ;* DESCRIPTION ;* ;* This application note shows how you can make a A/D converter using a AVR ;* device, one external resistor and one external capacitor. This solution ;* uses the Timer/Counter0 overflow interrupt in addition to the Analog ;* comparator interrupt. The usage of interrupts free's the MCU while ;* conversion is taking place. ;* ;* To minimize the usage of external components, this A/D converter uses ;* the charging of a capacitor (controlled by port D pin 2)through a ;* resistor while converting. ;* The voltage across the capacitor will follow an exponential curve of ;* voltage versus time. By constricting the voltage range of ;* the converter to 2/5Vdd, the exponential curve is a good approximation ;* of a straight line. This makes it possible to simply measure the time ;* it takes before the voltage across the capacitor equals the voltage which ;* is to be converted. To do this we use the analog comparator. The ;* comparator will give an interrupt when the voltage across the capacitor ;* rises above the measurement voltage. The output is divided into 64 ;* different levels. ;* ;* To ensure correct timing the time constant of the RC-network must ;* satisfie 512*(1/f)=-R*C*ln(1-2/5). ;* For the A/D converter to operate properly, the capacitor must be ;* completly discharged between each conversion. This is done by allowing ;* the discharging to take a minimum of 200us. ;* ;* ;* *** Initialization ;* ;* 1. Call convert_init ;* 2. Enable global interrupts (with sei) ;* ;* *** A/D conversion ;* ;* 1. Call AD_convert ;* 2. Wait for conversion complete (t to be set) (less than 521 cycles) ;* 3. Read data from result ;************************************************************************** .include "1200def.inc" ;***** Constants .equ preset=192 ;T/C0 Preset constant (256-64) ;***** A/D converter Global Registers .def result=r16 ;Result and intermediate data .def temp=r17 ;Scratch register ;************************************************************************** ;* ;* PROGRAM START - EXECUTION STARTS HERE ;* ;************************************************************************** .cseg .org $0000 rjmp RESET ;Reset handle .org OVF0addr rjmp ANA_COMP ;Timer0 overflow handle .org ACIaddr rjmp ANA_COMP ;Analog comparator handle ;************************************************************************** ;* ;* ANA_COMP - Analog comparator interrupt routine ;* ;* ;* DESCRIPTION ;* This routine is executed when one of two events occur: ;* 1. Timer/counter0 overflow interrupt ;* 2. Analog Comparator interrupt ;* Both events signals the end of a conversion. Timer overflow if the signal ;* is out of range, and analog comparator if it is in range. ;* The offset will be corrected, and the t'flag will be set. ;* Due to the cycles needed for interruption handling, it is necessary ;* to subtract 1 more than was added previously. ;* ;* ;* Total numbers of words : 7 ;* Total number of cycles : 10 ;* Low register usage : 0 ;* High register usage : 2 (result,temp) ;* Status flags : 1 (t flag) ;* ;************************************************************************** ANA_COMP: in result,TCNT0 ;Load timer value clr temp ;Stop timer0 out TCCR0,temp subi result,preset+1 ;Rescale A/D output cbi PORTD,PD2 ;Start discharge set ;Set conversion complete flag reti ;Return from interrupt ;************************************************************************** ;* ;* convert_init - Subroutine for A/D converter initialization ;* ;* ;* DESCRIPTION ;* This routine initializes the A/D converter. It sets the timer and the ;* analog comparator. The analog comparator interrupt is being initiated by ;* a rising edge on AC0. To enable the A/D converter the global interurrupt ;* flag must be set (with SEI). ;* ;* The conversion complete flag (t) is cleared. ;* ;* Total number of words : 6 ;* Total number of cycles : 10 ;* Low register usage : 0 ;* High register usage : 1 (result) ;* Status flag usage : 0 ;* ;************************************************************************** convert_init: ldi result,$0B ;Initiate comparator out ACSR,result ;and enable comparator interrupt ldi result,$02 ;Enable timer interrupt out TIMSK,result sbi PORTD,PD2 ;Set converter charge/discharge ;as output ret ;Return from subroutine ;************************************************************************** ;* ;* AD_convert - Subroutine to start an A/D conversion ;* ;* DESCRIPTION ;* This routine starts the conversion. It loads the offset value into the ;* timer0 and starts the timer. It also starts the charging of the ;* capacitor. ;* ;* ;* Total number of words : 7 ;* Total number of cycles : 10 ;* Low register usage : 0 ;* High register usage : 1 (result) ;* Status flag usage : 1 (t flag) ;* ;************************************************************************** AD_convert: ldi result,preset ;Clear counter out TCNT0,result ;and load offset value clt ;Clear conversion complete flag (t) ldi result,$02 ;Start timer0 with prescaling f/8 out TCCR0,result sbi PORTB,PB2 ;Start charging of capacitor ret ;Return from subroutine ;************************************************************************** ;* ;* Example program ;* ;* This program can be used as an example on how to set up the A/D ;* converter properly. ;* NOTE! To ensure proper operation, make sure the discharging period ;* of the capacitor is longer than 200us in front of each conversion. ;* The results of the conversion is presented on port B. ;* To ensure proper discharging we have added a delay loop. This loop is ;* 11 thousand cycles. This will give a 550us delay with a 20MHz oscillator ;* (11ms with a 1MHz oscillator). ;* ;************************************************************************** RESET: rcall convert_init ;Initialize A/D converter sei ;Enable global interrupt ldi result,$ff ;set port B as output out DDRB,result Delay: clr result ;Clear temp counter 1 ldi temp,$f0 ;Reset temp counter 2 loop1: inc result ;Count up temp counter 1 brne loop1 ;Check if inner loop is finished inc temp ;Count up temp counter 2 brne loop1 ;Check if delay is finished rcall AD_convert ;Start conversion Wait: brtc Wait ;Wait until conversion is complete out PORTB,result ;Write result on port B rjmp Delay ;Repeat conversion