Advertisement
adc

AVR Analog to Digital Conversion (ADC) – Tutorial #13

Did You Know? The “real” world we are living in is Analog! Whether it is sound, light, temperature or humidity, all are continuously varying things. However, when we want to communicate with the “digital” world, it is crucial to use digital values which can be easily recognized by the computing systems. The computing system understands this by converting them into binary numbers.

So for transferring external continuous information (analog information) into a digital/computing system, we must convert them into integer (digital) values. This type of conversion is carried out by Analog to Digital Converter (ADC). The process of converting an analog value into digital value is known as Analog to Digital Conversion. In short, Analog signals are real world signals around us like sound and light.

Digital signals are analog equivalents in digital or numeric format which are well understood by digital systems like microcontrollers. ADC is one such hardware which measures analog signals and produces a digital equivalent of the same signal. AVR microcontrollers has inbuilt ADC facility to convert analog voltage into an integer. AVR convert it into 10-bit number of range 0 to 1023.

Note that we have an Analog Reference (Aref) Voltage also, which will be considered equivalent to 1023 and any voltage value less than this Aref will have less number than 1023. The input range is 0-Aref and digital output is 0-1023. Here 0V will be equal to 0, and Aref/2 will be equal to 512 and so on.

adc

ADC in Atmega8

Now you have the basics of ADC, let us move to the inbuilt ADC of AVR microcontrollers. First of all note that the ADC is multiplexed with Port C, and the ADC can be operated in single conversion mode and free running mode. In single conversion mode, the ADC does a single conversion and stops. But in free running mode the ADC is continuously converting, ie it does a conversion and then start the next conversion instantly after that. Here are a few concepts with regards to Atmega8 to know beforehand:

  • ADC Prescaler: The ADC needsa clock pulse for the job,and for this the system clock is divided by a number (2, 4, 16, 32, 64 and 128) to get the lesser frequency (ADC requires a frequency between 50KHz to 200KHz)
  • ADC Channels: The ADC in Atmega8 PDIP package has 6 channels, allows you to take samples from 6 different pins
  • ADC Registers: Register provides the communication link between CPU and the ADC. You can configure the ADC according to your need using these registers. The ADC has 3 registers only:
    1. ADC Multiplexer Selection Register – ADMUX: For selecting the reference voltage and the input channel
    2. ADC Control and Status Register A – ADCSRA: It has the status of ADC and is also used to control it
    3. The ADC Data Register – ADCL and ADCH: Final result of the conversion is stored here
  • AVCC: This pin supplies power to ADC. AVCC must not differ more than ± 0.3V from Vcc
  • AREF: Another pin which can optionally be used as an external voltage reference pin.
  • Voltage Resolution:This is the smallest voltage increment which can be measured. For 10 bit ADC, there can be 1024 different voltages (for an 8 bit ADC, there can be 256 different voltages)

In principle, ADC in AVR microcontrollers uses a technique known as successive approximation by comparing input voltage with half of the reference voltage generated internally. The comparison continues by dividing the voltage further down and updating each bit in ADC register by 1 if input voltage is high, 0 otherwise. This process lasts 10 times (for 10 bit ADC) and generates resulting binary output.

A short break to the boring theory! Let’s start a simple test program to see our ADC in action. In this example, Atmega8 reads a 10K potmeter connected to PORT C and turns on (or off) the LEDs connected to PORT D. Here, the ADC is used in single conversion mode with 10 bit precision. Wire the hardware as shown, and feed the code to Atmega8. When the work is finished, slowly turn the 10K potmeter and check the result. As coded, if the value is less than 512 then LED1 (at PD6) should be turned on, else LED2 (at PD7).

(This is the right time to grab the Atmega8 datasheet again, for a jump to the ADC chapter. Just open the ADC chapter and carefully read description of every register, plus all about the ADC circuitry)

AVR13-2

#include <avr/io.h>
#define PORT_ON(port,pin) port |= (1<<pin)
#define PORT_OFF(port,pin) port &= ~(1<<pin)
int main(void)
{
   unsigned int adc_value; // Variable to hold ADC result
   DDRD=0xff; // Set Port D as Output
   PORTD = 0x00;
   ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);
   // ADEN: Set to turn on ADC , by default it is turned off 
   //ADPS2: ADPS2 and ADPS0 set to make division factor 32 
   ADMUX=0x05; // ADC input channel set to PC5
   while (1)
   {
      ADCSRA |= (1<<ADSC); // Start conversion
         while (ADCSRA & (1<<ADSC)); // wait for conversion to complete
      adc_value = ADCW; //Store ADC value
            if (adc_value < 512)
            {
                  PORT_OFF(PORTD,7); // Toggle LEDs
                  PORT_ON (PORTD,6);
            } 
            else
            {
                  PORT_ON(PORTD,7); // Toggle LEDs
                  PORT_OFF (PORTD,6);
            }
   }
}

→ Part 14: AVR & UART
← Part 12: AVR PWM Pulse Width Modulation

14 Comments

Join the conversation!

Error! Please fill all fields.
  • ildar

    This example may fail to work if the Vref isn’t set right. For Vref <- AVcc and ATmega328 this is set like this:
    ADMUX = 0x5 | (1<<REFS0);
    Of course RTFM for details.

  • Saurabh

    Sir
    I need a program which controls delay between 4 Running LEDs using ADC .I tried this one(below) but it has many errors.Please help me.

    Program :-
    #include
    #include
    int main(void)
    {
    int adc=0 ;
    DDRB=0xff; // Set Port B as Output
    PORTB = 0x00;
    ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);
    // ADEN: Set to turn on ADC , by default it is turned off
    //ADPS2: ADPS2 and ADPS0 set to make division factor 32
    ADMUX=0x03; // ADC input channel set to PC5
    while (1)
    {
    ADCSRA |= (1<<ADSC); // Start conversion
    while (ADCSRA & (1<<ADSC)); // wait for conversion to complete
    adc=ADCW ;
    PORTB=0b00010000;
    _delay_ms(adc);
    PORTB=0b00001000;
    _delay_ms(adc);
    PORTB=0b00000100;
    _delay_ms(adc);
    PORTB=0b00000010;
    _delay_ms(adc);
    }
    }

    • ildar

      > error: __builtin_avr_delay_cycles expects a compile time integer constant
      You use _delay_ms() in a wrong way.

    • ShBatman

      I dont think there is a register named ADCW. It should be ADC

  • suria sarath

    If we use multiple ADC pin at same time. Example please

    • Jamshid

      You can connect 8 signle ended Analog signals at a time. These are multiplexed and is given to the ADC. The selection of the Mux is controlled using a register.

  • Ethan

    Please help this code won’t work, Its a adc read function and should return the value back to the calling function but gets stuck at this line.

    while (ADCSRA & (1<<ADSC)); // Wait for Conversion to complete

    ADSC doesn't return to 0

    It gets stuck at the line where it waits for the conversion to complete. Its taken hours to debug to this point. heres the code

    #include

    int adcread (void)
    {
    ADCSRA = (1<<ADEN) | (1<<ADPS2) |(1<<ADPS1) | (1<<ADPS0); // Enable ADC and set pre-scaler to 128
    ADMUX = (1<<ADLAR); // Left adjust so we read the 8 MSB's

    int adcraw;
    ADCSRA |= 1<<ADSC; // Starts Conversion
    while (ADCSRA & (1<<ADSC)); // Wait for Conversion to complete

    return adcraw;
    }

  • John

    Unfortunately, this is not working for me :/ I’m getting values above 1023 from the adc apparently, how can this happen?

  • rijo

    sir i need a program
    vmlab,atmega 8 microcontroller i useing, just simple ADC coversion , and the code for slider varoation

  • T.K.Hareendran

    Simo: I am in a plan to add many more interesting & useful information in this tutorial. Thanks for the feedback!

  • simo

    Good morning sir, I am onesimo currently working thinking of going back to college next year.

    Thank you very much sir for these tutorials. Now I can start learning programming on pure c using arduino. Can you also post Uart tutorial n some more small projects to understand more both hardware and software.

Looking for the latest from TI?