Sep-18th-2008

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.

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.

Note:

Download PDF version | Get Adobe Reader Free !!!


60 Responses to “Using the Analog To Digital Converter.”

  1. 1
    Interfacing Temperature Sensor with AVR Microcontrollers - LM35 | eXtreme Electronics Says:

    [...] Using the ADC of AVRs [...]

  2. 2
    hamid Says:

    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

  3. 3
    John Says:

    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?

  4. 4
    Avinash Says:

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

  5. 5
    John Says:

    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?

  6. 6
    Avinash Says:

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

  7. 7
    Avinash Says:

    @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

  8. 8
    raghu Says:

    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 …

  9. 9
    Avinash Says:

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

  10. 10
    ade Says:

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

  11. 11
    Dhawal Says:

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

  12. 12
    Reading Temperature Sensor from STK500 « Home Commander Says:

    [...] Reading Temperature Sensor from STK500 By Willem Visser See: http://extremeelectronics.co.in/avr-tutorials/using-the-analog-to-digital-converter/ [...]

  13. 13
    Adeola Says:

    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.

  14. 14
    Avinash Says:

    @Adeola,

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

  15. 15
    cgo Says:

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

  16. 16
    wube Says:

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

  17. 17
    Sandeep Says:

    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.

  18. 18
    Avinash Says:

    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

  19. 19
    Sandeep Says:

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

  20. 20
    santosh Says:

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

  21. 21
    Avinash Says:

    @santosh

    adc

  22. 22
    asyar Says:

    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?

  23. 23
    Ashutosh Says:

    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

  24. 24
    snowserf Says:

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

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

  25. 25
    pandrosof Says:

    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?

  26. 26
    Avinash Says:

    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
    }

  27. 27
    Johan Says:

    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

  28. 28
    Avinash Says:

    @Johan

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

  29. 29
    Avinash Says:

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

  30. 30
    sanjay Says:

    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 .

  31. 31
    thiru Says:

    thanks for your help

  32. 32
    jing jie Says:

    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.

  33. 33
    Avinash Says:

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

  34. 34
    Pranav Says:

    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

  35. 35
    Avinash Says:

    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.

  36. 36
    Saif ullah khalid Says:

    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

  37. 37
    Phill Says:

    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!

  38. 38
    mohan Says:

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

  39. 39
    Andrew Says:

    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

  40. 40
    Avinash Says:

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

  41. 41
    Andrew Says:

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

  42. 42
    ABY Says:

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

  43. 43
    Avinash Says:

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

  44. 44
    Samuel Skånberg Says:

    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

  45. 45
    Avinash Says:

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

  46. 46
    Sayantan Says:

    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.

  47. 47
    Saurabh Says:

    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.

  48. 48
    lakshmi Says:

    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?

  49. 49
    Max Says:

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

  50. 50
    Avinash Says:

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

  51. 51
    Max Says:

    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!

  52. 52
    Peter Says:

    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 .

  53. 53
    Phill Says:

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

  54. 54
    Jean Says:

    @ Phil:

    Please can I have your code?

  55. 55
    Avinash Says:

    Multiple ADC Reading code can be found here
    http://extremeelectronics.co.in/robotics/obstacle-avoiding-robot-using-avr-atmega32-%e2%80%93-part-ii/

  56. 56
    Sven Says:

    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’

  57. 57
    Avinash Says:

    @Sven

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

  58. 58
    Hari Says:

    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.

  59. 59
    Arifuddin Sheik Says:

    @Hari

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

    I think this article will surely help you out!

  60. 60
    Interfacing MMA7260 Triple Axis Accelerometer with ATmega32 - AVR Tutorial | eXtreme Electronics Says:

    [...] Analog To Digital Convertor [...]

Leave a Reply

Comments

    • Akhila: thank you sir.
    • kiran: Thank you Avinash..i didn’t read the user manual thats the problem ..any how thank you.....
    • Akhila: Sir, I’m working on my final yr proj which is based on “Swarmbots”.I would...
    • Uttam Dutta: RGB LED are expected from long before, now easily avilabe, thank you, may be breadboard...
    • Sayantan: sir,can u please upload the custom characters tutorial for a 16×2 lcd (4-wire)module.I have...
    • kiran: Good tutorial ..In last tutorial you created library for Progfx with extension of .lib file...
    • Abraham: Awesome tutorials….I have gone through most of them…and they are so simple and...

Video

  • Comments

    • Akhila: thank you sir.
    • kiran: Thank you Avinash..i didn’t read the user manual thats the problem ..any how thank you.....
    • Akhila: Sir, I’m working on my final yr proj which is based on “Swarmbots”.I would...
    • Uttam Dutta: RGB LED are expected from long before, now easily avilabe, thank you, may be breadboard...
    • Sayantan: sir,can u please upload the custom characters tutorial for a 16×2 lcd (4-wire)module.I have...
    • kiran: Good tutorial ..In last tutorial you created library for Progfx with extension of .lib file...
    • Abraham: Awesome tutorials….I have gone through most of them…and they are so simple and...