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
You should use PWM and filter it through a capacitor. After few trials you should be able to produce any analog value up to 5V.
@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!
Thanks a lot Thomas!
I had been troubled by the original channel selection code bug for 3 days…
Thanks! The solution seems to work just fine with multiple inputs.But the thing is I’m unable to find the difference between
ch=(ADMUX & 0xf0)|(ch& 0x0f); //Correct
ADMUX=ch;
(AND)
ch=ch&0b00000111; //Bug
ADMUX|=ch;
could you please elaborate.
Regards
Also Avinash kindly correct the bug in the original code to avoid misleading people.
Regards
Here’s the explanation:
Imagine you want to read from ADC1 (ch = 0b0001) and afterwards from ADC2 (ch = 0b0010). Because of the bug, 0b0010 would be ORred with the previous value of the MUX3:0 bits (0b0001) so the result would be 0b0011 (ADC3) instead of 0b0010 (ADC2). That’s why you first have to reset the lower 4 bits of the data held by the register (ADMUX&0b00001111) and only then OR with the channel selection value. ch&0b00001111 is as a precaution so ch greater than 15 wouldn’t touch the upper 4 bits of the ADMUX.
@Chupo_cro,
Thanks a lot for your time to post a useful comment
A correction:
Instead of:
That’s why you first have to reset the lower 4 bits of the data held by the register (ADMUX&0b00001111)…
there should be:
That’s why you first have to reset the lower 4 bits of the data held by the register (ADMUX&0b11110000)…
@Avinash:
You’re welcome
If you are able to edit the comments, you may edit my first comment (January 30, 2013 at 2:13 am) and correct the binary value according to the correction in my second comment (January 30, 2013 at 2:16 am). Then you may delete my second comment so it would be easier to read the discussion.
Regards
Thank you also Thomas, that was really annoying!
No problem! Glad to see my comment still helps people! Why not just fix the code on this page?
@thomas
Still unable to read multiple pins
i dont know were im going wrong….
wen i m reading individually everything is fine…
is there some something with the hardware
i m trying to read LM35,POT and LDR using same come as suggested Avinash and modified by u…
k…
i got it working the problem was with hardware
replaced it and its working nw…..
@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;
What changes do i need to make to print the decimal values also?
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
these tutorials are great. you are great.
i will be very happy if you add one tutorial for me
how to convert 8bit and 16bit adc value to seven segment
desplay.
or make a complete programe 0 to 30volt 4digit volt-meter.i shall be very thankfull to you.
thanks
mr.rizwan ahmed.
2.04.2012 12pm
Thanks! This is an excelent and easy tutorial to follow. The example program worked first time also. Very inspiring.
hello!
i have to do 16 to 16 bit multipication in atmega32 bt i am nt getting a right code…..
plz can u help in getting code?
I need a little explaination
//Wait for conversion to complete
while(!(ADCSRA & (1<<ADIF)));
what we are exactly doing by this !ADCSRA&(1<<ADIF)
//Clear ADIF by writing one to it
ADCSRA|=(1<<ADIF);
why we are doing this as it is done by the hardware in the previous code….
its a confusing or m nt getting the concept correctly???
what is the function of
void Wait()
{
uint8_t i;
for(i=0;i<20;i++)
_delay_loop_2(0);
}
What is the material that is used for binding the wires to the LCD display? I have soldered pins to the LCD display…this looks like something else…is it suitable replacement for soldering, where heating the element is not permitted ?
Here normal soldering is used . But over the soldering ,hot glue is used to protect the wires from braking.
line follower program for analog inputs to mcu ….thanks in advance
Hello! I used lm35 and displayed its temperature on bargraph led but the value always keeps varying.
#define F_CPU 4000000UL
#include
#include
#include
int main()
{
DDRA=0b01111111;
DDRB=0xFF; // bargraph led
PORTB=0;
ADCSRA = 0b10001111; // ADC+interrupt enabled,1:128 prescaler
ADMUX = 0b11100000; // 2.56V ref,left adjusted data
sei();
ADCSRA|=(1<<ADSC);
while(1);
return 0;
}
ISR(ADC_vect)
{
cli();
PORTB=(ADCH);
_delay_ms(2000);
sei();
ADCSRA|=(1<<ADSC);
}
Dear avinash,
can you make me understand why we are doing this ch=ch&0b00000111;
ADMUX|=ch;
?
i am a beginner,need help! please guide.
thanks in advance.
@Masterofdisguise
You need to be “master” of bitwise operator, numbering system and patience to read in order to understand that!
So do you know the following?
1)operator &
2)operator |
@avinash:
sorry for the late reply.. yeah i do understand the operators & and |.These are bitwise and or operators.Actually i understand c but embedded c appears to be bit different may be i m new to this field that’s why?.I want to know why are we masking,the lower order bits for the variable ch(I mean making them one). And how the ADC register ADMUX is able to select the input channel.
sir I used your lcd lib code (that is lcd.c,lcd.h and myutils) with atmega48 to write code on temperature monitor and switch relay connected in portd as well display the value in lcd. the code works very well but my problem is the relay is not switching at any value of adc i want it to switch. so my question is did you use portd in your lcd lib code or the lcd lib code is not for atmega48. please how do I solve this problem
Good effort its helps a lot to start ADC….
how to take multiple ADC readings from PA0 & PA1. is that setting ADMUX registor to corresponding bits in each function is enough for taking continuous reading ?
Dear Avinash,
How to display float value of mathematically manipulated ADC value on LCD?
@Yogesh,
Use your right brain, take some help from the left part too. Then think of what is a LCD and what it shows, then think what is float. Then prepare an algorithm to solve this problem. Then try to bring to life the concept from your brain to this physical world. If it doesn’t work, try to collect the inputs and use them as a feedback to correct the first. Repeat the process until you die or get the solution. Contact me when you have at-least repeated the loop 10 times without success. Understood?
Hi,
I am connecting LM35 temperature sensor with ATMEGA328. I have connected it to ADC5 and VCC and GND.
My question is that do i need to pass voltage to AVCC or AHREF in order to use ADC ?
Also, if i dont pass voltage to AVCC and AHREF, will i get ADC sensor values ?
Thanks,
Ujjwal Soni
@Ujjwal Soni,
Read the data sheet of ATmega328
What if i want to display the adc value as a float type like adc/12.5 = 1023/12.5= 81.84 on lcd display.
What would be the code for this purpose???i have tried this code
adc_result=ReadADC(0)/204.6;
sprintf(lcd,”%0.2f”,adc_result);
LCDWriteStringXY(0,0,lcd);
lcd buffer has been also declared before.But the code doesn’t work.Shows garbage data on lcd.Please give a valuable advise of yours.Thanks in advance.
@Mahmud your process is correct, but avr-gcc has three flavous of sprintf. You need to link against floating point version. avr-gcc manual clearly state this.
Try this
int adc_result,adc_result_q,adc_result_r;
adc_result = ReadADC(0);
adc_result_q =(adc_result*2046)/10;
adc_result_q =(adc_result*2046)%10;
sprintf(lcd,”%d.%d”,adc_result_q,adc_result_r);
LCDWriteStringXY(0,0,lcd);
########TRY THIS##########
int adc_result,adc_result_q,adc_result_r;
adc_result = ReadADC(0);
adc_result_q =(adc_result*2046)/10;
adc_result_r =(adc_result*2046)%10;
sprintf(lcd,”%d.%d”,adc_result_q,adc_result_r);
LCDWriteStringXY(0,0,lcd);
Hey @Gangster,
Thanks man. but i have solve the problem.any ways Thanks again for ur code.