Using and Testing the AVR® MCU ADC

Last modified by Microchip on 2023/11/09 09:02

In this video:

  • Provide a block diagram overview of the AVR® MCU's 10-bit Successive Approximation Analog-to-Digital Converter (ADC).
  • Input voltage reference options and input channels.
  • Discuss prescale and conversion timing requirements, keeping the ADC clock between 50 and 200 kHz, setting a prescalar of 64, since we have a F_CPU = 8 MHz.
  • Set the range of the ADC by setting the voltage reference (to AVcc).
  • Open a new project and set writing ADC_init ( ) using the datasheet as a coding reference.
  • Choose ADC1 as ADC input channel, connected to port C1 on the ATmega328P Xplained mini.
  • Configure the options in the ADC status and control register and ADC Auto Trigger Enable (ADATE).
  • Toggle an LED in the ADC ISR, so that we have a line of code to set a breakpoint on.
  • Connect a potentiometer to vary the voltage on the ADC pin.
  • Debug to verify the register configuration.
  • Example reading on the ADC result register: 0x156, or 342.
  • Vin = ADC * Vref / 1024 = 342 * 3.297 = 1.1011 measured by ADC, 1.104 measured by voltmeter, so 2.9 mV error or 1 LSB.
  • "AVR120: Characterization and Calibration of the ADC on the AVR MCU".

Procedure

Review the ADC Block Diagram

As we have done in previous lessons, we will start by reviewing the ADC block diagram from the datasheet.  It shows us that we will need to configure the ADC through three registers.

  • ADMUX:  ADC Multiplexer Select - This register will allow us to select the Internal Voltage Reference and choose between a variety of ADC channels.
  • ADCSRA:  ADC CTRL & Status Register - This register is for setting up the clock prescaler.
  • ADCH/ADCL: ADC Date Register - This register is where our 10-bit data will go after conversion

ADC Block Diagram​​​​​

Back to Top


Review Important Considerations

  • The device's successive approximation circuitry requires an input clock frequency between 50kHz and 200kHz to get maximum resolution.
  • The frequency of the CPU must be at least 100kHz in order for the prescaler to generate and acceptable clock frequency.
  • In order to get the full range of the ADC the voltage reference we should set the AVCC equal to VCC
  • After the conversion is complete, the conversion result can be found in the ADC Result Registers (ADCL, ADCH).  The formula is for a single ended conversion...

ADC Formula​​​​​

Back to Top


Start a New Project in MPLAB X IDE

Create a new project just like in previous lessons and and add a new AVR main.c file to it.  It should similar to this:

New Main ADC Project​​​​​

Back to Top


Initialize the ADC

Create a function called ADC_init() and call it from main.c.   In this function we will configure the registers introduced in Step 1.

The ADMUX Register is shown along with a table to help us set the appropriate bits to set AVCC as our voltage reference

ADMUX Register​​​​​

We will set AVCC equal to VCC so we need to set the bit for REFS0 in our ADC_init() function.  We will also choose ADC1 as our input channel.  The bit to set is indicated in the table as shown:  T

ADC Channel 1 Selection Table​​​

Here is the updated code:

ADC_init(void)
{
   ADMUX |= (1 << REFS0) | (1 << MUX0);   //AVCC equal to VCC and ADC1 input
}

The datasheet tells us that the ADC1 channel is on Pin 24 (PC1).  This is the pin we will connect to the wiper of the potentiometer when we start testing the ADC.

ATmega328PB Pin Diagram​​

Now we need to set some bits in the ADC Status register (ADCSRA).  Most of the bits will be set in this register

ADSRA Register​​

ADE: Enables the ADC
ADSC: We will be in Free Running mode so setting this bit will start the conversions
ADATE: Enables Auto Triggering of the ADC
ADIE:  Enables the Conversion Complete Interrupt
ADPSn: Recall that we are running a CPU frequency of 16MHz.  We will use the 128 bit prescaler to set the ADC frequency at 125kHz

Since we have enabled interrupts in this register, we need to add the interrupt header toward the beginning of our program and add sei() to our ADC_init function.

#include <avr/interrupt.h>
ADC_init(void)
{
   ADMUX |= (1 << REFS0) | (1 << MUX0);   //AVCC equal to VCC and ADC1 input
  ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
   sei();
}

Here is the full code for the lesson:

#include <avr/io.h>
#include
<avr/interrupt.h>

#define LED_ON  PORTB |= (1<<PORTB5)
#define LED_OFF PORTB &= ~(1<<PORTB5)
#define LED_TOGGLE  PINB |= (1<<PINB5)

ISR(ADC_vect)
{
    LED_TOGGLE;
}

void ADC_init(void)
{
   ADMUX |= (1 << REFS0) | (1 << MUX0);   //AVCC equal to VCC and ADC1 input
  ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
   sei();
}


int main(void) {
   
    ADC_init();
   
   while (1) {
    }
}

Back to Top


Wire up the Potentiometer

Connect a potentiometer as shown.  The wiper should go to PC1 and the remaining two connections to VCC and GND.

ADC Wiring Diagram​​

Back to Top


Program the Device for Debugging

Press the Debug Project button at the top of the MPLAB X IDE GUI.

Now, place a breakpoint at the LED_TOGGLE macro inside the ISR. 

If working properly, you should see the LED toggle each time PLAY is pressed at the top of the MPLAB X IDE GUI.

Back to Top


Add a Watch Variable

Add the ADC special function register to the Watch window.  When an ADC conversion is complete, the result is found in this 16-bit register.

ADC Register

Try moving the potentiometer all the way to clockwise and selecting PLAY.  Now, turn it all the way to the other extreme and run it again.  You should have seen the ADC values swing from 0x03FF to 0x0000.

Back to Top