You are on page 1of 11

Xmega Development Board

Precision 12bit ADC HowTo


Summary
This document describes how to obtain 12 bits of resolution from the ADC found on the Atmel AVR Xmega family of Microcontrollers using the production signature row values and proper reference selection.

Requirements

Xmega Development board (e.g. EVAL-USB-64, EVAL-01, or your own Xmega board) AVRStudio and AVR-GCC compiler AVRDude (AVR programming Software included with WinAVR) or IAR or AVRStudio with Atmel AVRISP mkii or Atmel JTAGICE mkii Programmer for your board (only USB port required for EVAL-USB-XXX) Updated AVRDude.conf (for ver 5.6, not required for ver 5.8 and later included with WinAVR20100110) DMM with 3+ digits of precision (to verify voltage readings)

Xmega ADC Basics


The Xmega family of microcontrollers contains one (A4 family) or two (A1,A3 family) 12 bit SAR (Successive Approximation Result) ADCs connected to PORTA and PORTB. There are numerous configuration options to match your particular usage including:

Conversion Mode (signed or unsigned result) Input Mode (single ended or differential) Resolution (8 or 12bit) Frequency (set by dividing peripheral clock down to desired frequency)

Reference Voltage Front End Gain Stage (allows amplification of differential signals before conversion)

The ADC on the Xmega can begin a conversion upon many events, including DMA transfer requests, Analog comparator trigger levels, system events like counter overflows or matches and explicit software requests. It can also be configured for "Freerun" in which a new conversion begins after each conversion completes. Once triggered, a single conversion or multiple conversions (scan) will begin and within N clock cycles (propagation delay) the resulting ADC conversion will be stored in two separate 8 bit results registers (CHRESH and CHRESL). Each ADC channel has its own dedicated results registers. ADCA is connected to PORTA and its results will appear in ADCA.CH0RES. In addition to storing the results of each conversion in the appropriate register, a conversion completion can trigger a DMA transfer to move the resulting ADC data to a memory location, or trigger an interrupt to process the conversion results.

Xmega ADC Issues

ADC reference can not exceed VCC-0.6V. Since this part operates from 1.8 - 3.6V this puts a 3.0V limit on any ADC measurement. This also prevents using AVCC as AREF which is a common technique in ATMEGA microcontrollers. Unsigned mode has a small positive offset which needs to be subtracted out and prevents ADC from measuring AREF Calibration values are stored in the NVM Production Signature Row and must be manually read and written to ADC at runtime for best performance Some early revisions of the ATXmega chips (eg ATXMEGA128A1 pre revH) are known to have additional problems. Any chip you order from the bigger distributors (Digikey, Arrow, Avnet, Mouser) will likely no have any issues. The ADC uses a sample and hold circuit on the front end which increases current from your source. For high impedance signal sources you may need a buffer circuit or cap on the analog input to get good results.

Configuring ADC for single, unsigned 12bit conversion


In singled ended mode, ADC measurements are made from a single input pin on PORTA or PORTB with respect to the internal Xmega GND. The result of a conversion is a single 12bit unsigned value.

As a simple example, let's walk through the code required to do a single conversion on an ATXMEGA64A3 using the internal 1V voltage reference. We will use PORTA, Pin 2 as a single ended input using the analog gnd as reference.
PORTA.DIR = 0; ADCA.CTRLA |= 0x1; ADCA.CTRLB = ADC_RESOLUTION_12BIT_gc; ADCA.REFCTRL = ADC_REFSEL_INT1V_gc | 0x02; ADCA.PRESCALER = ADC_PRESCALER_DIV8_gc; ADCA.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc // // // // // // // configure PORTA as input enable adc 12 bit conversion internal 1V bandgap reference peripheral clk/8 (2MHz/16=250kHz) single ended PORTA:2

We can initiate a conversion by setting the appropriate start bit in the CTRL register.
ADCA.CH0.CTRL |= ADC_CH_START_bm; // start conversion on channel 0

To avoid setting up interrupt handlers or getting into the DMA we can just wait for the conversion complete flag to be set.
while(!ADCA.CH0.INTFLAGS);

Then we can read the results from the results register to a local variable. We do not need to explicitly copy the high and low 8bit results; AVR-GCC takes care of the code for us:
Result = ADCA.CH0RES;

Ideally, you could now turn the resulting integer ADC measurement into a voltage as follows:
ResultVolt = (float)Result/4096; // 1V ref, 12bit ADC count

However, we have the small positive offset and calibration settings to apply first to get all 12bits of

accuracy.

ADC Calibration
To improve the accuracy of the ADC results we need to apply the calibration values from the production signature row. AVR-GCC provides a simple set of library functions which can access this section of flash. You need to include the headers and C function in your code as follows:
#include <stddef.h> #include <avr\pgmspace.h> uint8_t ReadCalibrationByte( uint8_t index ) { uint8_t result; /* Load the NVM Command register to read the calibration row. */ NVM_CMD = NVM_CMD_READ_CALIB_ROW_gc; result = pgm_read_byte(index); /* Clean up NVM Command register. */ NVM_CMD = NVM_CMD_NO_OPERATION_gc; return( result ); }

You can load the calibration as follows. It should be done before you enable the ADC.
ADCA.CALL = ReadCalibrationByte( offsetof(NVM_PROD_SIGNATURES_t, ADCACAL0) ); ADCA.CALH = ReadCalibrationByte( offsetof(NVM_PROD_SIGNATURES_t, ADCACAL1) );

Single Ended GND Offset


Next, we need to calculate the small positive offset in the ADC. The only way to do this is to connect GND to one of the analog input pins. I will use PORTA:3 to accomplish this and solder a wire from this pin to ground. On the Atxmega64A3 chip PORTA:3 is located on pin 1 and the nearest GND is the ADC GND pin located on pin 60. It makes sense to measure GND at the input to the ADC since it will be as close as you can get to the GND used internally in the ADC for reference.

Now we do a single conversion on the ADC to measure GND.


// setup adc for single ended, unsigned sampling on PORTA:3 to calibrate ADC offset ADCA.CTRLA |= 0x1; // enable adc ADCA.CTRLB = ADC_RESOLUTION_12BIT_gc; // 12 bit conversion ADCA.REFCTRL = ADC_REFSEL_INT1V_gc | 0x02; // internal 1V bandgap reference ADCA.PRESCALER = ADC_PRESCALER_DIV16_gc; // peripheral clk/16 (2MHz/16=125kHz) ADCA.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; // single ended ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN3_gc; // PORTA:3 (GND) // trigger single conversion ADCA.CTRLA |= 0x4; // wait for result while(!ADCA.CH0.INTFLAGS); ADCA.CH0.INTFLAGS=ADC_CH_CHIF_bm; 1) gADC_CH0_ZeroOffset = ADCA.CH0RES; offset // read 12 bit value and save as zero

// wait for conversion complete flag // clear int flags (cleared by writing

Now whenever you do an ADC conversion you need to subtract the global variable gADC_CH0_ZeroOffset from your reading.
Result = ADCA.CH0RES - gADC_CH0_ZeroOffset;

Test 12bit Single ended ADC


Setup: 1) Connect an adjustable 3 pin potentiometer to your XMega evaluation board across pins on PORTA:1 -

PORTA:3 as follows (The photo shows the EVAL-01):

2) Solder or connect PORTA:3 to GND On the EVAL-01 board you will find the AGND is off C4 and the best via is closest to PORTA:3

On the EVAL-USB board you will find the AGND is off an unmarked cap closest to PORTA:3, you can also connect to the GND for the Crystal.

3) Compile xmega-adc-wcal-howto.c for single ended operation 4) Program the example to your development board. 5) Power your board with an external 5VDC power supply (USB power can be quite noisy and should be avoided but may work depending on your setup) 6) Use a DMM to adjust the voltage on PORTA:2 to be under 1V (0.5V is a good rough setting) and record the exact voltage. 7) Read the results of the ADC and note variation from sample to sample. Ideally the voltage divider voltage does not vary, and your ADC reading fluctuates only in the LSB (Least Significant Bit). There are two ways to read the ADC results: JTAG ICE mkii: Connect your Atmel JTAG ICE debugger to your development board, load the example code above into AVRStudio and debug through JTAG. Trigger Breaks and inspect the Result variable using the "watch" feature of AVRStudio. All others: The 12bit ADC value is output on PORTD and PORTE. Use a DMM to measure the voltage on each pin where the MSB is output on PORTE:4 and the LSB is on PORTD:0

Differential Mode
In Differential mode the ADC measures the difference between two input voltages and compares it with

the reference voltage you select.

The ADC conversion results are always signed since V1 > V2 or V2 > V1, so when measuring only positive voltages you lose 1 bit of resolution and effectively have an 11bit ADC. An example configuration for differential acquisition is shown.
ADCA.CTRLA |= 0x1; // enable adc ADCA.CTRLB = 0x10 | ADC_RESOLUTION_12BIT_gc; // 12 bit signed conversion (pos 11bits) ADCA.REFCTRL = ADC_REFSEL_INT1V_gc | 0x02; // internal 1V bandgap reference ADCA.PRESCALER = ADC_PRESCALER_DIV16_gc; // peripheral clk/16 (2MHz/16=125kHz) ADCA.CH0.CTRL = ADC_CH_INPUTMODE_DIFF_gc; // differential ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc | ADC_CH_MUXNEG_PIN3_gc; // PORTA:2 wrt A3 ADCA.CH0.INTCTRL = ADC_CH_INTLVL_HI_gc; // hi level interrupt ADCA.EVCTRL = ADC_EVSEL_0123_gc | ADC_EVACT_CH0_gc; // trigger ch0 conversion on event0

Front End Gain


When measuring particularly small signals it might be useful to amplify the input using the Xmega's builtin gain stage. This circuit allows the analog input signals from PORTA/B to be routed through an amplifier before the ADC. Available gain factors are 1-64x in powers of 2. The front end gain amplifier is only available in differential mode, so resolution is limited to 11bits. Also, when selecting your negative input you must choose from pins 4-7 instead of 0-3.

An example configuration for differential ADC configuration with a front end gain of 4x is shown. Note you need to explicitly set input mode to ADC_CH_INPUTMODE_DIFFWGAIN_gc on ADCA.CH0.CTRL. And in this example we are measuring PORTA:2 with respect to PORTA:4. If you want to try using the potentiometer as in the previous example you need to connect PORTA:4 to PORTA:3.
ADCA.CTRLA |= 0x1; // enable adc ADCA.CTRLB = 0x10 | ADC_RESOLUTION_12BIT_gc; // 12 bit signed conversion (pos 11bits) ADCA.REFCTRL = ADC_REFSEL_INT1V_gc | 0x02; // internal 1V bandgap reference ADCA.PRESCALER = ADC_PRESCALER_DIV16_gc; // peripheral clk/16 (2MHz/16=125kHz) ADCA.CH0.CTRL = ADC_CH_INPUTMODE_DIFFWGAIN_gc | ADC_CH_GAIN_4X_gc; // differential, 4x front end gain ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc | ADC_CH_MUXNEG_PIN4_gc; // PORTA:2 wrt PORTA:4 ADCA.CH0.INTCTRL = ADC_CH_INTLVL_HI_gc; // hi level interrupt ADCA.EVCTRL = ADC_EVSEL_0123_gc | ADC_EVACT_CH0_gc; // trigger ch0 conversion on event0

External Voltage Reference


To improve the input voltage range above to the ADC beyond 1.0V it is necessary to introduce an external voltage reference. The Xmega family is limited by the requirement that Vref < Vcc - 0.6V. For a 3.3V VCC configuration (standard on all Boston Android eval boards) this puts the upper limit on Vref to 2.7V. Just to be safe it recommended to use an external voltage reference no larger then 2.5V. All EVAL-USB boards as of RevC have a landing for an external voltage reference in a SOT-23 package (EVAL-USB-256 has this part installed).

The recommended part is a TI LM4040 or LM4050 precision shunt voltage reference. This part is inexpensive and very stable over temperature and comes in various grades from 0.05% initial accuracy to 2% initial accuracy and stable to typically 100ppm/C. In most applications you can use the voltage reference externally to null out any accuracy errors. The current available from the voltage reference is limited by the serial resistor and the default configuration of the EVAL-USB-256 is Rlim=1k. Iref = (VCC-Vref)/Rlim = (3.3 - 2.5)/1k = .8mA Since this is a shunt resistor, the LM4040 will sink any unused current and when not in use it will consume Iref = (VCC-Vref)/Rlim.

Test 12bit Single Ended ADC with External 2.5V reference


Setup: 1) Solder 3 pin 10k potentiometer on PORTA:0-2 (EVAL-USB-256 Shown)

2) Connect a stable low noise voltage reference to PORTA:0 (EVAL-USB RevC boards have a landing for SOT-23 shunt volt ref) 3) Connect PORTA:2 to board GND.

EVAL-USB RevC with Volt Ref and GND rework 4) Compile xmega-adc-wcal-howto.c for single ended operation with voltage reference 5) Program the example to your development board. 6) Use a DMM to adjust the voltage on PORTA:1 to any value from 0 to 2.5V and record the exact voltage. 7) Read the results of the ADC and note variation from sample to sample. Ideally the voltage divider voltage does not vary, and your ADC reading fluctuates only in the LSB (Least Significant Bit). There are two ways to read the ADC results: JTAG ICE mkii: Connect your Atmel JTAG ICE debugger to your development board, load the example code above into AVRStudio and debug through JTAG. Trigger Breaks and inspect the Result variable using the "watch" feature of AVRStudio. All others: The 12bit ADC value is output on PORTD and PORTE. Use a DMM to measure the voltage on each pin where the MSB is output on PORTE:4 and the LSB is on PORTD:0

Last Updated 4/15/2010 All content copyright 2010 by BostonAndroid

You might also like