How to work with the ADC unit of an AVR Micro-controller

Mathew Kevin July 9, 2017 3 Comments

Introduction

                The first step to digital signal processing is to convert a signal into digital data, and here the Analog to Digital Converter devices comes into action. Some of the AVR micro controllers include ADC unit in their features. This is a very useful unit for measurement related applications. The ADC used in AVR micro controllers are of successive approximation converter type. Read the Wikipedia article on SAR type ADC here. And you can read the Circuitstoday article too.

Using the ADC Unit

SAR type ADC needs these following things to run properly:

  • A very stable, low internal impedance, very low noise DC Supply
  • A Reference voltage
  • A clock signal for the digital circuitry

And few input signals necessary for the control of operation these are

  • ADEN, ADC Enable signal
  • ADSC, ADC Start Conversion
  • & Channel number to Multiplexer. This helps to select the ADC channel to perform a conversion operation.

Register Description & Minimum Configurations Required

ADC Multiplexer Selection Register – ADMUX

Bit

7

6

5

4

3

2

1

0

Bit Name

REFS1

REFS0

ADLAR

MUX3

MUX2

MUX1

MUX0

Read/Write

RW

RW

RW

R

RW

RW

RW

RW

Initial value

0

0

0

0

0

0

0

0

Bit 7:6 – REFS1:0: Reference Selection Bits

These bits select the voltage reference for the ADC, as shown in Table below. If these bits are changed during a conversion, the change will not go in effect until this conversion is complete (ADIF in ADCSRA is set). The internal voltage reference options may not be used if an external reference voltage is being applied to the AREF pin.

Interrupt Sense Control

REFS1

REFS0

Voltage Reference Selection

0

0

AREF, Internal VREF turned off

0

1

AVcc with External capacitor at AREF pin

1

0

Reserved

1

1

Internal 2.56V Voltage Reference with external capacitor at AREF pin

Bit 5 – ADLAR: ADC Left Adjust Result

Bits 3:0 – MUX3:0: Analog Channel Selection Bits

The value of these bits selects which analog inputs are connected to the ADC. See table below for details. If these bits are changed during a conversion, the change will not go in effect until this conversion is complete (ADIF in ADCSRA is set).

Input Channel Selections

MUX3

MUX2

MUX1

MUX0

Selected ADC Channel

0

0

0

0

ADC0

0

0

0

1

ADC1

0

0

1

0

ADC2

0

0

1

1

ADC3

0

1

0

0

ADC4

0

1

0

1

ADC5

0

1

1

0

ADC6 (Only for SMT Package)

0

1

1

1

ADC7 (Only for SMT Package)

1

0

0

0

1

0

0

1

1

0

1

0

1

0

1

1

1

1

0

0

1

1

0

1

1

1

1

0

1.30V (VBG)

1

1

1

1

0V (GND)

Bit 7 – ADEN: ADC Enable

Writing this bit to one enables the ADC. By writing it to zero, the ADC is turned off. Turning the ADC off while a conversion is in progress, will terminate this conversion.

Bit 6 – ADSC: ADC Start Conversion

In Single Conversion mode, write this bit to one to start each conversion. This first conversion performs initialization of the ADC. ADSC will read as one as long as a conversion is in progress. When the conversion is complete, it returns to zero. Writing zero to this bit has no effect.

Bit 5 – ADFR: ADC Free Running Select

Bit 4 – ADIF: ADC Interrupt Flag

This bit is set when an ADC conversion completes and the Data Registers are updated. The ADC Conversion Complete Interrupt is executed if the ADIE bit and the I-bit in SREG are set. ADIF is cleared by writing a logical one to the flag. Beware that if doing a Read-Modify- Write on ADCSRA, a pending interrupt can be disabled.

Bit 3 – ADIE: ADC Interrupt Enable

ADC Control and Status Register A – ADCSRA

Bit

7

6

5

4

3

2

1

0

Bit Name ADEN

ADSC

ADFR

ADIF

ADIE

ADPS2

ADPS1

ADPS0

Read/Write

RW

RW

RW

RW

RW

RW

RW

RW

Initial value

0

0

0

0

0

0

0

0

 

Bits 2:0 – ADPS2:0: ADC Prescaler Select Bits

These bits determine the division factor between the XTAL frequency and the input clock to the ADC.

ADC Prescaler Selections

ADPS2

ADPS1

ADPS0

Division Factor

0

0

0

2

0

0

1

2

0

1

0

4

0

1

1

8

1

0

0

16

1

0

1

32

1

1

0

64

1

1

1

128

The ADC Data Register – ADCL and ADCH

When an ADC conversion is complete, the result is found in these two registers. When ADCL is read, the ADC Data Register is not updated until ADCH is read. Consequently, if the result is left adjusted and no more than 8-bit precision is required, it is sufficient to read ADCH. Otherwise, ADCL must be read first, then ADCH. The ADLAR bit in ADMUX, and the MUXn bits in ADMUX affect the way the result is read from the registers. If ADLAR is set, the result is left adjusted. If ADLAR is cleared (default), the result is right adjusted

So to work with ADC, First of all, initial adjustments are necessary. And we need another function that initiates a conversion and generates a result. Let’s have look to the Codes:

void init() //Initiation Sequence
{ ADMUX |= (0<<REFS0)|(0<<REFS1); // Selecting the voltage reference
ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); // Select Prescaler Frequency and Enable ADC
}

int doConversion(char Channel_Number) // ADC Read Operation Sequence
{ ADMUX = (Channel_Number&0x0f)|(ADMUX&0xf0); // Set up the Multiplexer to Read Desired Channel
ADCSRA |= (1<<ADSC); // Start the conversion sequence
while(!(ADCSRA&(1<<ADIF))); //Wait until the ADIF Flag goes High. ADIF Indicates conversion completion
return ADC; // Return result to calling function
}

Code

Textbox: Code for the Experiment

#define F_CPU 1000000
#include
#include
#include
void init()
{ ADMUX |= (0<<REFS0)|(0<<REFS1);
ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

int doConversion(char Channel_Number)
{ ADMUX = (Channel_Number&0x0f)|(ADMUX&0xf0);
ADCSRA |= (1<<ADSC);
while(!(ADCSRA&(1<<ADIF)));
return ADC;
}

int main()
{ initLCD();
init();
printf(“ADC Ch.1 Value=”);
LCDcmd(0xc0); while(1)
{ printf(” “);
LCDcmd(0xc0);
printf(“%d”,doConversion(0));
_delay_ms(700);
}
return 0;
}

Textbox: Code of ‘ATmega8LCDcfg1.h’

#ifdef _LCD_CFG_H
#warning "LCD configuration file already loaded. ATmega8LCDcfg.h inclusion skipped."
#endif
#ifndef _LCD_CFG_H
#define _LCD_CFG_H 1
#ifndef _AVR_IO_H_
#include
#endif
#ifndef _UTIL_DELAY_H_
#include
#endif
#ifndef _STDIO_H_
#include
#endif
#define DEL1 10
#define DEL2 40
#ifndef DPORT
#define DPORT PORTB
#endif
#ifndef CPORT
#define CPORT PORTD
#endif
#ifndef DPDDR
#define DPDDR DDRB
#endif
#ifndef CPDDR
#define CPDDR DDRD
#endif
#ifndef RS
#define RS PD6
#endif
#ifndef EN
#define EN PD7
#endif
int LCD(char ch, FILE *fp);
// Function To send a single char. to LCD
void LCDcmd(char ch);
void initLCD();
static FILE lcd_str= FDEV_SETUP_STREAM(LCD,NULL,_FDEV_SETUP_WRITE);
/*
// Above: A stream is set up to give a formatted output
// to LCD using a macro FDEV_SETUP_STREAM(). The stream
// includes the function 'LCD()' that describes how a
// single character is sent to LCD. But the function
// prototype of 'LCD()' must be as per demand by the macro
// 'FDEV_SETUP_STREAM()'. See the prototype defined. This
// prototype is fixed. If this prototype difers. The program
// will not get complied.
// In the argument Field of 'FDEV_SETUP_STREAM(A,B,C)'. First
// field(A) holds the name of the function/routine that is to be
// used for sending a single charachter. Next field defines
// function to receive charachters. The next one defines if
// the stream to be set up can be used for reading charachters,
// writing charachters or both.
*/
void LCDcmd(char ch)
{ DPORT = ch;
CPORT = (1<<EN);
_delay_us(DEL1);
CPORT = (0<<EN);
_delay_us(DEL2);
if(ch==0x01||ch==0x02)
_delay_ms(10);
}
int LCD(char ch, FILE *fp)
{ DPORT =ch;
CPORT = (1<<EN)|(1<<RS);
_delay_us(DEL1);
CPORT = (0<<EN)|(0<<RS);
_delay_us(DEL2);
return 0;
}
void initLCD()
{ CPDDR = (1<<EN)|(1<<RS);
DPDDR = 0xff;
LCDcmd(0x38);
LCDcmd(0x0f);
LCDcmd(0x01);
LCDcmd(0x02);
stdout=&lcd_str;
}

Circuit Diagram

View PDF Working with ADC 

Picture: Circuit Diagram

 

Comments
  • Charles John
    September 3, 2012

    Great article

  • Murali
    August 17, 2012

    Thanks for Publishing…

  • rakesh
    August 7, 2012

    The only tutorial remaining is TWO Wire Interfacing! I hope someday I’ll come up with it!! 🙂 Now on!! There will be projects! good day!

    Thank you, and good day,
    Rakesh Bute
    Jnior Author
    Circuitstoday.com

Leave a Reply to Murali Cancel reply

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