;**** 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