Using the Analog To Digital Converter.
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.





[...] Using the ADC of AVRs [...]
October 12th, 2008 at 4:24 amhi.please guide me that how to use 2 or more channel of atmega32 adc(porta.0 ..7) togher by bascom. thanks alot best regard
November 7th, 2008 at 1:57 amIs it possible to configure different pins with different reference voltages simultaneously?
For example, PA1 at 2.56V, PA2 at VCC, and PA3 at VREF?
December 10th, 2008 at 2:23 pm@Jhon
Actually there is only one ADC inside the chip and the Input to it is multiplexed. So setting of different reference voltage for individual pins is not possible. But you can set the Reference voltage of ADC to required before taking the input for the desired pin.
For example set
ref=2.56 before sampling PA1
ref=VCC before sampling PA2
and ref=VCC before sampling PA3
this should give you desired result.
December 10th, 2008 at 7:26 pmTwo more quick questions . . .
If I set the ref voltage to 2.56V, and send a 5V signal to that ADC pin, would it get damaged?
Some of the ATmega’s, such as the 2560, have two ADC ports (16 pins). Could the the reference voltage on each be set differently?
December 10th, 2008 at 7:39 pm@Jhon
Ya than would damage the chip. With AVRs with two or more ADCs thats easily possible. Or if you want more ADCs you can go for separate ADC chips. Then you can design more complex circuit.
December 10th, 2008 at 8:00 pm@Jhon
The following is an excerpt from data sheet
“If the user has a fixed voltage source connected to the AREF pin, the user may not use
the other reference voltage options in the application, as they will be shorted to the
external voltage. If no external voltage is applied to the AREF pin, the user may switch
between AVCC and 2.56V as reference selection. The first ADC conversion result after
switching reference voltage source may be inaccurate, and the user is advised to dis-
card this result.
”
So you can easily switch between internal reference of 2.56 and 5.00 volts. But if you also need a third i.e. Custom Voltage applied to Vref it is not possible unless you have more onchip/off chip ADCs
December 10th, 2008 at 8:11 pmit means that only two ref voltages can be used either 2.56v or 5v ………..but what to do if we want any voltage other than those two …
December 24th, 2008 at 9:10 am@Raghu
“it means that only two ref voltages can be used either 2.56v or 5v ………..but what to do if we want any voltage other than those two …”
its simple , see the table in ADMUX register description above . In this example we have gone for 2nd option (bold italics) but to use any other volatge go for first option. Then you can apply any voltage to Aref pin (its pin 21 on mega8) and it will be the reference voltage.
Hope you got it!
December 24th, 2008 at 9:53 amHi,
February 23rd, 2009 at 4:05 amcan you pls provide the asm code to this program.Thanks alot
Can I use 2 multiplexer pins for ADC Analog Compare and rest for Input?……
March 10th, 2009 at 3:42 pm[...] Reading Temperature Sensor from STK500 By Willem Visser See: http://extremeelectronics.co.in/avr-tutorials/using-the-analog-to-digital-converter/ [...]
March 20th, 2009 at 12:08 amHi Avinash,
April 7th, 2009 at 6:06 pmyour tutorials are very helpful especially for newbies,my question is how can use your adc test for two channels.What i want to do is to measure voltages from two adc channels say 0 & 1 and display the result on the LCD.
Thank you.
@Adeola,
I think that is very easy once you understand the above code.
April 8th, 2009 at 7:37 amhow to write as sembly language program in 8051 in order to make a light adc involving sensor an lcd?
April 15th, 2009 at 6:43 pmHI
April 15th, 2009 at 11:35 pmI like your tutorial,but i have one question i.e. how to use assembly language rather than c.
Thank you
Hi Avinash,
April 17th, 2009 at 2:30 pmI am reading voltage from six adc channels of atmega32 in round-robin fashion and displaying the voltage on LCD. But I am getting different voltage of all channels in diferent round means I am not getting constant voltage in different rounds. With Voltmeter I checked the voltage at microcontroller pins, it is constant. I am feeding Unity gain opamp output to ADC inputs.
Please tell me the solution.
Hello Sandeep,
U r usin 6 ADC ch then r u using the other 2 Pins of PORTA for any digital IO (like Interface to LCD?).
If thats the case then free those PINs
Also let me see the code and schematic
mail them to me@avinashgupta.com
April 17th, 2009 at 5:10 pmDear Avinash,
April 23rd, 2009 at 4:16 pmI have sent the required information on urs email (me@avinashgupta.com). Plz tell me the solution.
hello….if the vref=2.5 and the input voltage is 3vol..then the how much ‘ll b the decimal value in ADCH
May 17th, 2009 at 7:09 pm@santosh

May 18th, 2009 at 8:43 amhi everybody.
May 18th, 2009 at 1:04 pmi have no good knowledge about ADC conversion. at my projects i just can use only 1 channel ADC on ATmega32, but actually i want to use 4 channel ADC in the same time. please help me how to use 4 channel ADC?
Sorry made a mistake in the comment that i use a following problem
it is I use the following program and problem is unit digit fluctuation please help me to make ADC reading stable on the seven segment display
May 21st, 2009 at 3:55 pmPara leer de ADC0 a ADC7 cambia esto:
__###################__
/* ch=ch&0b00000111;
ADMUX|=ch; */ <—– Error
__###################__
Por esto:
May 26th, 2009 at 9:40 am__###################__
ch=ch&0×07;
ADMUX = (ADMUX&0xF8)|ch;
__###################__
Hello i´m making a voltmeter with assembler, my question is if i want to send to the lcd the value of the voltage instead of the binary number what do i have to do?
June 1st, 2009 at 11:40 pmI think i have to compare the result of the ADC and then assigned the value that i want in the LCD but i have to compare several values and i think i lost time comparing , or what do you think?
Conver the binary no to a string say if ADC read out is 792 (stored in int) break it to ‘7′, ‘9′, ‘2′ ij ASCII and sent to LCD
The algo would be
int a;
a=ReadADC();
while(a)
June 2nd, 2009 at 6:17 am{
char ch;
ch=a%10; // divide by 10 and strore remainder in ch, 792%10=2 so u get the last digit
ch=ch=10; // remove last digit, 792/10= 79
}
Hello Avinash!
Great tutorials! A question regarding “Fig: LDR Connected to ADC of AVR”. What I see there is 100 kohm resitor going to ground. Is that a standard resitor value for all types of ADC connections? I have seen 10 kohm resitor values as well in schematics. What I try to say is: should it always be a resistor placed (to ground) like that when using AVR adc?
Regards
June 3rd, 2009 at 2:19 pmJohan
@Johan
Here LDR and R(100K) froms a Voltage Divider. More info here http://en.wikipedia.org/wiki/Voltage_divider
June 4th, 2009 at 8:28 am@Johan
here LDR is changing resistance according to light but ADC can measure Voltage only. To convert RESISTANCE to VOLTAGE we have used a Voltage Divider.
June 4th, 2009 at 8:31 amhi avinash
July 27th, 2009 at 1:01 ammy question is how to get ADC from multiple channels simultaneously.i used to get but the result of all the channel was same .
thanks for your help
September 11th, 2009 at 5:14 pmi am connecting a LDR to a PIC. i want to compare the value that the LDR to the preset value. how do i represent this preset value in hex? for example, if the value of LDR is less than 3V, then do the following commands. how to concert the 3V to hex value? thanks.
September 28th, 2009 at 12:28 pm@Jing
3v is 60% of 5v (Ref Voltage) so 60% of 1024 (Max Value for 10BIT A/D) is required value. 60% of 1024 is 614.4 = 614 and its HEX equivalent is 266. You must write 0×266 (prefix by 0x) in ‘C’ so that compiler knows it is a HEX and not decimal.
September 28th, 2009 at 12:42 pmHey Avinash,
October 20th, 2009 at 1:55 pmWhere do you buy all your electronics stuff in India? I live near Dehradun and for me it seems impossible to get any stuff, no hobby shops around!! Is there any safe online shop which deals with it and do you know any place around Delhi or preferably Lucknow?
I was able to get in touch with ATmega16 there in Lucknow for my surprise!! :p
Hello Pranav,
Many people in India face same problem. Thats why I started a Online Shop. You can visit it here
http://shop.extremeelectronics.co.in/
I was very angry by local dealers not giving correct parts and also charge so high.
October 20th, 2009 at 4:07 pmhi pranav,
October 21st, 2009 at 10:02 pmU better visit delhi,s lajpat rai market for required stuff ass you can found every thing required there…and that cheaper then any where else u will get
Hi, I’ve build a filtering ciruit and attached it to the adc, connected to the adc is a resistor, on one side of this i’m getting 2.5v on the micro side im getting 0v. if I remove the micro, this goes up to 2.5v. The signal is at approx 0.2mA is this enough? What do i need to set the DDR to for the adc port? I want to use some of the other portbits as digital inputs. Thanks for any help!
December 11th, 2009 at 8:10 pmhi avinash, good work.
December 15th, 2009 at 4:09 pmhi i am very poor in basic electronics and also in practicle(circuit designing)….
hi can u sugest me some good websites that gives some idea on basics and circuit design..
Hi
December 18th, 2009 at 5:52 pml am just starting to work with atmega16 controller and got a hang first tutorials and desided to try this one to but then l connect everything together but then l start a program a result l get is always 1023 and l tryed hooking up a pontiometer insted a LRD and turning it but always l get only 1023 is there something l do wrong with my software? maybe with registers? l used exactly a same code as your posted in this tutorial and l got nothing. Connections is ok
@Andrew
Please disconnect everything from the ADC port. The value now should show random value. Now connect it directly to Vcc the value should be 1023 and then connect (ADC0) to the GND pin it should show 0.
December 18th, 2009 at 6:56 pmyeah tryed it and then noticed some mistakes in a code l wrote it works nicely thx
December 18th, 2009 at 8:53 pmi have gone through the program.its so simple and quickly understood, but only confusion is that which port is assigned to take output,and how to connect that port to LCD…?
February 10th, 2010 at 3:45 pm@ABY
February 10th, 2010 at 4:25 pmplease see article on LCD interfacing first. their you will get details on lcd connections
Very nice tutorial!
I have one question, is it possible to use the other pins to other stuff (like lighting LEDs) if I only use one pin for the AD conversion? Or will the AD conversion use the whole port?
I use an AT Mega 16 and the port A is used for AD conversion as well. Can I use port A both to do AD conversion on pin 0 and light a LED on port 1?
Kind regards
February 10th, 2010 at 7:15 pm@Samuel,
good question. I recommend using the other pins of ADC PORT as INPUT only. And use some other I/O port for OUTPUT purpose. Once I tried to do the same thing. One ADC was used as ANALOG IN while the other pins carried medium frequency DIGITAL OUTPUT signal (to switch seven segment displays). In this configuration the ADC result were not constant, it was fluctuating. When I made the other PINs of ADC free and used some other PORts the problem was solved!
February 10th, 2010 at 7:24 pmSo this info may be useful for you.
Your tutorial is just awsome!!I just want to know how to use two or more adc channels simulataneously.Old question asked so far.Bt pls hlp me out.thnx.
March 17th, 2010 at 3:51 amsatyantan you just need to change the settings of ADMUX register for accessing different pins of ADC.Have a good look at the Data Sheet.
March 29th, 2010 at 8:41 amthanks for d tutorials… they were very helpful
April 3rd, 2010 at 6:49 pmthe atmega32 i am using shows highly fluctuating values even when the input to the pin is constant… wht cud be the problem?
and can we read negative voltages using atmega32 adc?
im a little confused with the lines:
ch=ch&0b00000111;
ADMUX|=ch;
does this mean that you are asking it to perform ADC on the first three channels, although you only need it from channel 0 because this is where you are reading the result from, so it could really be ch=ch&0b00000001;? Or have I drastically misunderstood this? So if i wanted to read if from channel 7 i could put ch=ch&0b10000000; and then change the last part to adc_result=ReadADC(7);?
April 18th, 2010 at 7:56 pm@Max
ch=ch&0b00000111;this line limits the channel number to a valid value. If you pass ch between 0-7 it remains unchanged but if you pass for example 11 it will be converted to 3. To get how it works you must be familiar with binary numbers and bit wise logical operation using bitwise logical operators like &.
April 20th, 2010 at 7:56 amahh thanks, this has clarified things for me a lot, i understand now. Now i shall get to work on programming my line following robot!
April 22nd, 2010 at 7:04 pmmy question is how to get ADC from multiple channels simultaneously.
i tried to edit sample code described above to such form:
.
.
.
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();
adc_result=ReadADC(1); // Read Analog value from channel-1
LCDWriteIntXY(10,1,adc_result,4); //Print the value in 10th column second line
}
.
.
.
but the result of all the channel was same .
April 23rd, 2010 at 2:03 pm@ Peter
ReadADC(0) just reads channel 0! The ADC can only do one conversion at one time (unless you’ve got a fancy chip with more than one ADC multiplexer on)
One way I did this was I wrote a conversion complete interrupt, which on conversion of a channel, stored it in to a variable (an array to be precise) and then started on the next conversion.
In the main loop, where ever I wanted to read more tha one ADC value, i just read the values out of the array, which were contstantly being updated by the interrupt! You can have my code if you like, just reply and let me know!
April 24th, 2010 at 12:45 pm@ Phil:
Please can I have your code?
May 5th, 2010 at 5:43 amMultiple ADC Reading code can be found here
May 5th, 2010 at 8:29 amhttp://extremeelectronics.co.in/robotics/obstacle-avoiding-robot-using-avr-atmega32-%e2%80%93-part-ii/
Hey, where can u find the lcd.h file? I took it out of his lcd guide, but then I still get some errors like:
I:\Eli, Elo, TT,\GIP\SVEN\ldr\default/../ldr.c:45: undefined reference to `LCDInit’
May 8th, 2010 at 10:09 pmI:\Eli, Elo, TT,\GIP\SVEN\ldr\default/../ldr.c:46: undefined reference to `LCDByte’
I:\Eli, Elo, TT,\GIP\SVEN\ldr\default/../ldr.c:52: undefined reference to `LCDWriteString’
I:\Eli, Elo, TT,\GIP\SVEN\ldr\default/../ldr.c:53: undefined reference to `LCDGotoXY’
I:\Eli, Elo, TT,\GIP\SVEN\ldr\default/../ldr.c:53: undefined reference to `LCDWriteString’
I:\Eli, Elo, TT,\GIP\SVEN\ldr\default/../ldr.c:58: undefined reference to `LCDGotoXY’
I:\Eli, Elo, TT,\GIP\SVEN\ldr\default/../ldr.c:58: undefined reference to `LCDWriteInt’
@Sven
Read the LCD Article CAREFULLY ! I think you are running too fast!
May 9th, 2010 at 8:41 amHello Avinash,
June 25th, 2010 at 3:02 amyour tutorials are really wonderful, they are very easy to understand and implement.
I want to read voltage values from 8 sensors using 8 adc channels on Atmega32 MC and then write these values to an SD card memory continuously, I have all the hardware required.
Can you suggest me an easy way to do this or do you have any tutorials related to this application. please help me out.
thanks.
@Hari
http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html
I think this article will surely help you out!
July 11th, 2010 at 3:18 pm[...] Analog To Digital Convertor [...]
August 9th, 2010 at 11:44 am