Lecture 6’s sequence
6.1 Introduction to ADC
6.2 ADC in ATmega16
Review of C Programming
6.3 Example application of ADC
6.1. Introduction to A-to-D conversion
An ADC samples an analogue signal at discrete times and converts the sampled
signal to digital form.
Used with transducers, ADCs allow us to monitor real-world inputs and
perform control operations based on these inputs.
Many dedicated ICs are made for ADC, e.g.
ADC0804: 8-bit, successive approximation.
Maxim104: 8-bit, flash type.
A-to-D conversion: Typical embedded application
A-to-D conversion: Example applications
Local positioning sensor for object tracking
Measure the distance b/t FM transmitter and receiver.
The receiver has Receiver Signal Strength Indicator (RSSI).
The RSSI voltage is inversely proportional to the squared distance.
Temperature sensor for shower water
Measure the temperature of shower water.
Use a thermistor as sensor.
Control hot/cold water valves.
A-to-D conversion: Example applications
Obstacle sensor & audio cues in the cane for the blind
Measure distance to nearest object with an ultrasonic sensor.
The sensor output is digitized using the ADC.
Electric fence monitoring
Determine if an electric fence is being tampered.
Measure the voltage level of an electric fence.
A-to-D conversion: Example applications
Wireless irrigation system
Measure soil moisture using a resistor and an ADC.
Turn ON or OFF the sprinklers.
Transmit data wirelessly to base station.
Intelligent clothesline
Use sensors to measure humidity, temperature, and wind speed.
Open/close the clothesline cover to protect against rain.
Lecture 6’s sequence
6.1 Introduction to ADC
6.2 ADC in ATmega16
Review of C Programming
6.3 Example application of ADC
6.2. The ADC in ATmega16
The ADC in ATmega16 has a 10-bit resolution.
The digital output has n = 10 bits.
The ADC has 8 input channels.
Analog input can come from 8 different sources.
However, it performs conversion on only one channel at a time.
If default reference voltage Vref = 5V is used.
step size: 5(V)/1024 (steps) = ± 4.88mV.
Error range: = ± 4.88mV. (not half of step)
The clock rate of the ADC can be different from the CPU clock rate.
One ADC conversion takes 13 ADC clock cycles.
An ADC pre-scaler will decide the actual ADC clock rate.
ADC unit ─ Relevant pins
8 ADC
input pins
External Vref
Supply voltage to
ADC and Port A
ADC unit ─ Block diagram
ADCCSRA to configure
the ADC unit
ADCMUX to select
the input source
ADCH/ACHL register
to store digital output
8 analog
input pins
ADC unit
6.2.1. What are the relevant ADC registers?
a) ADCMUX
b) ADCH/ADCL
c) ADCSRA
d) SFIOR
6.2.2. What are the steps to use the ADC?
6.2.3. How to use the ADC interrupt?
6.2.1a. ADC Multiplexer Selection Register (ADCMUX)
REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0
MUX4-0: Select analog channel and gain
ADC Left Adjust Result: 1 to left adjust the ADC result
Reference Selection bits: To select reference voltage
Reference voltage Vref can be selected among 3 choices. [Slide 28]
Analog input voltage can be selected among 8 pins.
Differential input and custom gain factor can also be chosen. [Slide 30]
ADLAR flag determines how the 10-bit digital output is stored in output register. [Slide 31]
Selecting reference voltage Vref
Table 6.1: ADC reference voltage selection.
REFS1 REFS0 Voltage Reference Selection
0 0 AREF, Internal Vref turned off
0 1 AVCC with external capacitor at AREF pin
1 0 Reserved
Internal 2.56V voltage reference with external
1 1
capacitor at AREF pin
Usually, mode 01 is used: AVCC = 5V as reference voltage.
If the input voltage has a different dynamic range, we can use mode 00 to select an
external reference voltage.
Selecting input source and gain factor
Table 6.2:
ADC input source.
Choices of analogue input voltage:
8 ADC pins (ADC0 to ADC0).
Differential between 2 ADC pins.
Different gain factors (1, 10, 200).
6.2.1b. ADC Left Adjust flag and ADCH/L registers
When bit ADLAR = 0 (Right Aligned)
# # # # # # ADC9 ADC8 ADC7 ADC6 ADC5 ADC4 ADC3 ADC2 ADC1 ADC0
ADCH ADCL
When bit ADLAR = 1 (Left Aligned)
ADC9 ADC8 ADC7 ADC6 ADC5 ADC4 ADC3 ADC2 ADC1 ADC0 # # # # # #
ADCH ADCL
Digital output is stored in two 8-bit registers ADCH and ADCL.
The format of ADCH and ADCL depends on flag ADLAR.
Important: When retrieving digital output, ADCL must be read first ADCH.
6.2.1c. ADC Control and Status Register (ADCSRA)
ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0
ADC Pre-scaler Select bits: next slide
ADC Interrupt Enable: 1 to enable ADC interrupt
ADC Interrupt Flag: set to 1 when conversion is completed
ADC Auto Trigger Enable: 1 to enable auto-triggering of ADC by certain events
ADC Start Conversion: 1 to start conversion
ADC Enable: 1 to enable the ADC unit
ADC unit can operate in two modes: manual or auto-trigger.
In manual mode, setting flag ADSC = 1 will start the conversion. When conversion is
completed, flag ADSC = 0 automatically.
In auto-trigger mode, a predefined event will start conversion.
ADC clock
Table 6.3: ADC Pre-scaler Selection
ADPS2 ADPS1 ADPS0 Division Factor
0 0 0 2
0 0 1 2
0 1 0 4
0 1 1 8
1 0 0 16
1 0 1 32
1 1 0 64
1 1 1 128
The ADC clock is obtained by dividing the CPU clock and a division factor.
There are 8 possible division factors, decided by the 3 bits {ADPS2, ADPS1, ADPS0}
Example: Using internal clock of 1Mz and ADC pre-scaler bits of ‘011’, one ADC clock cycle = 8 x CPU
clock cycle = 8 µs.
6.2.1d. Special Function IO Register (SFIOR)
ADC Auto Trigger Source
ADTS2 ADTS1 ADTS0 - ACME PUD PSR2 PSR10
Table 6.4: ADC Auto Trigger Source
ADTS2 ADTS1 ADTS0 Trigger Source
0 0 0 Free Running mode
0 0 1 Analog Comparator
0 1 0 External Interrupt Request
0 1 1 Timer/Counter0 Compare Match
1 0 0 Timer/Counter0 Overflow
1 0 1 Timer/Counter1 Compare Match B
1 1 0 Timer/Counter1 Overflow
1 1 1 Timer/Counter1 Capture Event
Three flags in register SFIOR specify the event that will auto-trigger an A-to-D conversion.
6.2.2. Steps to use the ADC
Step 1: Configure the ADC using registers ADMUX, ADCSRA, SFIOR.
What is the ADC source?
What reference voltage to use?
Align left or right the result in {ADCH, ADCL}?
Enable or disable ADC auto-trigger?
Enable or disable ADC interrupt?
What is the ADC pre-scaler?
Step 2: Start ADC operation
Write 1 to flag ADSC (register ADCSRA).
Step 3: Extract ADC result
Wait until flag ADSC becomes 0.
Read result from registers ADCL and then ADCH.
Example 6.1: Performing ADC
Write C program that repeatedly performs ADC on a sinusoidal signal
and displays the result on LEDs.
Step 1: Configure the ADC
What is the ADC source? ADC1 (pin A.1)
What reference voltage to use? AVCC = 5V
Align left, or right? Left (top 8-bit in ADCH)
Enable or disable ADC auto-trigger? Disable
Enable or disable ADC interrupt? Disable
What is the ADC pre-scaler? 2 (fastest, selector 001)
Example 6.1: Performing ADC
Step 1: Configure the ADC
0 1 1 0 0 0 0 1
REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0 ADCMUX
1 0 0 0 0 0 1 0
ADEN ADSC ADATE ADIF ADIE ADPS2 ASPS1 ADPS0 ADCSRA
Steps 2 and 3: Show next in the C program.
Example 6.1: adc.c
#include <avr/io.h>
int main (void){
unsigned char result;
DDRB = 0xFF; // set port B for output
// Configure the ADC module of the ATmega16
ADMUX = 0b01100000; // REFS1:0 = 01 -> AVCC = 5v as reference,
// ADLAR = 1 -> Left adjust,
// MUX4:0 = 00001 -> ADC1 as input,
ADCSRA = 0b10000001; // ADEN = 1: enable ADC,,
// ADSC = 0: don't start conversion yet,
// ADATE = 0: disable auto trigger,
// ADIE = 0: disable ADC interrupt,
// ASPS2:0 = 001: ADC prescaler = 2.
while(1){ // main loop
ADCSRA |= 0b01000000; // Start conversion by setting flag ADSC
while (ADCSRA & 0b01000000){;} // Wait until conversion is completed
result = ADCH; // Read the top 8 bits, output to PORTB
PORTB = ~result; // Port B connected to LEDs (0 = ON, 1 = OFF)
}
return 0;
}
6.2.3. Using ADC interrupt
In the polling approach shown previously, we must check ADSC flag to know
when an ADC operation is completed.
Alternatively, the ADC unit can trigger an interrupt when ADC is done.
We enable ADC interrupt through ADIE flag in register ADCSRA.
In the ISR, we can write code to read registers ADCL and ADCH.
ADC interrupt is usually combined with auto-trigger mode [Tutorial 05].
Example 6.2: ADC interrupt
Write interrupt-driven program to digitize a sinusoidal signal and
display the result on LEDs.
Step 1: Configure the ADC.
What is the ADC source? ADC1
What reference voltage to use? AVCC = 5V
Align left, or right? Left (top 8-bit in ADCH)
Enable or disable ADC auto-trigger? Disable
Enable or disable ADC interrupt? Enable
What is the ADC pre-scaler? 2 (fastest conversion)
Step 2: Start ADC operation (set ADSC flag = 1).
Step 3: In ISR, read and store ADC result in a global variable.
Example 6.2: adc_int.c
REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0 ADCMUX
#include<avr/io.h>
0 1 1 0 0 0 0 1
#include<avr/interrupt.h>
ADEN ADSC ADATE ADIF ADIE ADPS2 ASPS1 ADPS0 ADCSRA
volatile unsigned char result;
1 0 0 0 1 0 0 1
ISR(ADC_vect){
result = ADCH; // Read the top 8 bits, and store in variable result
}
int main (void){
DDRB = 0xFF; // set port B for output
// Configure the ADC module of the ATmega16
ADMUX = 0b01100000; // REFS1:0 = 01 -> AVCC as reference,
// ADLAR = 1 -> L ft dj t eft adjust
// MUX4:0 = 00001 -> ADC1 as input
ADCSRA = 0b10001001; // ADEN = 1: enable ADC,
// ADSC = 0: don't start conversion yet
// ADATE = 0: diable auto trigger,
// ADIE = 1: enable ADC interrupt,
// ASPS2:0 = 002: pre-scaler = 2
sei(); // enable interrupt system globally
while(1){ // main loop
ADCSRA |= 0b01000000; // start a conversion
PORTB = ~result; // display on port B
}
return 0;
}