Created by Scott Schmit, last modified on Dec 03, 2013
The Analog to Digital Converter (ADC) is one of the most commonly used peripherals in embedded designs. The purpose of this page is to demonstrate basic operation of the ADC in Atmel’s Xmega family of microcontrollers. For this project, the ATxmega128B1 was used. The Xmega128B1 Xplained board was used as the development platform. This project provides example code at the register level to get the user familiar with the various ADC registers and how to configure them based on the application needs. This demo utilizes the LCD included with the Xmega128B1 Xplained board to display the ADC results. Refer to this Basic LCD Example for Xmega for introduction to the LCD controller.
- XMEGA-B1 Xplained Board - Evaluation board for the ATxmega128B1 microcontroller.
- Atmel Debugger/Programmer - The JTAGICE3 was used for this demo, but any Atmel programmer that supports PDI programming can be used.
- USB mini-B Cable - Used to power the board (not included with Xplained Board). Refer to the XMEGA-B1 Xplained Hardware User Guide for alternative power-up options.
- Atmel Studio 6.1 - Free firmware development program from Atmel.
- XMEGA-B_Manual.pdf (5.9 MB) - Provides summary of features along with all register descriptions.
- ATxmega128B1_Complete.pdf (5.4 MB) - Provides summary of features along with pin assignments and electrical characteristics.
- XMEGA_B1_Xplained_Hardware_User_Guide.pdf (809.8 KB) - Provides a summary of on-board hardware features.
- xmega-B1 Xplained Rev4_Design_Documentation.pdf (5.6 MB) - Contains schematic and board layout.
- AVR1300_Using Xmega_ADC.pdf (487.9 KB) - App Note that provides background on Xmega ADC.
- AVR1324_Xmega_ADC_Selection_Guide.pdf (335.3 KB) - App Note that characterizes ADC differences across Xmega devices.
- AVR120_Calib_AVR_ADC.pdf (121.9 KB) - App Note that covers manual calibration of ADC on AVR devices.
Xmega Family Overview
The ATxmega device family is the highest performance 8-bit AVR microcontroller family from Atmel. They deliver excellent power consumption characteristics for 8-bit microcontrollers while integrating features such as 12-bit ADC, USB device connectivity, AES and DES crypto modules, DMA controller and Event System, CRC generator, along with multiple serial communication ports. The family is made up of the A, B, C, and D Series. The D Series is the entry level Xmega series with no USB connectivity or integrated LCD controller. The C Series adds USB Full-speed Device connectivity on top of the D Series functionality. The B Series adds an integrated LCD controller, capable of up to 4x40 segments, on top of the C Series functionality. The A Series adds AES/DES crypto engine on top of C Series functionality but has no integrated LCD controller. The Xmega chip-set is available in multiple different package types such as QFN, QFP, and BGA with available program memory ranging from 16 to 384 KB. The reader is strongly advised to read the datasheets corresponding to the microcontroller being used to determine which specific features are available, along with electrical characteristics and pin assignments. The images on this page and most of the information was found in the documents listed below. This page focuses on the ATxmega128B1 microcontroller loaded on the Xmega128B1 Xplained kit.
The Xmega presents a 12-bit ADC capable of up to 300 thousand samples per second. The designer can use the ADC in single-ended or differential mode. If differential mode is used, a built-in gain stage can be implemented to amplify small signals. The ADC can be configured to continuously start conversions, manually start a single conversion, or use various triggers to start a conversion. The Xmega contains a temperature sensor that can be connected as an input to the ADC internally. The Xmega DMA can be used to efficiently transfer ADC results. The event system can also be implemented to achieve accurate trigger timing for ADC conversions.
Figure 1 illustrates the available connection options for differential measurement. The default gain value is 1, but can be changed from 0.5~64. From Fig. 1, the reader can see that gain could be applied to a single-ended input by using differential mode with a user-specified gain value, and connecting the negative terminal of the ADC to GND (internal or external GND). However, when in Differential mode, the ADC must be in signed mode, which decreases the resolution from 12-bit to 11-bit (MSB used for sign bit).
Single Ended Measurement
When operating in Single-Ended Mode, the ADC can be used in signed or unsigned mode. Therefore, all 12-bits of resolution can be used when in Single-Ended mode. Figure 2 illustrates the available connection options for Single-Ended measurement in signed mode.
When operating in unsigned Single-Ended mode, the negative input of the ADC gets internally connected to a voltage level equal to approximately halfway between GND and VREF minus a fixed offset. This offset is equal to approximately 5% of the selected reference voltage. The offset allows the user to detect zero crossing in unsigned mode. Figure 3 illustrates the available connection options for Single-Ended measurement in unsigned mode.
Voltage Reference Selection
The following sources can be used as the reference voltage for the ADC.
- Internal 1.00V generated from bandgap
- Internal Vcc/1.6
- Internal Vcc/2
- External Voltage applied to AREF pin for PORTA
- External Voltage applied to AREF pin for PORTB
Note: The max reference voltage that can be used is AVcc - 0.6V. This means Vcc cannot be used as the reference voltage for the ADC.
This section describes some typical considerations a designer must account for in every Xmega design, along with some considerations for ADC-specific Xmega applications. Each sub-section includes general considerations, along with some register configuration examples. The reader is encouraged to have the Xmega-B manual available for easy reference. All example code is written in C-Programming language. If global definitions are used in the examples, the 8-bit hex value is included in comments for easy reference.
System Clock Options
The System Clock source is selected using the Clock Control Register (CLK.CTRL). The System Clock can be sourced from an internal 2MHz RC oscillator, an internal 32MHz RC oscillator, an internal 32.768kHz oscillator, an external clock source, or the PLL. Additionally, if the PLL is selected as the System Clock source, the PLL Clock source must be specified, along with a division and multiplication factor. This is done using the Oscillator PLL Control Register (OSC.PLLCTRL). The PLL can use the internal 2MHz RC oscillator, the internal 32MHz RC oscillator, or an external clock as its source. The PLL is then enabled using the Oscillator Control Register (OSC.CTRL). It is up to the designer to select the proper system clock source for a specific application while taking into account necessary speed, accuracy, acceptable power consumption, temperature effects, board space, and cost. For this example, a 16MHz system clock was implemented using the PLL. The internal 2MHz RC oscillator was selected as the PLL source. The following piece of code demonstrates this configuration.
OSC.PLLCTRL = OSC_PLLSRC_RC2M_gc | OSC_PLLFAC3_bm; // 0x08 - select 2MHz RC as source, multiply by 8 OSC.CTRL |= OSC_PLLEN_bm; // 0x10 - enable PLL while (0 == (OSC.STATUS & OSC_PLLRDY_bm)); // wait for PLL to stabilize CLK.CTRL = CLK_SCLKSEL2_bm; // 0x04 - select PLL as System Clock source
- The clock source and multiplication factor must be selected in PLLCTRL before enabling the PLL.
- When selecting a source for the PLL, the designer must also specify a non-zero multiplication factor. The default multiplication factor is zero, so if a new multiplication factor is not specified, the PLL output will be dead.
The System Clock is divided into 4 different peripheral clocks: Clk_per4, Clk_per2, Clk_per, and Clk_cpu, which are used to clock various different peripherals of the microcontroller. The rate of these clocks are set using the Clock Prescaler Control Register (CLK.PSCTRL). The ADC module is clocked from Clk_per. For this example, Clk_per was set to run at 8Mhz. The following code demonstrates this configuration.
CLK.PSCTRL = CLK_PSADIV0_bm; // this sets Clk_per4 = Clk_sys/2 => 16MHz/2 = 8MHz // leaving PSBCDIV[1:0] with default value of '00' sets Clk_per2, Clk_per, and Clk_cpu equal to Clk_per4 (8MHz)
The ADC Prescaler Register (ADCx.PRESCALER) is used to divide the Clk_per into the actual ADC clock. According to AVR1300, the ADC clock should run in the range 100kHz ~ 1.4MHz. ADCB was used for this example and the ADC clock was set to 125kHz. In order to achieve 125kHz, the ADC Prescaler must be setup to divide Clk_per by 64 (since Clk_per was previously set to 8MHz). The following code demonstrates this configuration.
// Set ADC clock to 125kHz: Clk_per/64 => 8MHz/64 = 125kHz ADCB.PRESCALER = ADC_PRESCALER2_bm; // 0x04
Keep in mind, for this example, the on-board LCD was used to display ADC conversion results. The Xmega-B LCD controller uses the RTC as the LCD clock source. Therefore, if the user wants to use the LCD, they must also configure and enable the RTC. See the Basic LCD Example for Xmega page for clock configuration details.
Xmega devices include a Programmable Multilevel Interrupt Controller which allows the designer to assign a priority level to individual interrupts. It also allows round-robin or vector address based interrupt execution. The designer should customize interrupt execution priority based on the needs of the specific application.
The Xmega supports two different ADC interrupt modes - Threshold Compare Interrupt mode and Conversion Complete Interrupt mode. With Threshold Compare mode, the user can specify a threshold value using the ADC Compare Registers (ADCx.CMPH and ADCx.CMPL). The user can then specify whether to trigger an interrupt based on ADC result above or below the threshold value. With Conversion Complete mode an interrupt is triggered when an ADC conversion has completed. The type of interrupt mode is not a global ADC setting. It can be different for individual virtual channels.
The type of interrupt mode is selected using the ADC Channel Interrupt Control Register (ADCx.CHn.INTCTRL). The ATxmega128B1 only has one virtual channel for the ADC. Therefore, CH0 was used in this example. This register is also used to select the priority of the ADC interrupt. For this example, Conversion Complete mode was enabled and was assigned high priority. The following example demonstrates this configuration.
// Enable Conversion Complete Interrupt with high priority ADCB.CH0.INTCTRL = ADC_CH_INTLVL1_bm | ADC_CH_INTLVL0_bm; // 0x03
Once an interrupt mode is specified, there are two separate registers that can be used to indicate an interrupt event has occured. The ADC Interrupt Flag Register (ADCx.INTFLAGS) keeps track of the individual virtual ADC channels. If an interrupt event has occured, the corresponding channel bit will be set in this register. Alternatively, the ADC Channel Interrupt Flags Register (ADCx.CHn.INTFLAGS) can be used to indicate an interrupt event has occured. This register is different because it keeps track of a single virtual channel as opposed to all of the virtual channels. These Interrupt Flag registers will indicate an interrupt event has occured regardless of which interrupt mode is being implemented. Generally, it is considered good design practice to explicitly clear these interrupt flags before enabling the ADC. This is done by writing a ‘1’ to the corresponding bits as shown in the following example.
// Ensure the conversion complete flag is cleared ADCB.INTFLAGS = ADC_CH0IF_bm; // 0x01
These interrupt flag bits can also be used in a polling scheme instead of an interrupt driven application. However, if polling is implemented, the user must manually clear the corresponding flag bits. If interrupt mode is used, the flag bits will clear automatically when the interrupt vector is executed. In general, interrupt driven applications are preferred to polling schemes to allow the CPU to perform other tasks instead of waiting on a single signal before proceeding. However, polling remains useful for preventing the application from proceeding before a needed signal has been received. For example, waiting for the PLL to stabilize before selecting it as the System Clock source is a necessary polling implementation.
Selecting the Mode of Measurement
By default, the ADC peripheral is enabled in the Power Reduction Register. However, if the ADC peripheral was disabled (ASF disables all peripherals initially in all of its examples and re-enables them as needed to conserve power), it must be re-enabled before writing to any of the ADC registers. The following code demonstrates how to enable the ADC.
ADCB.CTRLA = ADC_ENABLE_bm; // 0x01
The ADC Control B Register (ADCx.CTRLB) is used to specify whether the ADC is in signed or unsigned mode. With this register, the user can also specify whether a single conversion is to be performed, or if the ADC should continuously perform conversions. The user can also select whether the digital result is 12-bits or 8-bits. The ADC result register is 16 bits wide. Therefore, if 12-bit result is selected, the user can specify whether the 12-bit result is left or right justified within the 16-bit result register. For this example, the ADC was set to perform single conversions and run in Signed mode. The ADC was configured to display a 12-bit, right justified result. The following code demonstrates this configuration.
ADCB.CTRLB = ADC_CONMODE_bm; // 0x10
Note: Since we’re using signed mode, the resolution of the ADC is effectively 11-bits. The msb is used for the sign bit and will only equal ‘1’ if displaying a negative number.
Port I/O Configuration
In order for the ADC to be connected to the outside world, the user must ensure the proper pins are configured as inputs and connect them internally to the ADC. For this example, the output voltage of a potentiometer was used as the analog source for the ADC. On the Xmega-B1 Xplained board, the potentiometer is connected to PORTB, Pin 1 on the Xmega. Therefore, PB1 needs to be configured as an input as shown in the following bit of code.
// Configure PB1 as input; write '0' to pin position in Direction register PORTB.DIR &= ~0x02;
Since we are using Signed mode, the ADC can be used either in Differential or Single-Ended mode. The ADC Channel Control Register (ADCx.CHn.CTRL) is used to specify Single-Ended vs. Differential mode. If Differential mode is selected, the gain of the differential inputs can also be selected using this register. For Single-Ended mode, the gain is 1. For this example, the ADC was placed in Single-Ended mode. This is demonstrated in the following example code.
ADCB.CH0.CTRL = ADC_CH_INPUTMODE0_bm; // 0x01
The ADC Channel MUX Control Register (ADCx.CHn.MUXCTRL) is used to specify what is connected to the positive and negative terminals of the ADC comparator. For the positive terminal, the user can specify whether an external pin or an internal voltage source is used as the input to the ADC. For the negative terminal, the user can specify whether an external pin, internal GND, or PAD GND is connected. The negative terminal only applies when using Differential mode. Since we are in Single-Ended, Signed mode of operation, the negative terminal of the comparator is automatically connected to ground, internally. For this example, PB1 was connected to the positive terminal of the comparator, and the negative terminal was ignored, since it’s already connected to GND. The following code demonstrates this configuration.
// Connect PB1 to positive terminal ADCB.CH0.MUXCTRL = ADC_CH_MUXINT0_bm; // 0x08
The Xmega ADC module is calibrated at production time and the calibration value is stored in internal EEPROM. According to AVR1300, this calibration value compensates for mismatch between individual steps of the ADC pipeline and improves ADC linearity. AVR1300 also states that this calibration value should always be loaded into the proper ADC calibration registers before enabling the ADC. In order to access this information from internal EEPROM, the user must use the Non-Volatile Memory (NVM) Controller. Section 30 of the Xmega-B User Manual gives details on the NVM controller and the available commands. The reader is encouraged to read through Section 30 and refer to the ReadCalibrationByte() function within the “My_ADC.c” file in the example project below for example usage of the NVM.
Note: In order to read production calibration values from EEPROM, the following two header files must be included in the project:
#include <avr/pgmspace.h> #include <stddef.h>
It should be noted, the production calibration value is meant to compensate for non-linearities in the ADC. DC offsets and gain error can be compensated for using manual calibration with the application code. This is out of the scope of this project, however. Refer to AVR120 for documentation on this topic. For example code on manual calibration, refer to the Basic Voltage meter demonstration for XMEGA-B1 Xplained board from ASF library.
Additional ADC Configuration
The ADC Reference Control Register (ADCx.REFCTRL) is used to specify the reference voltage of the ADC. This will define the maximum acceptable analog value that can be correctly converted to a digital value. For this example, the reference voltage was set to Vcc/1.6. This configuration is demonstrated in the following example code.
ADCB.REFCTRL = ADC_REFSEL0_bm; // 0x10
For this example, Vcc of the Xmega was 3.3V. Therefore, the reference voltage of the ADC becomes approximately 2.06V (3.3 / 1.6). Since we are using effectively 11-bits of resolution (12-bit& with msb as sign bit), this gives us 2^11 possible digital codes (2048 different values). In order to calculate the value of the LSB, we simply take our analog input range (2.06 - GND = 2.06) and divide by the number of possible digital values (2.06 / 2048 = approx 0.001). This means that every digital bit of the result represents approximately 1 mV. For example, if the ADC result is 0xB2, we know the analog input was approximately 178mV (convert 0xB2 from hex to decimal). As an extreme example, for a digital result of 0xFE, we know the analog input was approximately 2046mV. Again, this is an approximation, and the error compounds as you approach the upper analog limit. However, in many applications, like this basic example, it is close enough. For higher accuracy, the application code would need to perform some sort of manipulation to the result.
The Event System can be used to trigger ADC conversions using the ADC Event Control Register (ADCx.EVCTRL). This allows for very specific timing of conversions. For this example, Event triggering was disabled. The following code shows how to ensure Event triggering is disabled.
ADCB.EVCTRL = 0x00;
The ADC has now been configured. The last step is to actually trigger a conversion. For this example, the ADC was configured to perform a single conversion upon being manually triggered. There are two different ways to start a conversion after the ADC is configured. The user can use the ADC Control A Register (ADCx.CTRLA) to trigger a conversion on any of the virtual channels, or the user can use the ADC Channel Control Register (ADCx.CHn.CTRL) for the specific channel being used. The following code demonstrates both methods of starting a conversion.
// Start a conversion using ADC Control A Register ADCB.CTRLA |= ADC_CH0START_bm; // 0x04 // Start a conversion using ADC Channel Control Register ADCB.CH0.CTRL |= 0x80;
Reducing Power Consumption
The ADC Control Register B (ADCx.CTRLB) can be used to limit the current consumption of the ADC by reducing the max sample rate. The designer can reduce the max sample rate to 225kSPS, 150kSPS, or 75kSPS. By default, no current limit is applied to the ADC. This example did not implement current limiting, but the following code demonstrates how the user could reduce the current consumption to 75kSPS. If implemented, this should be done early in the ADC configuration process when selecting the mode of measurement for the ADC.
ADCB.CTRLB |= ADC_CURRENTLIMITS_HIGH_gc; // 0x60
Downloadable Example Project
The following attachment provides an easy to read project with documentation that demonstrates basic functionality of the Xmega ADC. It builds on this Basic LCD Example for Xmega-B devices. Download the following zip file and open the the ADC Example Project for Xmega-B1 Xplained board.
- ADC_Ex_XMEGA-B1_Xplained.zip (59.6 KB)
The ADC is one of the most commonly used peripherals in embedded design. The purpose of this page was to give a basic ADC example project and explain the entire process of using an Xmega ADC. The reader is encouraged to read through all of the documentation that is referenced on this page. I hope you enjoy working with the Xmega ADC!
For questions or feedback about information on this or any other page, please go to the TechForum: TechForum