Showing posts with label Microcontroller. Show all posts
Showing posts with label Microcontroller. Show all posts

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.

Thursday, April 28, 2011

Interfacing LCD

LCD or Liquid Crystal Display is fast becoming a preferred choice for use as interface device for displaying over 7-Segment.


Every LCD needs a driver to interface between the microcontroller and LCD module. This driver is included on the LCD module.





The LCD we are using here is a 16x2 LCD. That means it has 16 columns and 2 rows.


Pin number 1 and 16 will be written behind the LCD.


The Pin Configuration of the LCD is given below


 (open image in separate tab to expand)


The LCD uses three control lines, EN, RS and RW.


The EN line is called 'Enable'. This control line is used to tell the LCD that you are sending it data. To send the data to the LCD, your program should make sure this line is low (0) and then set the other two control lines and/or put data on the data bus. When the other lines are completely ready, bring EN high (1) and wait for the minimum amount of time required ( about 200ns), and end by bringing it low(0) again.


The RS line is the 'Register Select' line. When RS is low(0), the data is to be treated as a command or special instruction (such as clear screen, position cursor, etc.). When RS is high (1), the data being sent is text data which should be displayed on the screen. For example, to display the letter 'T' on the screen you would set RS high(1).


The RW line is the 'Read/Write' control line. When RW is low (0), the information on the data bus is being written to the LCD. When RW is high (1), the program is effectively querying (or reading) the LCD. All commands are write commands except for 1 ('Get LCD status'), which is a read command. Thus, RW will almost always be low.


4 bit Mode / 8 Bit mode:


The LCD can work in either 8 bit parallel mode, i.e, then entire 8 bit data can be sent at once, or it can be used in 4 bit mode. In 4 bit mode the 8 bit data is broken into packets of 4 bit data and sent (higher 4 bits first, then lower 4 bits). 4 bit mode simplifies the circuit.
Upon power on, the LCD is automatically in 8 bit mode.

Here, we are using the LCD in 4-bit mode.


Initializing the LCD


Before you may really use the LCD, you must initialize and configure it. This is accomplished by sending a number of initialization instructions to the LCD.



  • The first instruction we send must tell the LCD we'll be communicating with it with a 4-bit data bus (4-bit mode). We also need to select a 5x8 dot character font. These two options are selected by sending the command 20h ( 'h' denotes hexadecimal format) to the LCD as a command. 
  • We've now sent the first byte of the initialization sequence. The second byte of the initialization sequence is the instruction 0Fh. This commmand is sent to instruct the the LCD to, 'Display on, Cursor Blinking'.
  • Other commands are also available. Send higher nibble first, then lower nibble.

This circuit diagram can be used as a reference.






You can download a header file for your use from here. The pin connections are mentioned in the header file.

For any further clarification please leave a comment below or mail us at blogger.engg@gmail.com