ADC (Analog To Digital Converter) of AVR Microcontroller

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.
ADC Theory

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.

  1. ADC Multiplexer Selection Register – ADMUX : For selecting the reference voltage and the input channel.
  2. ADC Control and Status Register A – ADCSRA : As the name says it has the status of ADC and is also use for controlling it.
  3. 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
We will go for 2nd option, i.e. Our reference voltage will be Vcc(5v). So we set
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

using adc of avr to sense light using ldr

Fig: LDR Connected to ADC of AVR

You have to connect a LDR (light dependant resistor) as shown above. After burning the code on chip use a light source to throw some light on LDR, the ADC will show a value between 0-1024 depending on light. For dark the value should be close to 0 while for bright condition the value will become close to 1000.
using adc of avr

Fig: Screenshot of ADC Test App.

Downloads

Note:

More AVR ADC Applications

AVR ADCGraph Windows PC

ADC Data On PC (Click to Read More …)

 

Facing problem with your embedded, electronics or robotics project? We are here to help!
Post a help request.

Avinash

Avinash Gupta is solely focused on free and high quality tutorial to make learning embedded system fun !

More Posts - Website

Follow Me:
FacebookLinkedInGoogle Plus

173 thoughts on “ADC (Analog To Digital Converter) of AVR Microcontroller

  • Pingback: Interfacing Temperature Sensor with AVR Microcontrollers - LM35 | eXtreme Electronics

  • By hamid - Reply

    hi.please guide me that how to use 2 or more channel of atmega32 adc(porta.0 ..7) togher by bascom. thanks alot best regard

  • By John - Reply

    Is it possible to configure different pins with different reference voltages simultaneously?

    For example, PA1 at 2.56V, PA2 at VCC, and PA3 at VREF?

    • By Iran - Reply

      Hi
      Yes, you can define voltage reference at any time on A/D registers. First time you configure A/D voltage reference on AVCC pin and then read A/D conversion then you configure A/D voltage reference on AREF pin and again read A/D conversion and finally you may like to use inside voltage reference that you can define it on A/D register.

  • By Avinash - Reply

    @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.

  • By John - Reply

    Two 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?

  • By Avinash - Reply

    @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.

  • By Avinash - Reply

    @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

  • By raghu - Reply

    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 …

  • By Avinash - Reply

    @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!

  • By ade - Reply

    Hi,
    can you pls provide the asm code to this program.Thanks alot

  • By Dhawal - Reply

    Can I use 2 multiplexer pins for ADC Analog Compare and rest for Input?……

  • Pingback: Reading Temperature Sensor from STK500 « Home Commander

  • By Adeola - Reply

    Hi Avinash,
    your 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.

  • By Avinash - Reply

    @Adeola,

    I think that is very easy once you understand the above code.

  • By cgo - Reply

    how to write as sembly language program in 8051 in order to make a light adc involving sensor an lcd?

  • By wube - Reply

    HI
    I like your tutorial,but i have one question i.e. how to use assembly language rather than c.
    Thank you

  • By Sandeep - Reply

    Hi Avinash,
    I 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.

  • By Avinash - Reply

    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

  • By Sandeep - Reply

    Dear Avinash,
    I have sent the required information on urs email (me@avinashgupta.com). Plz tell me the solution.

  • By santosh - Reply

    hello….if the vref=2.5 and the input voltage is 3vol..then the how much ‘ll b the decimal value in ADCH

    • By Avinash - Reply

      @santosh

      adc

  • By asyar - Reply

    hi everybody.
    i 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?

  • By Ashutosh - Reply

    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

  • By snowserf - Reply

    Para leer de ADC0 a ADC7 cambia esto:
    __###################__
    /* ch=ch&0b00000111;
    ADMUX|=ch; */ <—– Error
    __###################__

    Por esto:
    __###################__
    ch=ch&0x07;
    ADMUX = (ADMUX&0xF8)|ch;
    __###################__

    • By Daniel Torres - Reply

      Thank you snowserf.
      Your comment was very help for me.

  • By pandrosof - Reply

    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?
    I 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?

    • By Avinash - Reply

      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)
      {
      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
      }

  • By Johan - Reply

    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
    Johan

    • By Avinash - Reply

      @Johan

      Here LDR and R(100K) froms a Voltage Divider. More info here http://en.wikipedia.org/wiki/Voltage_divider

    • By Avinash - Reply

      @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.

  • By sanjay - Reply

    hi avinash
    my question is how to get ADC from multiple channels simultaneously.i used to get but the result of all the channel was same .

  • By thiru - Reply

    thanks for your help

  • By jing jie - Reply

    i 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.

    • By Avinash - Reply

      @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 0x266 (prefix by 0x) in ‘C’ so that compiler knows it is a HEX and not decimal.

  • By Pranav - Reply

    Hey Avinash,
    Where 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

    • By Avinash - Reply

      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.

  • By Saif ullah khalid - Reply

    hi pranav,
    U 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

  • By Phill - Reply

    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!

  • By mohan - Reply

    hi avinash, good work.
    hi 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..

  • By Andrew - Reply

    Hi
    l 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

  • By Avinash - Reply

    @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.

  • By Andrew - Reply

    yeah tryed it and then noticed some mistakes in a code l wrote it works nicely thx 🙂

  • By ABY - Reply

    i 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…?

    • By Avinash - Reply

      @ABY
      please see article on LCD interfacing first. their you will get details on lcd connections

  • By Samuel Skånberg - Reply

    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

    • By Avinash - Reply

      @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!
      🙂
      So this info may be useful for you.

  • By Sayantan - Reply

    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.

  • By Saurabh - Reply

    satyantan you just need to change the settings of ADMUX register for accessing different pins of ADC.Have a good look at the Data Sheet.

  • By lakshmi - Reply

    thanks for d tutorials… they were very helpful
    the 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?

  • By Max - Reply

    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);?

    • By Avinash - Reply

      @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 &.

  • By Max - Reply

    ahh thanks, this has clarified things for me a lot, i understand now. Now i shall get to work on programming my line following robot!

  • By Peter - Reply

    my 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 .

  • By Phill - Reply

    @ 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!

  • By Jean - Reply

    @ Phil:

    Please can I have your code?

  • By Avinash - Reply
  • By Sven - Reply

    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’
    I:\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’

    • By Avinash - Reply

      @Sven

      Read the LCD Article CAREFULLY ! I think you are running too fast!

  • By Hari - Reply

    Hello Avinash,
    your 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.

  • By Arifuddin Sheik - Reply

    @Hari

    http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html

    I think this article will surely help you out!

  • Pingback: Interfacing MMA7260 Triple Axis Accelerometer with ATmega32 - AVR Tutorial | eXtreme Electronics

  • By wlewis - Reply

    @Av

    Hey Av.. any chance of putting a mapping function into the adc? These LSR’s have a wide range of resistance like from 0 – 200k +.

    Youre doing a great job.

  • By wlewis - Reply

    @Av

    I have a great idea for a tutorial… sensors which output are measured by frequency. cool huh? :o)

    • By Avinash - Reply

      @Wlevis,

      Nice Idea, give me a example sensor and I will design code for it.

  • By wlewis - Reply

    @Av.. about ADC.. how about a tutorial that does a 10bit conversion?

  • By wlewis - Reply

    @Av..

    To my knowledge, practically all the non-ic humidity sensors are frequency dependant devices

  • By Robin - Reply

    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.

  • By Song Ying - Reply

    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?

  • By Lee - Reply

    easy to understand, tks

  • By ahmad kassem - Reply

    hello
    how can i obtain an analogue output from avr microcontroller thank you

    • By Guldsdone - Reply

      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.

  • By Avinash - Reply
  • By Chris - Reply

    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!

    • By Avinash - Reply

      @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

  • By Thomas H - Reply

    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;

    • By Avinash - Reply

      @Thomas H

      Thanks for finding and fixing the bug!

    • By avr_bike - Reply

      Thanks a lot Thomas!

      I had been troubled by the original channel selection code bug for 3 days…

    • By Smith - Reply

      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

      • By Smith -

        Also Avinash kindly correct the bug in the original code to avoid misleading people.

        Regards

      • By Chupo_cro -

        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.

      • By Avinash -

        @Chupo_cro,

        Thanks a lot for your time to post a useful comment 🙂

      • By Chupo_cro -

        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)…

      • By Chupo_cro -

        @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

    • By Razvan - Reply

      Thank you also Thomas, that was really annoying!

    • By ThomasH - Reply

      No problem! Glad to see my comment still helps people! Why not just fix the code on this page?

    • By sumit - Reply

      @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…

    • By sumit - Reply

      k…
      i got it working the problem was with hardware
      replaced it and its working nw…..

  • By Phill - Reply

    @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!)

  • By Chris - Reply

    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.

  • By Jayant - Reply

    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.

  • By Jayant - Reply

    Sorry for the question posted in my previous comment.
    I didn’t noticed the bitwise operator ‘|’ in ADMUX|=ch;.
    Anyway thanking you.

  • By dinesh ygv - Reply

    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.

  • By praveen - Reply

    please provide some insight into use of ADC in differential mode and a sample code to assist beginners .

  • By dama - Reply

    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.

    • By Avinash - Reply

      declare percent as float like

      float percent;
      percent = (adc_result*100.00)/1023.00;

      • By avinash -

        What changes do i need to make to print the decimal values also?

  • Pingback: LED with adjustable flash, need help

  • Pingback: Visualize ADC data on PC Screen using USART - AVR Project | eXtreme Electronics

  • By Sadiqur Rahman - Reply

    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…

  • By kusum - Reply

    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?

  • By Jeraj - Reply

    Its really a nice tutorial for new guys in avr!!!

  • By Jeraj - Reply

    Its really a nice tutorial for new guys in avr!!!
    thanx dear.!!

  • By Mike - Reply

    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.

  • By Antzy - Reply

    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…

  • By Abhilash - Reply

    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.

    • By Avinash - Reply

      @Abhilash

      Thanks, but I personally don’t like Arduinos. So I am never gonna work on those.

  • By Andre - Reply

    Hi Avinash & thanks for this wonderful page of AVR. I have a question friend. where returns the function return(ADC)? thanks

  • By rizwan - Reply

    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

  • By steve - Reply

    Thanks! This is an excelent and easy tutorial to follow. The example program worked first time also. Very inspiring.

  • By abida - Reply

    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?

  • By Gangster - Reply

    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???

  • By Md Sharif Ahmed - Reply

    what is the function of
    void Wait()
    {
    uint8_t i;
    for(i=0;i<20;i++)
    _delay_loop_2(0);
    }

  • By chintan pathak - Reply

    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 ?

    • By gaurav - Reply

      Here normal soldering is used . But over the soldering ,hot glue is used to protect the wires from braking.

  • By python00078 - Reply

    line follower program for analog inputs to mcu ….thanks in advance

  • Pingback: Interfacing Analog Joystick with AVR ATmega32 | eXtreme Electronics

  • By shayan - Reply

    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);
    }

  • By masterofdisguise - Reply

    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.

    • By Avinash - Reply

      @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 |

      • By masterofdisguise -

        @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.

  • By ify - Reply

    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

  • By Rashid - Reply

    Good effort its helps a lot to start ADC….

  • By san - Reply

    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 ?

  • By Yogesh - Reply

    Dear Avinash,

    How to display float value of mathematically manipulated ADC value on LCD?

    • By Avinash - Reply

      @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?

  • By ujjwal soni - Reply

    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

    • By Avinash - Reply

      @Ujjwal Soni,
      Read the data sheet of ATmega328

  • By Mahmud Ibrahim - Reply

    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.

    • By Avinash - Reply

      @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.

    • By Gangster - Reply

      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);

      • By Gangster -

        ########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);

  • By Mahmud Ibrahim - Reply

    Hey @Gangster,
    Thanks man. but i have solve the problem.any ways Thanks again for ur code.

    • By Rajeev Bista - Reply

      mahmud,i m facing the same problem.can u please tell how u corrected it??

  • Pingback: Getting Started with Microcontrollers | eXtreme ElectronicseXtreme Electronics

  • By sumita - Reply

    sir, I am working on ATtiny87. I just want to know the uart communication on that can you help me please…

    • By Avinash - Reply

      @Sumita,

      have you used USART before?

  • By Rohit Kamat - Reply

    hiii Avinash nice tutorials..ive bought avr programmer+dev board from extreme electronics recently…i must say you have done a grt work here

  • By chandra - Reply

    Hi Im chandra , i need the atmega8 c code for adc with uart .

    convert analog to digital and sent to uart port .

    chnadra.sreekala@gmail.com this is my email id sent to email …

  • By Arnab Chowdhury - Reply

    sir i have found a mistake in your ReadADC() function.
    suppose i want to first read ADC channel 1 and then read ADC channel 2.

    so when i write ReadADC(1);
    so ADMUX will be 0b01000001

    now if i write ReadADC(2);
    ADMUX |=ch;
    as u have done OR operation
    so ADMUX=ADMUX | ch;
    so ADMUX=0b01000001 | 0b00000010;
    so ADMUX=0b01000011;
    so ADMUX will read Channel 3 not Channel 2;
    So i think OR operation is wrong here.

  • By sunil - Reply

    this is a simple program for adc which i have written,working perfectly with atmega16,but when i write the same for atmega8 it doesn’t work…
    #include
    #define F_CPU 16000000UL
    #include
    int main()
    {
    DDRD=0xFF;
    PORTD=0×00;
    ADMUX=(1<<REFS0);
    ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
    while(1)
    {
    ADCSRA|=(1<<ADSC);
    while(!(ADCSRA & (1<<ADIF)));
    ADCSRA|=(1<<ADIF);
    PORTD=ADCW;
    _delay_ms(50);
    }
    }
    plz..tell me ,what are the changes needed to be made ???

  • By GEORGE - Reply

    Dear Sir
    Can I get a Weighing Scale 30kg 1g accuracy based on AVR. I need technical details tutorial with it.

    Regards
    George KUWAIT

    • By Thomas - Reply

      Dear George,
      You cannot get that resolution over the whole range. If you use a 10 bit ADC you have 2^10=1024 different values. The resolution and accuracy depend on the measuring hardware and the signal conditioning before it reaches the ADC on the AVR.

      I think a scale is unlikely to be 1g accurate in the 30kg range, because of the typical quality of sensors. Even if you average out many values, the measurement might not be repeatable, i.e. have low precision.

      See this for precision, accuracy, and resolution: http://www.tutelman.com/golf/measure/precision.php

  • By Palak - Reply

    Sir,
    using your codes I’ve read many sensors but this time something wrong. I’m working on At-8, two ADC channel using. While I change the value of 5th ADC channel the value of 4th channel is automatically changed, and no vice-versa means there is no change by 4th even not in 4th output. Hardware is correct. What will be the reason . I’ve applied delay.

  • By nidalki - Reply

    thanks for this tutorial..
    am a newbie to avr..i have done this program with the same code but the value displayed in tha adc is always the same value ‘1023’..please help me..

    • By nidalki - Reply

      and am using protues to stimulate this program.

  • By arghavan - Reply

    hi
    could u please describe me more, how LRD works in this project?

  • By venkat - Reply

    Hi sir,

    i recently wrote a program for atmega8,iam getting
    0-255 values,how to have 10 bit i.e.,1024 readings…
    what should i be doing for that….

  • By Kevin - Reply

    Hi there,
    At lower temperature range especially for negative temperature mine adc value toggles around 2 to 3 degrees.Can anyone help me understand what exactly is going wrong.

  • By chet - Reply

    hai,
    I am connecting 2 ldrs to two adc pins,and want to compare the output power of two ldrs.so how should i go with it?
    as far as i know the ports read voltage/current of the ldrs.so how do I make it read the resistance and estimate their power?

    • By Thomas H. - Reply

      Hi Chet,
      The short answer is: No, you don’t want to do that. 😉 What is the purpose?

      The analog pins only measure voltage relative to the reference voltage, not current. Google ADC. To measure a varying voltage due to the LDR, google for example Arduino LDR. You would need a voltage divider or other circuit.

      To measure the current you would need a circuit that converts the current into voltage and amplifies it suitably. Depends on your application. LDRs don’t generate current by themselves! See http://www.electronics-tutorials.ws/io/io_4.html

      If you really want to measure power you can for example measure the resistance of the LDR using a multimeter under different lighting conditions and under those same conditions you measure your analog input value from a voltage divider. Tabulate those for approximations. Then you know voltage and resistance and can calculate power. Don’t expect to get accurate measurements unless you do it properly (which is too much to explain here).
      BR, Thomas H.

  • By DHEERAJ - Reply

    hi avinash,
    can you tell me what is the maximum input current one can give to adc pin of atmega 16,
    and tell the method of scaling down the current of 2amps to the current that can be given as input to adc input of atmega16.
    plz help me with this

    • By Thomas H. - Reply

      Hi Dheeraj,
      I think you have misunderstood the relationship between voltage, current and resistance. Read up.

      The ADC has very high input impedance (resistance), maybe on the order of 100 megaohms, and the current draw of the analog pin will be on the order of nanoamps (ideally 0). The ADC measures voltage, not current.

      If you need to measure current you can for example use a shunt resistor or other circuitry. Try googling that. If the shunt resistance is too high you will affect the operation of the circuit.
      BR,
      Thomas H.

  • By Avinash - Reply

    ADCs measure voltage NOT current! If you want to measure current then you first need to convert it to voltage.

    As Thomas H said ADCs and anyother voltage measuring device have very high input impedance and this is taught in class 12 physics.

  • By eli - Reply

    Hi Avinash
    please help me
    I can not set the rejister adc i want to start adc in timer0 and stop the adc in inttrupt adc and then read adc so store the array . i dont no please help!!!!
    EXcuse me sir!!!
    I dont have facebook so i cant downloade sample code for adc .i need for help you.
    thanks alot

  • By Aditya - Reply

    Hello Avinash,

    Can you please help me with the same concept where we can use LCD display instead of 7 segment display?

  • By Shreyansh Parekh - Reply

    Hi,
    Firstly, thank you for an excellent tutorial.
    it was of great help in understanding the basics of ADC.
    I also need to understand differential adc and how to use gain of adc in atmega 16.
    however with almost nothing on the web explaining it,
    it is getting really difficult.
    it would be of great help if u make a tutorial explaining this.
    Thank you.

  • By mehri hasani - Reply

    hi sir.can you help me how to calculate frequency by adc unit of atmega32 ??do you khnow any source about it??I write and debug a schematic and program in code vision to calculate and show voltage of an analog signal but I want to add a part to my program to calculate frequency of signal and show it.thanks alot

  • By santosh - Reply

    i want read 8 input channel to the avr 164a program.

  • By Babis - Reply

    I have a problem with STK600 ATMEGA2560 and board A08-0431.A
    I need to change the reference voltage for the ADC I need 2.56 and 1.1 the one after the other. When I change it it seems that the 1.1V is never applied. I tried to insert delay between these two succesive measurements and after 2s of delay the 1.1V finally was the reference for my ADC. Does anybody else experienced such a problem?

  • By Ranjit - Reply

    Hi,
    This is really nice tutorial for beginners like me.
    Just a small spelling mistake in below line:
    “If an electrical quantity is made to vary directly in proportion to this value (temperature etc) then what we have is Analogue signal.”

    Please change “Analogue signal” to “analog signal”.

  • By Deepam Das - Reply

    Actually I have bought the 40 pin avr development board from extreme Electronics store. But I am not getting any reading from the lm35 sensor.Is your development board is suitable for adc conversion or not!? Or I have to make external connection for this AREF pin. .

    • By Avinash - Reply

      @Deepam Das,

      You you are unable to make this. Please send the board to us. We will make all connection and load suitable program and send it back to you. The cost would be Rs. 3500

  • By DEEPAM DAS - Reply

    I couldn’t get you. Why i have to pay Rs. 3500? I just want to know whether the Aref pin in the development board should be left disconnected or connected to ground or Vcc through ceramic capacitor.

    • By Avinash - Reply

      It should be left unconnected.

      • By DEEPAM DAS -

        Thanks u so much…….

  • By jimmi sitepu - Reply

    it work for 10bits adc?

    • By Avinash - Reply

      Yes, the AVR’s have 10 bit ADCs only

  • By ashish - Reply

    I am not value of adc upto 1023 on my lcd using 10 bit adc in avr.I only get value upto 200.why?

  • By George Varughese - Reply

    I AM IN KUWAIT. HOW I CAN ORDER YOUR PRODUCTS.CAN YOU ACCEPT PAYPAL.

  • By iman - Reply

    hi
    i have assignment which about (analog to digital conversion ) in ATmega2560

    please can you help me with my assignment

  • By Abhay vhatkar - Reply

    How can we use adc value for delay?
    Please help me/response.

Leave a Reply

Your email address will not be published. Required fields are marked *


8 + four =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>