Get Adobe Flash player
FacebookTwitterGoogle+
English Arabic French German Italian Portuguese Russian Spanish

Did you know?

One lightning bolt has enough electricity to service 200 000 homes
 

Help us stay online:

small donate

AVR ADC inputsWhen interfacing microcontroller to outside world we often need to make Analog/Digital conversions (ADC). In this example, we will make 5 ADC inputs scanning that is driven by interrupt. Data are placed in results buffer and sent to UART by using functions for conversion of integer value to string.

 

ATmega8535 has one ADC module and multiplexer at input to allow multiple inputs scanning. This is the case with almost all microcontrollers. This means conversions will be executed sequentionally by measuring one by one input.
For making of project we will use Codevision wizard with settings as shown on the picture.

uartADC

We will scan inputs 0,1,2,3,4.
Code for ADC initialization is given below

// ADC initialization
// ADC Clock frequency: 1000.000 kHz
// ADC Voltage Reference: AVCC pin
// ADC High Speed Mode: Off
// ADC Auto Trigger Source: Free Running
ADMUX=FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff);
ADCSRA=0xEB;
SFIOR&=0x0F;

Here we set 3 registers:
ADC Multiplexer Selection Register – ADMUX

 admux

• Bit 7:6 – REFS1:0: Reference Selection Bits
These bits select the voltage reference for the ADC, as shown in Table 84. 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.
1
Voltage Reference Selections for ADC
• Bit 5 – ADLAR: ADC Left Adjust Result
The ADLAR bit affects the presentation of the ADC conversion result in the ADC Data
Register. Write one to ADLAR to left adjust the result. Otherwise, the result is right
adjusted. Changing the ADLAR bit will affect the ADC Data Register immediately,
regardless of any ongoing conversions.

• Bits 4:0 – MUX4:0: Analog Channel and Gain Selection Bits

The value of these bits selects which combination of analog inputs are connected to the
ADC. These bits also select the gain for the differential channels. 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).

2

ADC Control and Status Register A – ADCSRA

adcsra

• 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. In Free Running
mode, write this bit to one to start the first conversion. The first conversion after
ADSC has been written after the ADC has been enabled, or if ADSC is written at the
same time as the ADC is enabled, will take 25 ADC clock cycles instead of the normal
13. 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 – ADATE: ADC Auto Trigger Enable
When this bit is written to one, Auto Triggering of the ADC is enabled. The ADC will start
a conversion on a positive edge of the selected trigger signal. The trigger source is
selected by setting the ADC Trigger Select bits, ADTS in SFIOR.
• 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 hardware when executing the corresponding interrupt
handling vector. Alternatively, 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.
This also applies if the SBI and CBI instructions are used.
• Bit 3 – ADIE: ADC Interrupt Enable
When this bit is written to one and the I-bit in SREG is set, the ADC Conversion Complete
Interrupt is activated.
• 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
3 

Special Function IO Register – SFIOR

SFIOR

• Bit 7:5 – ADTS2:0: ADC Auto Trigger Source
If ADATE in ADCSRA is written to one, the value of these bits selects which source will
trigger an ADC conversion. If ADATE is cleared, the ADTS2:0 settings will have no
effect. A conversion will be triggered by the rising edge of the selected interrupt flag.
Note that switching from a trigger source that is cleared to a trigger source that is set,
will generate a positive edge on the trigger signal. If ADEN in ADCSRA is set, this will
start a conversion. Switching to Free Running mode (ADTS[2:0]=0) will not cause a trigger
event, even if the ADC Interrupt Flag is set.

ADC Auto Trigger Source Selections

4

• Bit 4 – RES: Reserved Bit
This bit is reserved bit in the ATmega8535, and will always read as zero..

When creating project by using Codevision wizard these registers will be initialized automatically.
If you want to modify these settings, use data provided above to choose correct settings.

Conversion is as previously stated handled by ADC ISR. Here is that part of the code:

// ADC interrupt service routine
// with auto input scanning
interrupt [ADC_INT] void adc_isr(void)
{
static unsigned char input_index=0;
// Read the AD conversion result
adc_data[input_index]=ADCW;
// Select next ADC input
if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
   input_index=0; 
//analog multiplexer is set for correct input
ADMUX=(FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff))+input_index;
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
}

ISR will be executed as soon as ADC conversion is finished. In this way, we will have results in corresponding member of array adc_data[input_index].
For displaying measurement results we will use functions for work with strings:

void itoa(int n, char *str)

Converts the integer n to characters in string str. For use of this function we predefined string for measured data in string format.
This is char buf[5] and since maximum possible measured number of quants is 1023 we reserved 5 bytes for every digit of this number + string terminator.

void puts(char *str)

Outputs, using putchar, the null terminated character string str, located in RAM. Usage of this function is already explained in previous tutorials. Use site search to find it.

Here is code for data display:

puts(" Measured data from ADC is: ");
puts(" ADC0: ");
itoa(adc_data[0], buf);
puts(buf);

Delay is used to slow down sending process so we can see it clearly. This complete project with source code can be downloaded from THIS LINK.
Here is a snapshot of terminal development tool that displays results of measurement changes.

terminal

Below is whole code for this example with key lines highlighted.

/*****************************************************
This program was produced by the
CodeWizardAVR V2.04.4a Advanced
Automatic Program Generator
© Copyright 1998-2009 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project : 
Version : 
Date    : 12/20/2011
Author  : NeVaDa
Company : 
Comments:
Chip type               : ATmega8535
Program type            : Application
AVR Core Clock frequency: 8.000000 MHz
Memory model            : Small
External RAM size       : 0
Data Stack size         : 128
*****************************************************/
#include <mega8535.h>
#include <delay.h>
#include <stdlib.h>
#ifndef RXB8
#define RXB8 1
#endif
#ifndef TXB8
#define TXB8 0
#endif
#ifndef UPE
#define UPE 2
#endif
#ifndef DOR
#define DOR 3
#endif
#ifndef FE
#define FE 4
#endif
#ifndef UDRE
#define UDRE 5
#endif
#ifndef RXC
#define RXC 7
#endif
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<DOR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)
// USART Receiver buffer
#define RX_BUFFER_SIZE 8
char rx_buffer[RX_BUFFER_SIZE];
#if RX_BUFFER_SIZE<256
unsigned char rx_wr_index,rx_rd_index,rx_counter;
#else
unsigned int rx_wr_index,rx_rd_index,rx_counter;
#endif
// This flag is set on USART Receiver buffer overflow
bit rx_buffer_overflow;
char buf[5];
// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
   rx_buffer[rx_wr_index]=data;
   if (++rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
   if (++rx_counter == RX_BUFFER_SIZE)
      {
      rx_counter=0;
      rx_buffer_overflow=1;
      };
   };
}
#ifndef _DEBUG_TERMINAL_IO_
// Get a character from the USART Receiver buffer
#define _ALTERNATE_GETCHAR_
#pragma used+
char getchar(void)
{
char data;
while (rx_counter==0);
data=rx_buffer[rx_rd_index];
if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
#pragma used-
#endif
// Standard Input/Output functions
#include <stdio.h>
#define FIRST_ADC_INPUT 0
#define LAST_ADC_INPUT 4
unsigned int adc_data[LAST_ADC_INPUT-FIRST_ADC_INPUT+1];
#define ADC_VREF_TYPE 0x40
// ADC interrupt service routine
// with auto input scanning
interrupt [ADC_INT] void adc_isr(void)
{
static unsigned char input_index=0;
// Read the AD conversion result
adc_data[input_index]=ADCW;
// Select next ADC input
if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
   input_index=0; 
ADMUX=(FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff))+input_index;
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
}
// Declare your global variables here
void main(void)
{
// Declare your local variables here
// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTA=0x00;
DDRA=0x00;
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTB=0x00;
DDRB=0x00;
// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTC=0x00;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 9600
UCSRA=0x00;
UCSRB=0x98;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x33;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
// ADC initialization
// ADC Clock frequency: 1000.000 kHz
// ADC Voltage Reference: AVCC pin
// ADC High Speed Mode: Off
// ADC Auto Trigger Source: Free Running
ADMUX=FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff);
ADCSRA=0xEB;
SFIOR&=0x0F;
// Global enable interrupts
#asm("sei")
while (1)
      {
      // Place your code here
       //#asm("cli")    //enabling and disableing global interrupts when delay functions 
       //are used is recomended only if exact timings are importatnt
       //which is not case here so we will not use this
        /* 10ms delay */
        delay_ms(50); 
       // #asm("sei") //enable global interrupts if you disabled it before pause
        puts(" Measured data from ADC is: ");
        puts(" ADC0: ");
           itoa(adc_data[0], buf); 
        puts(buf);
        puts(" ADC1: ");
           itoa(adc_data[1], buf); 
        puts(buf);
        puts(" ADC2: ");
           itoa(adc_data[2], buf); 
        puts(buf);
        puts(" ADC3: ");
           itoa(adc_data[3], buf);   
        puts(buf);
        puts(" ADC4: ");
           itoa(adc_data[4], buf);
        puts(buf);
        putchar(13);
      };
}