# The analog digital converter (ADC) on the ATMega328p and ATMega2560

15 Jul 2021 - tsp
Last update 15 Jul 2021

## Analog digital converter?

First of - what is an analog digital converter (ADC)? An ADC allows one to convert an analog voltage into a digital value. Basically it’s a voltage measurement (in relation to a reference voltage) and one of the easier to use peripherals on the ATMega AVR series.

The ATMega328p and the ATMega2560 offers a single analog digital converter that is connected to six different input pins (16 on the Mega2650) via a programmable multiplexer - so one can measure on multiple input pins, but only one at a time. The maximum resolution is 10 bits with about $\pm 2$ bits absolute accuracy. For 5V reference voltage that would be a resolution of about $\pm 20 mV$. The ADC can run either in single conversion mode in which it’s performing one measurement and then waiting again for the next trigger or in free running mode in which it performs one measurement after each other.

If one really requires high precision measurements one should of course go the route of an external ADC - possibly also using some external bandgap voltage reference. The cheapest (and slowest) method is to use some kind of Delta-Sigma ADC chip on the I2C port.

The reference voltage for the ATMega can be either:

• The AVCC pin. This is the separate VCC pin for the ADC that is allowed to differ only by $\pm 0.3V$ from VCC. This is used on the most simple setups and also on most of the Arduino boards - in the latter case the reference voltage is just the 5V power rail which of course is not that reliable when using some cheap 5V power adapter or USB port of the computer. But it’s the simplest reference to use.
• An additional AREF pin that provides reference voltage. This has to stay within about $1V$ below VCC. This is most useful in case one also wants to use 5V reference voltage but wants to implement some special stabilization like a bandgap reference (for example an LT1029 - these are basically devices that work like Zener diodes that are connected to shunt and provide a stable and more precise voltage reference with higher temperature stability.
• An internal $1.1V$ voltage reference that’s implemented inside the microcontroller via the forward voltage of an diode. Since the ADC input voltage has to be in the range from GND to the reference voltage this places a tight constraint on the input voltage of the ADC - but on the other hand also increases the resolution. The $1.1V$ bandgap reference would allow for a theoretical resolution of $1.075 mV$ which would be enough for simple direct measurements using thermocouples when one requires to know only tens (not tenth) of degrees.

Usually one requires an capacitor connected between AREF and GND - one should really read the datasheet or at least skim the schematic provided. In most of my setups I use a $0.1 \mu F$ capacitor towards ground and connect AVCC directly to the $5V$ VCC line. Note that AREF is connected via a switch to the internal reference pin. If one’s using an external bandgap reference one cannot use any other mode since the internal sources would be shorted through one’s own reference. So if you plan to use the bandgap reference do not connect the AREF pin.

On the ATMega2560 one can not only use single channels but use an differential ADC between a variety of pins with variable gains.

There are two additional inputs to the multiplexer - an on die temperature sensor as well as the bandgap reference. The input channel is selected via the ADMUX register. In case one’s using the internal temperature sensor one also has to use the internal bandgap reference as reference voltage and thus has to leave AREF open. Note the internal temperature sensor is not particularly useful - its usually working around $\pm 1^\circ C$ - but it’s absolute value is only accurate to about $\pm10^\circ C$ due to manufacturing differences.

Generally the ADC is enabled by ADEN in ADCSRA. The result of the last successful conversion is stored in two registers - ADCH and ADCL. One always has to read the low register first, then the high one. After reading the low register the hardware automatically blocks the ADCs access to the high register to allow an atomic read in which case the result of the conversion would be lost. This is usually not a problem when just storing data inside an ISR but might be a problem in free running mode when doing data storage from within a loop that’s interrupted by other operations.

To start a single conversion:

• Set PRADC to zero to disable any power saving features if this is the first time you’re initializing the ADC.
• Setup ADMUX, ADCSRB and ADCSRA to match your input selection, trigger selection, prescaler configuration, etc.
• Write logical one to the ADC start conversion bit in ADSC. This bit will stay high as long as the conversion is in progress. As soon as the bit goes low the result can be read in case one does polling access. If one has configured interrupt mode the ADC_vect will be triggered as soon as data is available.
• Read data from ADC register pair (as usual when not using any macros of your compiler or writing assembly code read ADCL first since this triggers an interlock to prevent modification of ADCH till if has been read)

Note that any changes to the selected channel will only take effect after the currently running conversion has finished. This allows one to swap the selected channel on the multiplexer immediately after starting the conversion to provide uninterrupted rolling measurements.

Additionally one can use a variety of sources to trigger the conversion - this is controlled by the ADTS bits in ADCSRB. This is particularly useful in case one uses the ADIF - the interrupt flag of the ADC - as a trigger source. In this mode the ADC automatically starts a new conversion whenever it’s own interrupt occurs. This means it would perform an endless loop of performing conversions. The first conversion has to be started by writing a logical one to the ADSC bit in ADCSRA register.

### How to calculate voltages from ADC values

This is pretty simple - the ADC always samples in relation to the voltage range from $0$ to the selected reference voltage $V_{ref}$. This range is divided into the 10 bits of the ADC. Since $2^{10} = 1024$ we have 1024 equally spaced divisions - so the ADC counts $n_{ADC}$ can simply be converted by a simple multiplication:

Note that the lower reference point is always the ground potential at $0 V$.

### Clock source

The ADC is clocked from it’s own prescaler. 10 bit resolution is possible for up to $200 kHz$. Higher frequencies are possible when one is satisfied with a lower resolution. One should never set a clock frequency below $50 kHz$, this might also lead to unstable operation.

• The first conversion takes 25 clock cycles (ADC cycles) - with $200 kHz$ this is a sampling frequency of $8 kHz$
• Every normal conversion takes 13 ADC cycles ($15 kHz$)
• Auto triggered conversion sample after 2 cycles, the conversion time is 13.5 cycles.

Results are stored 1.5 clock cycles after the start of a normal conversion or around 13.5 cycles during the initial one. The internal bandgap voltage reference that can be used to calibrate for the external reference voltage might also be used - but it might take a few measurements to stabilize after multiplexer configuration.

## Registers

### ATMega328p

The ADMUX register allows one to select the reference voltage and left / right adjusting of the data register as well as most of the input sources.

Bit Name Content
7 REFS1 Voltage reference selection
6 REFS0 Voltage reference selection
4 - Reserved
3:0 MUX3:0 Input multiplexer

The selectable voltage references are:

REFS1 REFS0 Selected reference
0 0 AREF pin, internal reference voltage turned off
0 1 AVCC with external capacitor on AREF
1 0 -
1 1 Internal 2.56 bandgap reference with external capacitor on AREF

The ADLAR bit can left adjust (1) or right adjust (0) the measurement result.

The MUX selection works together with the sixth bit in ADCSRB:

Bit Name Content
6 ADSC Start conversion. One can start a conversion / the first conversion by writing a one.
5 ADATE ADC auto triggering enable. If set the ADC will trigger on the specified trigger source.
4 ADIF Interrupt flag. If one doesn’t use the interrupt vector one has to clear the interrupt flag by writing a logical 1.
3 ADIE Interrupt enable. If this bit is set the ISR will be executed when the ADC sets ADIF.

The prescaler selection configures the ADC clock. This prescaler is directly applied to the system clock - the generated ADC clock should lie somewhere between $50 kHz$ and $200 kHz$. If one requires less precision by discarding lower bits one can select up to $1000 kHz$. If one has a system clock of for example $16 MHz$ and wants to run the ADC at around $125 kHz$ one can calculate the optimal value:

[ \frac{16 * 10^8}{125 * 10^3} = 128 ]

As one can see one should set all bits ADPS2:0 to one. This would result in a conversion speed of $9.6 kHz$ (1 conversion taking 13 ADC cycles - the first one takes 25 ADC cycles).

000 2
001 2
010 4
011 8
100 16
101 32
110 64
111 128

This register contains trigger selection and the comparator multiplexer enable bit that is set to zero for ADC usage:

Bit Content
7 -
6 ACME - Multiplexer enable for comparator output. Set to zero for ADC usage
5 -
4 -
3 -

The trigger selection can choose between one of the following sources:

000 Free running mode
001 Analog comparator
010 External interrupt request 0
011 Timer/counter 0 compare/match A
100 Timer/counter 0 overflow
101 Timer/counter 1 compare/match B
110 Timer/counter 1 overflow
111 Timer/counter 1 capture event

#### DIDR0 - Digital input disable register 0

This register allows one to disable the digital input logic on the pins used by the ADC. This is especially useful on the ATMega328p since the ADC pins are shared with other digital pins.

Bit Content
7 -
6 -
5 ADC5D - Disable digital I/O on ADC5 if set to 1
4 ADC4D - Disable digital I/O on ADC4 if set to 1
3 ADC3D - Disable digital I/O on ADC3 if set to 1
2 ADC2D - Disable digital I/O on ADC2 if set to 1
1 ADC1D - Disable digital I/O on ADC1 if set to 1
0 ADC0D - Disable digital I/O on ADC0 if set to 1

### ATMega2560

The ADMUX register allows one to select the reference voltage and left / right adjusting of the data register as well as most of the input sources.

Bit Name Content
7 REFS1 Voltage reference selection
6 REFS0 Voltage reference selection
4:0 MUX4:0 Input multiplexer (one more bit in ADCSRB)

The selectable voltage references are:

REFS1 REFS0 Selected reference
0 0 AREF pin, internal reference voltage turned off
0 1 AVCC with external capacitor on AREF
1 0 Internal 1.1V bandgap reference with external capacitor on AREF
1 1 Internal 2.56 bandgap reference with external capacitor on AREF

The ADLAR bit can left adjust (1) or right adjust (0) the measurement result.

The MUX selection works together with the sixth bit in ADCSRB:

MUX5:0 Single ended input Positive differential Negative differential Gain
011110 1.1V bandgap
011111 GND

Bit Name Content
7   0
6 ACME Analog Comparator Multiplexer Enable - used for comparator operation
5   0
4   0
3 MUX5 See multiplexer register above

The ACME bit is not used during normal analog digital conversion operation.

The trigger select selects the trigger source for the ADC to start conversions:

000 Free running mode
001 Analog comparator output
010 External interrupt request 0
011 Timer0 Compare Match A
100 Timer0 Overflow
101 Timer1 Compare Match B
110 Timer1 Overflow
111 Timer1 Capture Event

Bit Name Content
7 ADEN ADC enable (1) or disable (0). Running conversions will be terminated when disabling
6 ADSC Start conversion. In single mode writing 1 starts a single conversion, in free running mode starts the first conversion
5 ADATE Auto trigger enable. If set the ADC will automatically start on the selected trigger
4 ADIF Interrupt flag. Set after complete conversion.If ADIE is set will raise an interrupt - else one has to manually clear this flag by writing 0.
3 ADIE Interrupt enable. If set the ADIF will raise an interrupt.
2:0 ADPS2:0 Prescaler selection (see below)

The prescaler selection configures the ADC clock. This prescaler is directly applied to the system clock - the generated ADC clock should lie somewhere between $50 kHz$ and $200 kHz$. If one requires less precision by discarding lower bits one can select up to $1000 kHz$. If one has a system clock of for example $16 MHz$ and wants to run the ADC at around $125 kHz$ one can calculate the optimal value:

[ \frac{16 * 10^8}{125 * 10^3} = 128 ]

As one can see one should set all bits ADPS2:0 to one. This would result in a conversion speed of $9.6 kHz$ (1 conversion taking 13 ADC cycles - the first one takes 25 ADC cycles).

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

#### DIDR0 to DIDR2

These three registers allow one to disable the digital input logic to be disabled for analog input pins. This should be done when using pins only for analog input not requiring any digital functions such as interrupts, etc. There may be situations where one wants to use both - for example for special trigger scenarios, etc.

DIDR0 Bit Content
DIDR1 Bit Content
7 -
6 -
5 -
4 -
3 -
2 -
1 AIND1 - Disable digital input logic on AIN1
0 AIND0 - Disable digital input logic on AIN0
DIDR2 Bit Content

Differential measurements are presented in one complements notation.

## Example and snippets

### Polling manually triggered measurement

When one just wants to synchronously fetch an ADC measurement after manually triggering one can use something like the following snippet:

void adcInit() {
/*
First disable interrupts while modifying the ADC settings.
Though not really necessary in all cases there are some where
it's required
*/
uint8_t oldSreg = SREG;
cli();

/*
Disable power saving features for ADC
*/
PRR0 = PRR0 & ~(0x01);

/*
AVCC reference voltage   01xxxxxx
MUX 0                    xxx00000
right aligned            xx0xxxxx
*/

/*
Free running trigger mode                 xxxxx000
highest mux bit on ATMega2560 set to 0    xxxx0xxx
Disable comparator multiplexer            x0xxxxxx
*/

/*
Do not start conversion now     x0xxxxxx
Disable autotriggering          xx0xxxxx
Clear interrupt flag            xxx1xxxx
Disable interrupt               xxxx0xxx
Prescaler select (/128)         xxxxx111
*/

/*
Restore interrupt flag
*/
SREG = oldSreg;
}

uint16_t adcMeasure() {

while((ADCSRA & 0x40) != 0) {
/* Busy wait till measurement is done ... */
}

}


### Manually triggered, ISR based

A better approach to busy waiting is most of the time the usage of an synchronous interrupt service routine that’s triggered whenever a measurement has finished. This is the ADC_vect vector. To use the interrupt service routine one just has to set the ADIE bit in ADCSRA, the remaining operation stays the same:

void adcInit() {
/*
First disable interrupts while modifying the ADC settings.
Though not really necessary in all cases there are some where
it's required
*/
uint8_t oldSreg = SREG;
cli();

/*
Disable power saving features for ADC
*/
PRR0 = PRR0 & ~(0x01);

/*
AVCC reference voltage   01xxxxxx
MUX 0                    xxx00000
right aligned            xx0xxxxx
*/

/*
Free running trigger mode                 xxxxx000
highest mux bit on ATMega2560 set to 0    xxxx0xxx
Disable comparator multiplexer            x0xxxxxx
*/

/*
Do not start conversion now     x0xxxxxx
Disable autotriggering          xx0xxxxx
Clear interrupt flag            xxx1xxxx
Enable interrupt                xxxx1xxx
Prescaler select (/128)         xxxxx111
*/

/*
Restore interrupt flag
*/
SREG = oldSreg;
}

void adcStartMeasurement() {
}


The ISR will then handle any available data:

uint16_t currentADCValue;

/*
Do whatever (short) operation you want using ADC
register. In this sample simply store it somewhere.
One might switch channels, trigger another measurement,
push the measurement into some serial port, etc.
*/

}


### Fully automatic triggering, ISR based

The only modification one needs from interrupt based manually triggered mode to fully free running mode is enabling auto triggering by setting ADATE in ADCSRA. Setting this bit starts a conversion whenever the specified trigger source is activated. If one sets free running mode as trigger the ADC instantaneously triggers whenever the ADC is available (i.e. after the previous measurement has finished). In case one switches the input multiplexer after each measurement one has to notice that a change in ADMUX only applies during the startup phase - whenever the ISR is triggered the MUX value already has been latched by the ADC so it will only take effect after the next interrupt.

The following sample code is used by me in an simple electron gun controller for example to sample 8 channels in a round robin fashion:

void adcInit() {
/*
First disable interrupts while modifying the ADC settings.
Though not really necessary in all cases there are some where
it's required
*/
uint8_t oldSreg = SREG;
cli();

/*
Disable power saving features for ADC
*/
PRR0 = PRR0 & ~(0x01);

/*
AVCC reference voltage   01xxxxxx
MUX 0                    xxx00000
right aligned            xx0xxxxx
*/

/*
Free running trigger mode                 xxxxx000
highest mux bit on ATMega2560 set to 0    xxxx0xxx
Disable comparator multiplexer            x0xxxxxx
*/

/*
Do not start conversion now     x0xxxxxx
Enable autotriggering           xx1xxxxx
Clear interrupt flag            xxx1xxxx
Enable interrupt                xxxx1xxx
Prescaler select (/128)         xxxxx111
*/

/*
Restore interrupt flag
*/
SREG = oldSreg;

/*
Start first conversion now. This will latch the current
MUX bits. This is done by setting ADSC.
*/

/*
*/
}

/*
Note that one has to provide a interrupt safe method to
access these values.
*/

/*
Advance MUX to the next input - this will take
effect on the measurement after the next one.
The logical and with 0x07 provides an easy way to
iterate over the first 8 channels (implements modulo 8)
*/
ADMUX = (oldMux & 0xE0) | (((oldMux & 0x1F) + 1) & 0x07;

/*
Store the value at the (oldMux & 0x1F) - 1 entry.
To prevent having to work with negative numbers
one can simply add 7 and perform again a modulo 8.
*/