The ADC of The AVR: Analog To Digital Conversion

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 25

The ADC of the AVR

Analog to Digital Conversion

Most real world data is analog. Whether it be temperature, pressure,


voltage, etc, their variation is always analog in nature. For example, the temperature
inside a boiler is around 800°C. During its light-up, the temperature never approaches
directly to 800°C. If the ambient temperature is 400°C, it will start increasing gradually to
450°C, 500°C and thus reaches 800°C over a period of time. This is an analog data.

Signal Acquisition Process


Now, we must process the data that we have received. But analog signal processing is
quite inefficient in terms of accuracy, speed and desired output. Hence, we convert
them to digital form using an Analog to Digital Converter (ADC).
Signal Acquisition Process
In general, the signal (or data) acquisition process has 3 steps.
 In the Real World, a sensor senses any physical parameter and converts into an
equivalent analog electrical signal.
 For efficient and ease of signal processing, this analog signal is converted into a digital
signal using an Analog to Digital Converter (ADC).
 This digital signal is then fed to the Microcontroller (MCU) and is processed
accordingly.

ADC Pins – ATMEGA16/32

Interfacing Sensors
In general, sensors provide with analog output, but a MCU is a digital one. Hence we
need to use ADC. For simple circuits, comparator op-amps can be used. But even this
won’t be required if we use a MCU. We can straightaway use the inbuilt ADC of the
MCU. In ATMEGA16/32, PORTA contains the ADC pins.
The ADC of the AVR
The AVR features inbuilt ADC in almost all its MCU. In ATMEGA16/32, PORTA
contains the ADC pins. Some other features of the ADC are as follows:
ADC Features – ATMEGA16/32

Right now, we are concerned about the 8 channel 10 bit resolutionfeature.


 8 channel implies that there are 8 ADC pins are multiplexed together. You can easily
see that these pins are located across PORTA (PA0…PA7).
 10 bit resolution implies that there are 2^10 = 1024 steps (as described below).

8 channel 10 bit ADC

Suppose we use a 5V reference. In this case, any analog value in between 0 and 5V is
converted into its equivalent ADC value as shown above. The 0-5V range is divided into
2^10 = 1024 steps. Thus, a 0V input will give an ADC output of 0, 5V input will give an
ADC output of 1023, whereas a 2.5V input will give an ADC output of around 512. This
is the basic concept of ADC.
To those whom it might concern, the type of ADC implemented inside the AVR MCU is
of Successive Approximation type.
Apart from this, the other things that we need to know about the AVR ADC are:
 ADC Prescaler
 ADC Registers – ADMUX, ADCSRA, ADCH, ADCL and SFIOR
ADC Prescaler
The ADC of the AVR converts analog signal into digital signal at some regular interval.
This interval is determined by the clock frequency. In general, the ADC operates within
a frequency range of 50kHz to 200kHz. But the CPU clock frequency is much higher (in
the order of MHz). So to achieve it, frequency division must take place. The prescaler
acts as this division factor. It produces desired frequency from the external higher
frequency. There are some predefined division factors – 2, 4, 8, 16, 32, 64, and 128.
For example, a prescaler of 64 implies F_ADC = F_CPU/64. For F_CPU = 16MHz,
F_ADC = 16M/64 = 250kHz.
Now, the major question is… which frequency to select? Out of the 50kHz-200kHz
range of frequencies, which one do we need? Well, the answer lies in your need. There
is a trade-off between frequency and accuracy. Greater the frequency, lesser the
accuracy and vice-versa. So, if your application is not sophisticated and doesn’t require
much accuracy, you could go for higher frequencies.
ADC Registers
We will discuss the registers one by one.
ADMUX – ADC Multiplexer Selection Register
The ADMUX register is as follows.

ADMUX Register

The bits that are highlighted are of interest to us. In any case, we will discuss all the bits
one by one.
 Bits 7:6 – REFS1:0 – Reference Selection Bits – These bits are used to choose the
reference voltage. The following combinations are used.
Reference Voltage Selection

ADC Voltage Reference Pins

The ADC needs a reference voltage to work upon. For this we have a three pins AREF,
AVCC and GND. We can supply our own reference voltage across AREF and GND. For
this, choose the first option. Apart from this case, you can either connect a capacitor
across AREF pin and ground it to prevent from noise, or you may choose to leave it
unconnected. If you want to use the VCC (+5V), choose the second option. Or
else, choose the last option for internal Vref.
Let’s choose the second option for Vcc = 5V.
 Bit 5 – ADLAR – ADC Left Adjust Result – Make it ‘1’ to Left Adjust the ADC Result.
We will discuss about this a bit later.
 Bits 4:0 – MUX4:0 – Analog Channel and Gain Selection Bits – There are 8 ADC
channels (PA0…PA7). Which one do we choose? Choose any one! It doesn’t matter.
How to choose? You can choose it by setting these bits. Since there are 5 bits, it
consists of 2^5 = 32 different conditions as follows. However, we are concerned only
with the first 8 conditions. Initially, all the bits are set to zero.
Input Channel and Gain Selections

Thus, to initialize ADMUX, we write


ADMUX = (1<<REFS0);
ADCSRA – ADC Control and Status Register A
The ADCSRA register is as follows.

ADCSRA Register

The bits that are highlighted are of interest to us. In any case, we will discuss all the bits
one by one.
 Bit 7 – ADEN – ADC Enable – As the name says, it enables the ADC feature. Unless
this is enabled, ADC operations cannot take place across PORTA i.e. PORTA will
behave as GPIO pins.
 Bit 6 – ADSC – ADC Start Conversion – Write this to ‘1’ before starting any
conversion. This 1 is written as long as the conversion is in progress, after which it
returns to zero. Normally it takes 13 ADC clock pulses for this operation. But when you
call it for the first time, it takes 25 as it performs the initialization together with it.
 Bit 5 – ADATE – ADC Auto Trigger Enable – Setting it to ‘1’ enables auto-triggering of
ADC. ADC is triggered automatically at every rising edge of clock pulse. View the
SFIOR register for more details.
 Bit 4 – ADIF – ADC Interrupt Flag – Whenever a conversion is finished and the
registers are updated, this bit is set to ‘1’ automatically. Thus, this is used to check
whether the conversion is complete or not.
 Bit 3 – ADIE – ADC Interrupt Enable – When this bit is set to ‘1’, the ADC interrupt is
enabled. This is used in the case of interrupt-driven ADC.
 Bits 2:0 – ADPS2:0 – ADC Prescaler Select Bits – The prescaler (division factor
between XTAL frequency and the ADC clock frequency) is determined by selecting the
proper combination from the following.
ADC Prescaler Selections

Assuming XTAL frequency of 16MHz and the frequency range of 50kHz-200kHz, we


choose a prescaler of 128.
Thus, F_ADC = 16M/128 = 125kHz.
Thus, we initialize ADCSRA as follows.
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
// prescaler = 128
ADCL and ADCH – ADC Data Registers
The result of the ADC conversion is stored here. Since the ADC has a resolution of 10
bits, it requires 10 bits to store the result. Hence one single 8 bit register is not sufficient.
We need two registers – ADCL and ADCH (ADC Low byte and ADC High byte) as
follows. The two can be called together as ADC.

ADC Data Registers (ADLAR = 0)


ADC Data Registers (ADLAR = 1)

You can very well see the the effect of ADLAR bit (in ADMUX register). Upon setting
ADLAR = 1, the conversion result is left adjusted.
SFIOR – Special Function I/O Register
In normal operation, we do not use this register. This register comes into play whenever
ADATE (in ADCSRA) is set to ‘1’. The register goes like this.

SFIOR Register

The bits highlighted in yellow will be discussed as they are related to ADATE. Other bits
are reserved bits.
 Bits 7:5 – ADC Auto Trigger Source – Whenever ADATE is set to ‘1’, these bits
determine the trigger source for ADC conversion. There are 8 possible trigger sources.

ADC Auto Triggering Source Selections

These options are will be discussed in the posts related to timers. Those who have prior
knowledge of timers can use it. The rest can leave it for now, we won’t be using this
anyway.
ADC Initialization
The following code segment initializes the ADC.
1 void adc_init()
2 {
// AREF = AVcc
3 ADMUX = (1<<REFS0);
4
5 // ADC Enable and prescaler of 128
6 // 16000000/128 = 125000
7 ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
8 }
9
Reading ADC Value
The following code segment reads the value of the ADC. Always refer to the register
description above for every line of code.
1
2 uint16_t adc_read(uint8_t ch)
3 {
4 // select the corresponding channel 0~7
5 // ANDing with ’7′ will always keep the value
// of ‘ch’ between 0 and 7
6 ch &= 0b00000111; // AND operation with 7
7 ADMUX = (ADMUX & 0xF8)|ch; // clears the bottom 3 bits before ORing
8
9 // start single convertion
10 // write ’1′ to ADSC
11 ADCSRA |= (1<<ADSC);
12
// wait for conversion to complete
13 // ADSC becomes ’0′ again
14 // till then, run loop continuously
15 while(ADCSRA & (1<<ADSC));
16
17 return (ADC);
}
18
19
Physical Connections
Let’s connect two LDRs (Light Dependent Resistors) to pins PA0 and PA1 respectively.
The connection is as follows. The function of potentiometers is explained in a later
section, Sensor Calibration. You can scroll down to it. ;)
LDR Connections

Now suppose we want to display the corresponding ADC values in an LCD. So, we also
need to connect an LCD to our MCU. Read this post to know about LCD interfacing.
Since it is an LDR, it senses the intensity of light and accordingly change its resistance.
The resistance decreases exponentially as the light intensity increases. Suppose we
also want to light up an LED whenever the light level decreases. So, we can connect
the LED to any one of the GPIO pins, say PC0.
Note that since the ADC returns values in between 0 and 1023, for dark conditions, the
value should be low (below 100 or 150) whereas for bright conditions, the value should
be quite high (above 900).
Now let’s write the complete code.
Example Code
To learn about LCD interfacing, view this post. You can type, compile and build it in
AVR Studio 5. View this page to know how. To know about the I/O port operations in
AVR, view this page.
+ expand source
Sensor Calibration
Calibration means linking your real world data with the virtual data. In the problem
statement given earlier, I have mentioned that the LED should glow if the light intensity
reduces. But when should it start to glow? The MCU/code doesn’t know by itself. You
get the readings from the sensor continuously in between 0 and 1023. So, the question
is how do we know that below ‘such and such’ level the LED should glow?
This is achieved by calibration. You need to physically set this value. What you do is
that you run the sensor for all the lighting conditions. You have the ADC values for all
these levels. Now, you need to physically see and check the conditions yourself and
then apply a threshold. Below this threshold, the light intensity goes sufficiently down
enough for the LED to glow.
The potentiometer connected in the circuit is also for the same reason. Now, by the
basic knowledge of electronics, you could easily say that upon changing the pot value
the ADC value changes. Thus, for various reasons (like poor lighting conditions, you are
unable to distinguish between bright and dark conditions, etc), you can vary the pot to
get desired results.
This is why I have given the two thresholds (RTHRES anf LTHRES) in the beginning of
the code.
So, this is all with the ADC. I hope you enjoyed reading this. Please post the
comments below for any suggestion, doubt, clarification, etc

#include
int main (void)
{
DDRD = 0xFF;
int x;
ADCSRA = (1<<ADEN) | (1<<ADPS2);
while (1)
{
ADMUX = 0;
ADCSRA |= (1<<ADSC);
while (ADCSRA & _BV(ADSC) ) {}
x = ADC;
PORTD = x<<PD4;
}
return 0;
}

>>
#include
#include
#include
volatile static unsigned char channel=0;
uint16_t adc_val;
void enable_adc(void) //enable timer2_overflow
{
sbi(TCCR2,CS22); //Timer clock =system clock/128
cbi(TCCR2,CS21);
sbi(TCCR2,CS20);
sbi(TIFR,TOV2); //Clear TOV0 clear pending interrupts
sbi(TIMSK,TOIE2); //Enable Timer0//Overflow Interrupt
}
SIGNAL(SIG_OVERFLOW2)
{ ADMUX=(1<<REFS1)|(1<<REFS0)|(channel & 0x07);//setting 2.56 internal Vref
ADCSRA = (1<< ADEN) | (1<<ADSC) | (1<<ADIF);
// Gonna wait for first conversion to complete but discard (as it's usually inaccuarate)
do {// keep waiting
} while(!(ADCSRA & (1 << ADIF))); // Wait for A/D conversion to finish
// Then start the REAL conversion we're interested in....
ADCSRA |= (1 << ADSC); // start another conversion (discard the first reading)
do {// keep waiting
} while(!(ADCSRA & (1 << ADIF))); // Wait for A/D conversion to finish
adc_val=ADC; //store value after conversion in adc_val
}
volatile int digital_analog(void){
return(adc_val);}//return digital output
int main(void)
{
sei();
DDRD=0xFF;
DDRA=0x00;
enable_adc();
}

#include
#define F_CPU 1000000UL
#include "util/delay.h"
int main (void)
{
int x=0;
int i;
DDRD = 0xFF;
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
ADMUX = (1<<REFS0);
while (1)
{
ADMUX = 0;
ADCSRA |= (1<<ADSC);
while (ADCSRA & (1<<ADIF)) {}
x = ADC;
for (i=0; i<10; i++)
{
_delay_ms(500);
if (x & (1 << (9-i)))
{
_delay_ms(500);
PORTD |= (1 << PD4);
}
else
{
_delay_ms(500);
PORTD &= ~(1<<PD4);
}
}
}
return 0;
}
/* adc1.c
* Basic usage of the ADC as a comparator
*/

#include<avr/io.h>
#define COMPARE_VALUE 125
#define sbi(x,y) x |= _BV(y)
#define cbi(x,y) x &=~_BV(y)

int main(void)
{
DDRB=0xff; //PORTB as output
PORTB=0x00;
DDRA=0x00; //PORTA set as input (since we want to take inputs for the
ADC)
PORTA=0xff; //Enable the pull-ups on PORTA

ADMUX =_BV(ADLAR); //Enable the Left aligning of the ADC result

ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); //Enable the


ADC with prescaler=128

ADCSRA |=_BV(ADSC); //Set the bit to start the conversion


while(1==1)
{
while(ADCSRA &_BV(ADSC)) {} //Waits for ADC conversion to complete

if(ADCH>=COMPARE_VALUE)
{
sbi(PORTB,PB1); //Suppose a motor between PB0 and PB1
with proper buffers
cbi(PORTB,PB0); // In this case, the motor runs forward
}
else if (ADCH<COMPARE_VALUE)
{
sbi(PORTB,PB0); //Here the motor runs backward
cbi(PORTB,PB1);
}
ADCSRA |=_BV(ADSC); //Set the bit to start conversion again
}
return 0;
}

/* adc2.c
* This program demonstrates the use of ADMUX for selecting the ADC input pin
*/

#include<avr/io.h>

void main(void)
{
DDRB = 0xFF; // port B output
unsigned char compare_value = 0x7f;
unsigned char x[4]; //Array
ADCSRA = _BV(ADEN) | _BV(ADPS2); //Enable ADC with prescaler=16
int i=0;
while (1==1)
{
for(i=0;i<4;i++)
{
// Select pin ADC[0..3] using MUX
ADMUX = _BV(ADLAR) + i;

//Start conversion
ADCSRA |= _BV(ADSC);

// wait until converstion completed


while (ADCSRA & _BV(ADSC) ) {}

if(ADCH > compare_value) x[i]=1; //if value at the pin is greater


than compare value
else x[i]=0;
}
if(x[0] == 1 || x[1] == 1 || x[2] == 1 || x[3] == 1) PORTB=0xFF; //If
voltage at all the four pins is greater than the set voltage
else PORTB=0x00;
}
}

#include <avr/io.h>

#define XTAL 16000000L //Crystal Freq.


#define TIMER_CLOCK 2 // Toggling Freq.

int main(void)
{
DDRD = _BV(PD5); // set OC1A pin as output, required for output
TCCR1A = _BV(COM1A0); // enable toggle OC1A output on compare
TCCR1B = _BV(CS10) | _BV(CS11) | _BV(WGM12); // prescaler=64, clear
timer/counter on compareA match
OCR1A = ((XTAL/64)/(2*TIMER_CLOCK)) - 1; // preset timer1 high/low byte
/*
* Initialisation done, LED will now flash without executing any code !
*/
for(;;){} // loop forever
}

#include <inttypes.h> //short forms for various integer types


#include <avr/io.h>

#define XTAL 16000000L

static void delay(uint16_t us)


{
while ( us ) us--;
}

int main(void)
{
uint8_t i = 0;
DDRB = 0xff; // use all pins on PortB for output
PORTB = 0x00; // set output high -> turn all LEDs off
// set OC1A pin as output, required for output toggling
DDRD |= _BV(PD5);
// enable 8 bit PWM, select inverted PWM
TCCR1A = _BV(WGM10) | _BV(COM1A1) | _BV(COM1A0);
// timer1 running on 1/8 MCU clock with clear timer/counter1 on Compare
Match
// PWM frequency will be MCU clock / 8 / 512, e.g. with 4Mhz Crystal 977
Hz.
TCCR1B = _BV(CS11) | _BV(WGM12);

for (;;)
{
/* dimm LED on */
for (i=0; i<255; i++)
{
OCR1AL = i; //Note OCR1AL NOT OCR1A
PORTB=i; //for debugging
delay(XTAL/1600); //delay 10 ms just to slow things down
}
/* dimm LED off */
for( i=255; i>0; i--)
{
OCR1AL = i; //Note OCR1AL NOT OCR1A
PORTB=i; //for debugging
delay(XTAL/1600); //delay 10 ms just to slow things down
}
}
return 0;
}

#include <avr/io.h>
#include <avr/interrupts.h> //file to be included if using interrupts
#include <stdint.h>

static volatile uint8_t adc_value = 0;

void avr_init(void);

ISR(ADC_vect)
{
adc_value = ADCH;
ADCSRA |= _BV(ADSC);
}

int main(void)
{
avr_init();
sei();
ADCSRA |= _BV(ADSC);
while(1==1)
{
OCR0 = adc_value; //Instead of starting conversion and waiting for it
here, it is done in the interrupt routine
}
}

void avr_init()
{
DDRB = 0xff; //PORTB output
PORTB = 0x00;
OCR0 = 0;
TCCR0 = _BV(WGM01) | _BV(WGM00) | _BV(COM01) | _BV(CS00); //Fast pwm with
1 prescaler with 16Mhz to get pwm freq 62.5kHz
ADCSRA = _BV(ADEN)| _BV(ADPS2) | _BV(ADPS1) | _BV(ADIE); //Prescaler 64
with 16Mhz to get sampling freq 19.2kHz
ADMUX = _BV(ADLAR);
}

#include <inttypes.h> //short forms for various integer types


#include <avr/io.h>
#include <avr/interrupt.h> //file to be included if using interrupts

#define XTAL 4000000L // Crystal frequency in Hz


#define TIMER_CLOCK 2 // LED flashing frequency in 1Hz

static volatile uint8_t led; // use volatile when variable is accessed


from interrupts

ISR(TIMER1_COMPA_vect) // handler for Output Compare 1 overflow interrupt


{
PORTB = led;
led <<= 1; // move to next LED
if (!led) // overflow: start with bit 0 again
led = 1;
}

int main(void)
{
DDRB = 0xff; // use all pins on PortB for output
PORTB = 0xff; // and turn off all LEDs
TCCR1B = _BV(CS10) | _BV(CS11) | _BV(WGM12); // prescaler=64, clear
timer/counter on compareA match
OCR1A = ((XTAL/2/64/TIMER_CLOCK) - 1 );
// enable Output Compare 1 overflow interrupt
TIMSK = _BV(OCIE1A);
led = 1; // init variable representing the LED state
sei(); // enable interrupts
for (;;) {} // loop forever
}
One of the important features in today’s modern microcontroller is the capability of converting the analog
signal to the digital signal. This feature allows us to process the analog world easily such as temperature,
humidity, light intensity, distance, etc; which usually captured by electronics sensor and represent it on the
change of voltage level.

In this tutorial we will show you how to program the Atmel AVR microcontroller for reading the analog
signal. We will use the AVRJazz Mega168 board as our learning platform and let’s start the fun by pasting
this code to your AVR Studio 4 editor:
//***************************************************************************
// File Name : ADC.c
// Version : 1.0
// Description : Using AVR ADC Peripheral
// Author : RWB
// Target : AVRJazz Mega168 Learning Board
// Compiler : AVR-GCC 4.3.0; avr-libc 1.6.2 (WinAVR 20080610)
// IDE : Atmel AVR Studio 4.14
// Programmer : AVRJazz Mega168 STK500 v2.0 Bootloader
// : AVR Visual Studio 4.14, STK500 programmer
// Last Updated : 21 March 2008
//***************************************************************************
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
unsigned char chSign,chEye;
unsigned int iDelay;
DDRD = 0xFF; // Set PORTD as Output
chEye=0x01; // Initial Eye Variables with 0000 0001
chSign=0;
// Set ADCSRA Register in ATMega168
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);
// Set ADMUX Register in ATMega168
ADMUX=0;

for(;;) { // Loop Forever


// Start conversion by setting ADSC in ADCSRA Register
ADCSRA |= (1<<ADSC);
// wait until conversion complete ADSC=0 -> Complete
while (ADCSRA & (1<<ADSC));
// Get ADC the Result
iDelay = ADCW;
if (iDelay < 1) iDelay=1;

// Display the LED


if (chSign == 0) {
PORTD=chEye;
_delay_ms(iDelay); // Call Delay function
chEye=chEye << 1;
if (chEye >= 0x80) chSign=1;
} else {
PORTD=chEye;
_delay_ms(iDelay); // Call Delay function
chEye=chEye >> 1;
if (chEye <= 0x01) chSign=0;
}
}
return 0; // Standard Return Code
}
// EOF: ADC.c
Before building the program first configure the frequency to 11059200 Hz from menu Project -
>Configuration Option. Rebuild and down load the program to the AVRJazz Mega168 board by first
putting the board in programming mode and select STK500 or AVRISP programmer from AVR Studio 4. For
detail explanation of using this board feature you could go to AVRJazz Mega168 Learning and Development
Board or Starting Atmel AVR C Programming Tutorial.

####Introduction {#intro}

A lot of times when you deal with robotics, working only with +Vcc (Logic 1)
and Ground (Logic 0) might not be enough. What if we have an analog input, i.e., value
varies over a range, say 0V to +5V?
Then we require a tool that converts this analog voltage to discrete values. Analog to Digital
Converter(ADC) is such a tool. In this tutorial, we shall learn the basic implementation of
ADC in Atmel megaAVR devices in Single ended mode with code examples on ATmega16
to get started.

The ATmega16 ADC has 10-bit resolution. This means that an analog voltage
between 0V and 5V will be encoded into one of 1024 binary representations via 8 channels
that are port A pins from A0 to A7.

The reference voltage as shown in figure can be either less or equal to the supply voltage of
the microcontroller.
This means that if you are using a 10 bit precision and 5V is the Vref, then, 0 corresponds
to 0V, 1023(2^10 - 1) corresponds to 5V. Similarly, the intermediate voltages correspond
to their corresponding ADC value. So, what exactly is the output that we get from ADC?

ADC value is nothing but, the corresponding digital value given by:

ADC = Vin x 255 / Vref #(8 bit)


ADC = Vin x 1023 / Vref #(10 bit)

Registers
You need to declare certain registers before you initiate ADC specifying the conditions it
needs to follow.
ADCSRA

ADEN: ADC Enable bit, this bit must be set to 1 for turning ADC on.
ADSC: ADC Start Conversion bit, this bit is set to 1 to start ADC conversion, as soon as
conversion is completed this bit is set back to 0 by the hardware.
ADATE: ADC Auto Trigger Enable, this bit is set to 1 to enable auto triggering of ADC
conversion.
ADIF: ADC Interrupt Flag, this bit is set to 1 when ADC conversion gets complete.
ADIE: ADC Interrupt Enable, this bit is set to 1 if we want to activate the ADC conversion
complete interrupt.
ADPS: ADC Prescaler bits, these bits are used to set the ADC clock frequency.

ADC clock frequency = XTAL frequency / Prescaler


The ADC clock frequency must lie somewhere between 50 kHz to 200 kHz to get maximum
resolution. If ADC resolution of less than 10 bits required, then the ADC clock frequency can
be higher than 200 kHz. At 1 MHz it is possible to achieve eight bits of maximum resolution.

In the microcontroller we’re discussing, prescaler values of 2, 4, 8, 16, 32, 64 & 128 are
provided. A typical core clock speed is 16 MHz. So if you are running your AVR board at 16
Mhz, you can set the ADC clock to one of the following values:

16 MHz / 2 = 8 MHz
16 MHz / 4 = 4 MHz
16 MHz / 8 = 2 MHz
16 MHz / 16 = 1 MHz
16 MHz / 32 = 500 kHz
16 MHz / 64 = 250 kHz
16 MHz / 128 = 125 kHz

Since the ADC clock needs to be between 50 kHz & 200 kHz for 10 bit accuracy, we can
only use the 128 prescaler and achieve a 125 kHz ADC clock.

ADMUX
REFS: REFS1 and REFS0 bits determine the source of reference voltage whether it is
internal or the external voltage source connected to AREF pin.
MUX: MUX bits are used to select between the channels which will provide data to ADC for
conversion.
ADLAR: ADLAR bit when set to 1 gives the left adjusted result in data registers ADCH and
ADCL. These help to get the required precision in the output.

When ADCL is read, the ADC Data Register is not updated until ADCH is read.
Consequently, if the result is left adjusted and no more than 8-bit precision is required, it is
sufficient to read ADCH. Otherwise, ADCL must be read first, then ADCH.

Code
The following code has used an 8-bit precision. Where, the input is constantly at Pin A0.
The 8 bit output is stored in PORT B.

#define F_CPU 16000000UL


#include <util/delay.h>
#include <avr/io.h>

int main(void)
{
//Declaring Port A as input
DDRA=0;

//Declaring port B as output


DDRB=255;

// Enabled ADC and set prescaler division factor as 128


ADCSRA|=(1<<ADEN)|(1<<ADPS2) |(1<<ADPS1) |(1<<ADPS0);

//Vref is same as Vcc, Input constantly at Pin 0 of port A and Setting ADLAR to 1
left shifts the bits for an 8-bit precision
ADMUX|=(0<<REFS1)|(1<<REFS0)|(0<<MUX4)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0)|(1
<<ADLAR);

while(1) {
//Start ADC
ADCSRA|=(1<<ADSC);

//Do nothing till the conversion is over. Here, the condition that ADSC is cl
eared after conversion has been used. You can also use ADIF
while((ADCSRA& (1<<6))!=0);

//The ADC value stored in ADCH has been sent to PORT B as output.
PORTB = ADCH;

_delay_ms(100);
};
}
For a small demonstration, you can connect pin A0 to a powered potentiometer and vary its
resistance to get ADC values of intermediate voltages. You can display this output on LCD
display or you can even display it’s binary output on LEDs.

You might also like