Wednesday, June 8, 2011

Analog to Digital Converter (ADC)

(Circuit for IR Sensor for a Line Follower given at the end of the post)
Q1)  What is an analog signal?


In simple words, Any signal that looks like this  
is called an analog signal.


For example, if we were to plot the amplitude variation of a song with time, it would be an analog signal.
(The picture on the right is a part of amplitude variation of an actual song)


Q2) What is a digital signal?
Any signal that would look like this
is called a digital signal.                                                                                                  
                                                                                                                                    
                                                                                                                                
                                                                                                                                                                                                                                                         


The main difference between an analog signal and digital signal is that a digital signal will keep fluctuating between two fixed values i.e it will take no other value, whereas an analog signal has no such conditions.
The 'high' part of the digital signal is usually represented with a 1 and the low part with a 0.
The high part can be anything, but it must be fixed throughout. Generally, the high part should be at 5 Volts and the low part is at 0 Volt.
The process of converting an analog signal to a digital signal is called an analog to digital conversion.


It is recommended that you have the datasheet open in front of you before reading further.
Datasheets for 
1) ATMega16
2) ATMega32




Now, the MEGA series of AVR Microcontrollers (like ATMega16, ATMega32 etc) have an inbuilt ADC.
ATMega16 and ATMega32 have 8 ADC channels on PortA with PA0 being the first ADC channel and PA7 being the 8th ADC channel
The analog signal should be given at one of these channels. The analog signal is a simply varying analog signal, which can also be produced by simply making a voltage divider circuit. The circuit is shown at the end of the blog post.


Now, in the datasheet open the section for Analog to Digital Converter. Keep scrolling down till you come to ADC Multiplexer Register (ADMUX)


The register configuraton for ADMUX is as given below:


REFS1, REFS0 :
Now, bit 7 and bit 6 are the Reference Selection bits. The configuration of these bits is based on these conditions:




Note: AREF pin is pin number 32 in both ATMega16 and ATMega32


Let's assume that AREF is connected through a capacitor to ground.


Now, ATMega16/32 has a 10-bit ADC. This means that the maximum value that we can get  is :
210-1 = 1023 


The microcontroller takes samples of the incoming analog signal at a specific frequency. The frequency is defined by us. We'll come to that a little later. 
These samples are then converted to a digital value and stored in a 16 bit register called ADC Data Register. This register is made up of two 8-bit registers: ADCH (for storing the higher 8-bits) and ADCL ( for storing the lower 8-bits)
Since, it is a 10-bit ADC, by default, the lower 8 bits of the result is stored in ADCL and the most significant 2 bits are stored in ADCH.


ADLAR:  ADC Left Adjust Result
If ADLAR=0, the result is stored in ADCH and ADCL  as explained above and shown diagrammatically below.




if ADLAR is set to 1, the result is left adjusted, that means, the result is shifted towards the left by 8 places. This is a configuration.
It is shown below.
                                                
  Note this carefully, the ADC data register will be updated only after ADCH is read. Thus it is necessary to read the ADCH data register in the program.
Note: When we say read ADCH, it implies a C code statement like, k=ADCh, here k is some variable.
Thus, if we can manage with 8 bit precision (value till 255), we left adjust the result, and read only ADCH, it makes programming easier. Otherwise, ADCL must be read first and then ADCH.
Hence, by setting ADLAR=1, we get an 8 bit ADC instead of 10 bit ADC.                                                             
Thus, let ADLAR=1;
The remaining bits are used to select which ADC channel we are using. for example if we are using PA0, the last 5 bits will have  ( 00000), similarly, if we are using PA1, the last 5 bits will have  the value ( 00001 ). This is shown in the table below.

Thus, the ADMUX register can be defined as


ADMUX=0b01100000;   // if PA0 is the input


Next register of importance is ADCSRA register.


ADCSRA : ADC Control and Status Register



  • ADEN : ADC Enable
    Write this bit to 1 to enable ADC. If this bit is made 0 at any time, even when the conversion is going on, will turn off the ADC and terminate the conversion
  • ADSC: ADC Start conversion
    This bit is set to 1 to start conversion.
    These bit values need to be set outside the infinite loop. And then again inside the infinite loop. When it is set outside the infinite loop, a conversion is performed, which initializes the ADC.
    ADSC will read as 1 as long as conversion is progress. When the conversion is over it becomes 0.
    No other operation should take place on the ADC while the conversion is in pogress. This, we write a code to wait till ADSC==1;
  • ADATE: ADC Auto trigger enable
    In Auto trigger mode, we need to provide a triggering signal. If ADATE bit is set to 1, the ADC will start a conversion on the positive edge of the selected trigger signal.
  • ADIF: ADC Interrupt flag
    When the conversion is complete, an interrupt occurs and this bit is set to 1. The user needs to clear this bit after the interrupt occurs. To use any interrupts it is essential that global interrupts are enabled using the command sei();
  • ADIE: ADC Interrupt Enable.
    This bet is set to 1 by the user to enable ADC Interrupt.
ADPS2, ADPS1, ADPS0: Prescalar Bits.
Remember we were earlier talking about selecting the sampling frequency? Well, these are the bits that are used to select the sampling frequency.

Note: Nyquist criterion states the the sampling frequency must be twice the maximum frequency content of the signal. But, this is important mainly when the signal has to be reconstructed or some action needs to be performed utilizing the frequency of the incoming signal, e.g an Audio Signal. In cases like line follower sensors, there is no need to go by the Nyquist Criterion. Because we simply need to sample the incoming values


ADC Sampling frequency = (Clock Frequency of the Microcontroller/Division Factor)

Set these bits to (000) outside the infinite loop. Set to (001 or above as required) inside the infinite loop or just set it to (001 or above) outside the infinite loop itself.


Now, we write the sample code, assuming that I have only one input connected at PA0

#include<avr/io.h>
#include<util/delay.h>
unsigned char k;
void main()
{  
   ADMUX=0b01100000;    // setting ADLAR to 1
   ADCSRA=0b11000001;
  
  while(1)
  {    ADCSRA|=(1<<ADSC);          // start conversion
       while(ADCSRA&(1<<ADSC));  // wait here till ADSC is 1
       k=ADCH;                             // conversion is complete, store the result in a variable.
/* Once we have the ADC value stored, we can do whatever further is required, compare, display etc.
*/
   }
}

Please note that at one instant of time only one ADC channel can be used.
Now, what if we have to use multiple ADC Channels??

In this case we will enable one channel, store the ADC value, then disable it and enable another channel.
Since, this process happens in a matter of micro-seconds, in 'human' terms, all the channels are being sampled nearly simultaneously.


Code to use multiple ADC Channels


Assuming that one analog input is at PA0 and another at PA1. 


#include<avr/io.h>
#include<util/delay.h>

void main()
{  
   ADMUX=0b01100000;    // setting ADLAR to 1, initially selecting PA0
   ADCSRA=0b11000001;
   while(1)
  {  ADMUX=0b01100000;    // select PA0 
     ADCSRA|=(1<<ADSC);          // start conversion
     while(ADCSRA&(1<<ADSC));  // wait here till ADSC is 1
     k=ADCH;   
    
    ADMUX=0b01100001;     // select PA1
    ADCSRA|=(1<<ADSC);          // start conversion
    while(ADCSRA&(1<<ADSC));  // wait here till ADSC is 1
     l=ADCH;   
  
   // Similarly repeat for as many (max. 8) channels required.
  }
}
   
You can build a simple Voltage divider circuit to learn ADC.
For a voltage divider circuit, the output voltage (Vout) when converted to decimal will give a result (d) which can be calculated using the formula:

((Vout)x(Vref)/(255) )=d
Vref here is 5V.
For 8 bit ADC the denominator is 255. For 10 bit ADC, the denominator is 1023, (210-1)



Those of you trying to make a line follower, if you are going to use ADC, you can use the circuit for the line sensor shown below. The line marked output is given as input to one of the ADC channels of the microcontroller.

In case of any doubt, or if you need help in doing a project using ADC, please leave a comment below.


Follow Techila on Facebook.