Professional Documents
Culture Documents
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)
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.
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.
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) );
// 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;
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 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
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
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.
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