Thursday, May 24, 2018

Analog to Digital Converter (ADC) library for AVR microcontrollers

This library provides a quick and easy way to set up an ADC on AVR microcontrollers and retrieve the values in 8-bit or 10-bit format in an interrupt driven fashion.

Nowadays even the cheapest microcontroller has a build-in ADC (Analog to Digital Converter). An ADC converts analog signals into digital signals and can be used in a wide range of applications like recording a signal from a microphone into a digital format, reading light sensors like an LDR (light dependent resistor), measuring current consumption, reading temperature or humidity sensors, etc. All these requires voltage measurements that an ADC can do.




On ATmega328 there are 8 ADC pins (channels) that can be connected to 8 different analog sources, but only one at a time can be measured. This pins ADC0 to ADC7 can also be used as digital pins. No resistor is required when taking voltage measurements. In case resistors are used, the datasheet recommends that the impedance should not exceed 10k. Care must be taken that the voltage to this pins must not exceed VCC. If the microcontroller is powered from 5V, don't put more than 5V on the pins. In case that higher voltages are to be measured resistor dividers should be used.

ADC power and voltage reference pins

The ADC has it's own pins for power supply named AVCC and GND. The AVCC pin is recommended to be connected to VCC through an LC filter for noise suppression. 

The AREF pin can be used in two ways and both require a 100n decoupling capacitor to ground:

  1. By default the ADC register is configured to use an external reference voltage from this pin. If you apply a voltage to this pin you must avoid setting the ADC to use an internal reference as this will cause a short circuit between them.
  2. The second method is to use one of the internal voltage references such as VCC, 2.56V and 1.1V. Not all avr microcontrollers have all of them. Consult the datasheet to see which ones are available for your device.

AVR ADC power connections with LC filter and analog ground plane
ADC power connections with LC filter and analog ground plane


Important thing to know when using ATmega324PB ADC and the ADC values are always at maximum value or 0x03FF

By default the AREF pin on ATmega324PB is disabled and only internal voltage references can be used. To enable the AREF pin, the GPIOEN pin in the ADCSRB register must be set to 0 because the default value is 1. Many people including myself have spent hours trying to figure out why the ADC is not working and here's why.

Here is the datasheet for ATmega324PB revision DS40001908A


and here is the datasheet with revision DS40001892A

ATmega324PB datasheet revision DS40001892A ADCSRB ADC register

The GPIOEN bit is missing in the newer revision. The library will set this bit low if using ATmega324PB and external reference voltage.

ADC values

Most AVR microcontrollers have 10 bit ADC resolution but 8 bit resolution can be used also. Suppose that the voltage reference is set to VCC 5V and you measure a potentiometer with 10 bit ADC resolution. The minimum value returned by the ADC will be 0 - meaning ground - and the maximum value will be 2^10-1 = 1023 (meaning 5V). The returned value will be between 0 and 1023. This value can be converted to voltage using the following formula, but most of the time this is not needed.

V = ADCvalue * (Vref / ADCresolution)

To find out the minimum voltage that can be measured depending on the ADC resolution, divide voltage reference by the maximum ADC value. For 10 bit resolution and 5V Vref

Vref / 2^10
 5 / 1024 = 0.0048V (4.8mV)

ADC Library for AVR

This library uses the ADC interrupt, so the CPU doesn't have to wait for ADC conversion. Defaults to 10 bit resolution and external reference voltage.

Usage

Download the library and place the file inside the folder with the main.c file then include it

#include "adc.h"

Functions

adcStartConversion(pin)

Start the ADC conversion and enables the ADC interrupt. Takes the ADC pin number as an argument. For example if you want to read the pin ADC4, pass 4 to the function. When the ADC conversion is completed, the ADC_COMPLETED flag will be set and the ADC value can be accessed using the adcReadValue() function. When this function is executed again the ADC_COMPLETED flag will be cleared.

In this function the ADC is set up, but only the first time it is executed.

On devices that supports this feature, the digital mode for the selected pin is disabled in order to save power.

 

adcReadValue()

After the conversion is complete, the ADC value is returned using this function.

To know when the ADC conversion is complete, check the ADC_COMPLETED flag. If 1, the ADC conversion is done, 0 otherwise.

 

adcDisable()

Disable the ADC to save power.

 

adcEnable()

Re-enable the ADC.


adcResolution(bit_resolution)

Change the ADC resolution. The bit_resolution argument can be 8 or 10. Default 10.

 

adcReference(reference_voltage)

Change the ADC reference voltage. Default Vref (external reference voltage).

reference_voltage can be one of the following flags:

VREF_VCC - AVCC with external capacitor at AREF pin
VREF_INTERNAL_1V1 - Internal 1.1V Voltage Reference with external capacitor at AREF pin
VREF_INTERNAL_2V56 - Internal 2.56V Voltage Reference with external capacitor at AREF pin (only if supported by the hardware)
VREF_EXTERNAL - The voltage connected to AREF pin

Code example:

#include <adc.h>

int main(void){
    uint16_t adc_val = 0;
	
    // 10-bit ADC resolution
    adcResolution(10);
	
    // Use the external voltage reference connected to the AREF pin
    adcReference(VREF_EXTERNAL);
	
    while(1){
	// After ADC conversion is complete the ADC ISR will set this flag to 1
	if(ADC_COMPLETED){
	    // Read the ADC value
	    adc_val = adcReadValue();
			
	    // Start a new conversion using ADC0 pin
	    // This will also set the ADC_COMPLETED to 0
	    adcStartConversion(0);
	}
    }
}

 

Download

v1.4
adc.h