Most of the physical quantities around us are continuous. By continuous we mean that the quantity can take any value between two extreme. For example the atmospheric temperature can take any value (within certain range). If an electrical quantity is made to vary directly in proportion to this value (temperature etc) then what we have is Analogue signal. Now we have we have brought a physical quantity into electrical domain. The electrical quantity in most case is voltage.To bring this quantity into digital domain we have to convert this into digital form. For this a ADC or analog to digital converter is needed. Most modern MCU including AVRs has an ADC on chip. An ADC converts an input voltage into a number. An ADC has a resolution. A 10 Bit ADC has a range of 0-1023. (2^10=1024) The ADC also has a Reference voltage(ARef). When input voltage is GND the output is 0 and when input voltage is equal to ARef the output is 1023. So the input range is 0-ARef and digital output is 0-1023.
![]() |
Fig: ADC Theory |
Inbuilt ADC of AVR
Now you know the basics of ADC let us see how we can use the inbuilt ADC of AVR MCU. The ADC is multiplexed with PORTA that means the ADC channels are shared with PORTA. The ADC can be operated in single conversion and free running more. In single conversion mode the ADC does the conversion and then stop. While in free it is continuously converting. It does a conversion and then start next conversion immediately after that.
ADC Prescaler.
The ADC needs a clock pulse to do its conversion. This clock generated by system clock by dividing it to get smaller frequency. The ADC requires a frequency between 50KHz to 200KHz. At higher frequency the conversion is fast while a lower frequency the conversion is more accurate. As the system frequency can be set to any value by the user (using internal or externals oscillators)( In xBoard™ a 16MHz crystal is used). So the Prescaler is provided to produces acceptable frequency for ADC from any system clock frequency. System clock can be divided by 2,4,16,32,64,128 by setting the Prescaler.
ADC Channels
The ADC in ATmega32 has 8 channels that means you can take samples from eight different terminal. You can connect up to 8 different sensors and get their values separately.
ADC Registers.
As you know the registers related to any particular peripheral module(like ADC, Timer, USART etc.) provides the communication link between the CPU and that peripheral. You configure the ADC according to need using these registers and you also get the conversion result also using appropriate registers. The ADC has only four registers.
- ADC Multiplexer Selection Register – ADMUX : For selecting the reference voltage and the input channel.
- ADC Control and Status Register A – ADCSRA : As the name says it has the status of ADC and is also use for controlling it.
- The ADC Data Register – ADCL and ADCH : The final result of conversion is here.
(Please Read the Tutorial "Internal Peripherals of AVR" before using ADC of AVRs.)
Using the ADC.
In this sample we will setup and use the ADC in single conversion mode. We will connect a LDR( light dependent resistor) which is a light sensor to input. The result will be shown in LCD.
Initialization.
We have to configure the ADC by setting up ADMUX and ADCSRA registers. The ADMUX has following bits.
ADMUX Register.
REFS1 REFS0 selects the reference voltage. See table below –
| REFS1 | REFS0 | Voltage Reference Selection |
| 0 | 0 | ARef internal Vref Turned off |
| 0 | 1 | AVCC |
| 1 | 0 | Reserved |
| 1 | 1 | Internal 2.56 Voltage Reference |
ADMUX=(1<<REFS0);
The ADCSRA Register.
- ADEN – Set this to 1 to enable ADC
- ADSC – We need to set this to one whenever we need adc to do a conversion.
- ADIF – This is the interrupt bit this is set to
1 by the hardware when conversion is complete. So we can wait till conversion
is complete by polling this bit like
//Wait for conversion to complete while(!(ADCSRA & (1<<ADIF)));
The loop does nothing while ADIF is set to 0, it exits as soon as ADIF is set to one, i.e. conversion is complete. - ADPS2-ADPS0 – These selects the Prescaler for ADC. As I said the ADC frequency must be between 50KHz to 200KHz.
![]() |
We need to select division factor so as to get a acceptable frequency from our 16Mhz clock. We select division factor as 128.So ADC clock frequency = 16000000/128 = 125000 = 125KHz (which is in range of 50KHz to 200KHz). So we set ADCSRA as
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(ADPS1)|(ADPS0); //Enable ADC with Prescalar=Fcpu/128
Reading an analog value.
Now every thing is set up. We now write a routine that will ReadADC.
uint16_t ReadADC(uint8_t ch)
{
//Select ADC Channel ch must be 0-7
ch=ch&0b00000111;
ADMUX|=ch;
//Start Single conversion
ADCSRA|=(1<<ADSC);
//Wait for conversion to complete
while(!(ADCSRA & (1<<ADIF)));
//Clear ADIF by writing one to it
ADCSRA|=(1<<ADIF);
return(ADC);
}
We can call this function from any where from our code and simply need to pass
0-7 as for which channel we need to read.
Sample Code.
The following is complete code to Read Channel 0 and display its value on LCD.
#include <avr/io.h>
#include "lcd.h"
void InitADC()
{
ADMUX=(1<<REFS0); // For Aref=AVcc;
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); //Rrescalar div factor =128
}
uint16_t ReadADC(uint8_t ch)
{
//Select ADC Channel ch must be 0-7
ch=ch&0b00000111;
ADMUX|=ch;
//Start Single conversion
ADCSRA|=(1<<ADSC);
//Wait for conversion to complete
while(!(ADCSRA & (1<<ADIF)));
//Clear ADIF by writing one to it
//Note you may be wondering why we have write one to clear it
//This is standard way of clearing bits in io as said in datasheets.
//The code writes '1' but it result in setting bit to '0' !!!
ADCSRA|=(1<<ADIF);
return(ADC);
}
void Wait()
{
uint8_t i;
for(i=0;i<20;i++)
_delay_loop_2(0);
}
void main()
{
uint16_t adc_result;
//Initialize LCD
LCDInit(LS_BLINK|LS_ULINE);
LCDClear();
//Initialize ADC
InitADC();
//Put some intro text into LCD
LCDWriteString("ADC Test");
LCDWriteStringXY(0,1,"ADC=");
while(1)
{
adc_result=ReadADC(0); // Read Analog value from channel-0
LCDWriteIntXY(4,1,adc_result,4); //Print the value in 4th column second line
Wait();
}
}
Hardware
![]() |
Fig: LDR Connected to ADC of AVR |
![]() |
Fig: Screenshot of ADC Test App. |
- I used the xBoard for testing the application but you can use your own development board. See Home Made AVR Devboard.
- You can also get a low cost ATmega8 based board and "USB AVR Programmer" for quick start.
- For LCD Interfacing See- "LCD Interfacing Tutorial"
- Please Read the Tutorial "Internal Peripherals of AVR" before using ADC of AVRs.
More AVR ADC Applications
- Interfacing Temperature Sensor – LM35
- Interfacing MMA7260 Triple Axis Accelerometer with ATmega32
- AVR Graphic LCD and Accelerometer Demo
- Visualize ADC data on PC Screen using USART – AVR Project
ADC Data On PC (Click to Read More ...) |








@Av..
To my knowledge, practically all the non-ic humidity sensors are frequency dependant devices
Hello,
The LDR tutorial is good and works well too, when I tried it out on the XBoard MINI. The 100K resistor measures low light (tubelight) accurately but is swamped when exposed to sunlight.
I have been told that using a 1K resistor will measure sunlight more accurately. Will there be any problems for the ADC if I were to use a 1K resistor. I am told that ADC can take a max of only 40 milliamps? Thanks.
Hi,
This tutorial is useful for me.But, I just want the ADC in 8 bit resolution. After that, the results of these 8 bit data need to send out through TXD.What should I write for the programming?
easy to understand, tks
hello
how can i obtain an analogue output from avr microcontroller thank you
@Ahmad,
Use the technique called PWM
http://extremeelectronics.co.in/avr-tutorials/introduction-to-pwm-pulse-width-modulation/
How can I get the LCD to display the ADC value in percentage, i.e. 0 = 0% and 1024 = 100%. Any sample code would be greatly appreciated. Thanks!
@Chris ask any 5th standard student!
percent = (adc_input/1023)*100
thats it !
If you know arithmetic try putting 0 and 1023(max value adc can give ) in the above equation
Hi, I had some serious problems using this code for multiple inputs, but the following substitution fixed the error:
use:
ch=(ADMUX & 0xf0)|(ch& 0x0f);
ADMUX=ch;
instead of:
ch=ch&0b00000111;
ADMUX|=ch;
@Thomas H
Thanks for finding and fixing the bug!
@Avinash, im not sure that that would work. If you divided by 1023, then the answer would be one as it cant deal with decimal fractions unless your using the float data type. If you only need 1% accuracy, why not grab the 7most significant bits from adc times by 100 and then divide by 127. That way you can advoid floats, and your code should be significantly faster (because your advoiding floats, and using smaller numbers!)
I apologize for my ignorance. I wasn’t asking for an arithmetic lesson, just help with the C code which I am a beginner. I was enjoying your website and tutorials until I was ridiculed. What’s the sense of publishing tutorials for beginners if you are clearly offended by a beginner’s questions. Phill, thanks for being open minded and not blinded by ego.
hi, thanks for the great tutorial…..it is really very helpful for beginners like me.
i had a doubt….in your ReadADC routine when you say :
ch=ch&0b00000111;
ADMUX|=ch;
we are also setting the REFS1 and REFS2 to 0…..which means we are no more using AVCC as our reference voltage(which we were supposed to do).
Please correct me if i am going wroing somewhere.
thanking you in advance.
Sorry for the question posted in my previous comment.
I didn’t noticed the bitwise operator ‘|’ in ADMUX|=ch;.
Anyway thanking you.
There is a function named LCDInit(LS_BLINK|LS_ULINE) in the code given above but, the actual name of the function in lcd.h supplied by your web-site in LCD usage tutorial has the function InitLCD(LS_BLINK|LS_ULINE).
Please consider changing this and make this to be in sync with that tutorial.
please provide some insight into use of ADC in differential mode and a sample code to assist beginners .
Hi, I’m having a problem regarding with getting the percentage of the adc reading. I am using this equation percent=(adc_result1*100)/1023. I get my percentages right, but the problem is that after 64 percent it jumps to 1 percent again, I was wondering if you could help me figure out the problem.
declare percent as float like
float percent;percent = (adc_result*100.00)/1023.00;
Is there any way to test segment program without Microcontroller. I means that I wanna test segment code as a new programmer in my PC. Please reply to me…
thank u.it is a great tutorial.but i have a question if instead of using lcd we would like to the accelerometer values in eeprom of atmega128,then what do we need to do?
Its really a nice tutorial for new guys in avr!!!
Its really a nice tutorial for new guys in avr!!!
thanx dear.!!
Hello.I have a problem understanding this line of code:
ADMUX=(1<<REFS0);
I know that "<<" is the bitshift operator. The operation x<<n shifts the value of x left by n bits. If REFS0 is initial 0 then 1 is shifted to the left by 0 positions. So in the right-hand side we have the bit value 1 and this value is assign to ADMUX(a register with 8 bits).Is this possible? I'll appreciate if you could help me figure out this problem.
Thanks.
@Mike
see this
http://extremeelectronics.co.in/avr-tutorials/programming-in-c-tips-for-embedded-development/
Hi,
Just wanted to point out that ADCSRA should be set as:
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); //Enable ADC with Prescalar=Fcpu/128
You did not shift 1 by ADPS1 and ADPS0 in the explanation while it is written correctly in the code.
Hope I'm not wrong…
Sir thank you very much for your great support through these tutorials.Please post tutorials on arduino development boards.
I hope that in future Arduino Uno will be there in your online shop.
Thank you.
@Abhilash
Thanks, but I personally don’t like Arduinos. So I am never gonna work on those.
Hi Avinash & thanks for this wonderful page of AVR. I have a question friend. where returns the function return(ADC)? thanks