Frank's Random Wanderings

Atmel Xmega ADC Problems & Solutions

I’ve been working with the A/D converter on the Atmel Xmega AVR processors, and it’s been quite the handful. At this point in time (Jan 2010) the parts have some very serious issues, and I’ve lost a lot of time trying to get them to work as advertised. This posting is to share what I’ve found, and the best solutions I’ve determined to get the Xmega A/D converters to work.

If you haven’t already done so, you need to read Atmel application note AVR1300, “Using the XMEGA ADC”, as well as use the source code provided with that app note.

You also need to read the errata for the Xmega. The errata is found towards the bottom of the Xmega datasheet. I should point out that the errata only gives you a hint of the problems with the part, but at least Atmel acknowledges there are some big issues. I’ve been working a lot with Atmel’s tech support, and I must say they’ve been helpful (to the extent they can be) and responsive. Supporting a broken part can’t be easy for them either. They have told me that Atmel is working on a silicon revision to fix these problems, but they haven’t been able to tell me when it might see the light of day.

OK, here we go. The code snippets below are from my code; you’ll need to edit them slightly to suit your needs. But they will show you which AVR1300 functions to use.

Singled-Ended Vs Differential

The Xmega is a differential 12-bit ADC that can be used in either singled-ended or differential mode. Initially I’d been using it in single-ended mode, just because it’s easier, and I’d been getting errors of magnitude well in excess of 30 counts (LSBs). Obviously that’s enormous. In fact the errata implies that errors of up to 60 LSBs are possible. That’s almost 6 bits! At that point it’s closer to a random number generator than an A/D converter…

The best thing to do here is use differential mode. I found the best results came from tying one of the external ADC pins to GND, and using that as the “negative” input for the converter. You want to do this with no gain. Using gain only adds more error. So your code would look something like this (change your pin names as appropriate):

ADC_Ch_InputMode_and_Gain_Config(&ADC_BK.CH0, ADC_CH_INPUTMODE_DIFF_gc, ADC_DRIVER_CH_GAIN_NONE); 	// differential mode, no gain
ADC_Ch_InputMux_Config(&ADC_BK.CH0, pin, ADC_CH_MUXNEG_PIN1_gc);			 // + & - inputs

It’s possible to use the internal GND reference, but my experiments seemed to favour using an external pin tied to GND.

Xmega ADC Reference Voltage

The part has an internal 1.00V reference voltage, that you’re supposed to be able to use as the reference voltage for the A/D. In practice this doesn’t work. The errata suggests this, and suggests the 1.00V reference is too low a voltage for the ADC to function properly.

I suspect the best solution is to provide a buffered external stable reference voltage of around 2V and feed it into the part via its external reference pin. In my case this wasn’t an option so I had to settle for something else. The next best option is to use the internal setting of VCC/1.6 as the ADC reference. In such a case your code will look something like this:

ADC_Reference_Config(&ADC_BK, ADC_REFSEL_VCC_gc); 		// use Vcc/1.6 as ADC reference

With a 3.3V VCC, this gives a reference of 2.06V. The problem here is that the reference for an A/D is assumed to be stable. Very stable. That’s hard to claim for a VCC line, which may have all kinds of noise on it. So this falls into the “do the best you can” camp, but it’s still better than using the internal 1.00V.

If you don’t know what VCC is (perhaps you need to measure it), how do you accomplish that when VCC/1.6 is your ADC reference? The only way I know is by getting the ADC to sample the internal bandgap voltage. Something like this:

ADC_Ch_InputMode_and_Gain_Config(&ADC_BK.CH0, ADC_CH_INPUTMODE_INTERNAL_gc, ADC_DRIVER_CH_GAIN_NONE);
ADC_Ch_InputMux_Config(&ADC_BK.CH0, ADC_CH_MUXINT_BANDGAP_gc, 0);

You’ll want to calibrate this yourself. The datasheet says the bandgap is 1.1V. I’ve found it’s a little less than that – more like 1.08V. There’s no way to put a multimeter on it directly, but you can get the ADC to measure it, and you can of course measure your VCC voltage, so that tells you what the bandgap actually is. Note that according to the errata, you cannot measure the bandgap if your VCC is below 2.7V, so in that case you’re probably out of luck, and you’d better look again at providing an external ADC reference voltage.

Note there is an error in the errata. (Yes, Atmel needs an errata for their errata….). Errata #4 talks about using the A/D to measure the 1.00V internal voltage reference. Atmel tech support has confirmed this is not possible – the errata is wrong. The only “reference” you can measure is the bandgap.

Signed vs Unsigned Mode

The Xmega has a 12-bit A/D. It can be run in either signed mode, or unsigned mode. If you’re measuring a simple single-ended input referenced to ground (which most folks do), the way to get the full 12-bits of resolution is to use unsigned mode. In unsigned mode, the “negative” input of the ADC is tied to an internally generated voltage which is at the approximate midpoint of the ADC reference voltage. The problem is this internally generated voltage is very unstable, which has the effect of producing widely varying ADC outputs for a stable input voltage.

The solution is to use signed mode. In signed mode, the “negative” ADC input is under your control, and should be routed to a external pin which is tied to GND (as per “Singled-Ended vs Differential” above). Signed mode is selected much like this:

ADC_ConvMode_and_Resolution_Config(&ADC_BK, ADC_ConvMode_Signed, ADC_RESOLUTION_12BIT_gc);

In signed mode, with a simple single-ended input referenced to ground, you effectively have an 11 bit converter, not a 12. Output values will typically be in the range of 0 – 2047 (0 – 0x7ff).

ADC Sampling Speed

For measuring internal signals you need to be running the ADC below 100 kHz. I’ve tried a bunch of different sampling speeds, and 62 kHz seems to be the sweet spot. If you’re using the internal 2 MHz CPU clock, this is an ADC prescaler of 32. Like so:

ADC_Prescaler_Config(&ADC_BK, ADC_PRESCALER_DIV32_gc); 

ADC Offset

I’ve struggled with this one. The AVR1300 app note & example code shows a simple method of measuring the ADC’s DC offset, and subtracting it from every ADC measurement. I’ve found the DC offset to be relatively small: around -4 in my case. Yet when I try to “correct” my ADC measurements by subtracting it out, it makes my measurements less accurate, not more.

It almost makes me wonder if it’s not a real DC offset, but rather some artifact of the nonlinearity problems currently inherent to the ADC.

After much experimenting, I’ve decided not to use it. What I do is measure the ADC offset when the code starts up, and I print it out for informational purposes. But then I set the offset to zero, and leave it there. So I’m not using the offset (offset = 0 in my code) and this seems to produce better ADC results.

ADC Calibration Bytes

The AVR1300 code shows how to read the ADC calibration bytes into the appropriate ADC register. Like so:

ADC_CalibrationValues_Set(&ADC_BK);

I’ve found it doesn’t seem to make any real difference. I do indeed do it, but commenting this line out doesn’t appear to make a meaningful difference.

Averaging & Clipping

Averaging ADC measurements certainly helps reduce their sample-to-sample variability. To take an ADC measurement, I take 4 and average them. This is well worthwhile.

The other thing I do is clip them to zero. Because I’m measuring single-ended voltages, a negative voltage is not physically possible (or desired). So after my average of 4 ADC measurements, if the result is less than zero, I make the result zero. This helps in my code later, because I know I don’t need to deal with negative numbers. Otherwise, because my ADC appears to have a slight negative “offset”, measuring a grounded pin can result in an ADC measurement of -3 for example, which is not meaningful. So something like this gets clipped to zero.

Summary

To summarise a long post on the care & feeding of a currently rather broken Xmega ADC, here are my hard-earned suggestions:

  • Use Differential mode / differential inputs. Tie an external ADC pin to GND if necessary.
  • Use either VCC/1.6, or an external reference voltage, for the ADC reference. Not the internal 1.00V. Use the ADC to measure the bandgap if that helps.
  • Use signed mode.
  • Consider using a 62 kHz ADC sampling rate.
  • Consider assuming the ADC offset is zero.
  • Average multiple ADC measurements (and clip if it makes sense in your application)

Good luck! Let’s hope Atmel releases fixed silicon really soon.

Update: In late 2011 Atmel started shipping a revised line of Xmega parts called the “U” parts. The new partnumbers end with a U. For example ATXMEGA32A4U. Although I haven’t personally tried these new devices, apparently these U parts have the ADC problems fixed. I believe the non-U parts still have the ADC problems. If you’re going to be using an Xmega for something, I’d suggest looking for a U part.

39 thoughts on “Atmel Xmega ADC Problems & Solutions

  1. Fergus Dixon

    Thanks, Frank. Definitely signed 0-2047 mode makes a huge difference. Here are our readings after doing these changes

    DVM PC 62kHz Signed Unsigned
    Measured ADC5=GND PC PC PC
    0.142 0.134 0.008 0.134 0.008 0.136 0.006 0.144 -0.002
    0.270 0.264 0.006 0.264 0.006 0.264 0.006 0.28 -0.010
    0.385 0.379 0.006 0.379 0.006 0.379 0.006 0.402 -0.017
    0.528 0.523 0.005 0.523 0.005 0.524 0.004 0.555 -0.027
    0.667 0.664 0.003 0.664 0.003 0.666 0.001 0.77 -0.103
    0.816 0.813 0.003 0.813 0.003 0.815 0.001 0.86 -0.044
    0.943 0.942 0.001 0.940 0.003 0.942 0.001 0.993 -0.050
    1.051 1.050 0.001 1.050 0.001 1.052 -0.001 1.103 -0.052
    1.245 1.245 0.000 1.246 -0.001 1.247 -0.002 1.293 -0.048
    1.368 1.370 -0.002 1.369 -0.001 1.370 -0.002 1.416 -0.048
    1.490 1.492 -0.002 1.492 -0.002 1.492 -0.002 1.531 -0.041
    1.612 1.615 -0.003 1.616 -0.004 1.611 0.001 1.643 -0.031
    1.719 1.724 -0.005 1.723 -0.004 1.717 0.002 1.736 -0.017
    1.818 1.822 -0.004 1.826 -0.008 1.814 0.004 1.829 -0.011
    1.959 1.965 -0.006 1.965 -0.006 1.952 0.007 1.956 0.003
    2.070 2.076 -0.006 2.076 -0.006 2.048 0.022 2.048 0.022
    MAX 0.008 0.008 0.022 0.022
    MIN -0.006 -0.008 -0.002 -0.103
    AVG 0.0003 0.0002 0.0034 -0.0298

  2. limpkin

    Hey there!

    Just wanted to let you know that the single ended problem still exists on the U part.
    I’m using an atxmega16a4u and it seems that depending on the signal source impedance the atxmega will behave very badly in single ended mode.

    Cheers!
    Mathieu

  3. CS

    Hi Frank,
    thank you for your great article. I had the problem of measuring an external signal in full 0V to VCC range which happen to be a bit difficult. After hours of research I found Atmels note “AVR1324: XMEGA ADC Selection Guide”. For those who happen to have an xmega B or an xmega AU have the ability to do that.

    How to measure a signal around VCC with xmega B or AU:
    – Select Differential-With-Gain mode with Gain 0.5 (this gain setting is available on B and AU series)
    – Select either a pin tied to GND or Pad-GND as negative input
    – You’re done!
    There is a drawback: you only get 11 bits of resolution as the rest of your range is below 0V…

    Greetings from Germany.

  4. Sina

    Hello again and with good news.
    Also xmega’s internal 1v and internal Vcc/1.6 do not work as a reference for ADC (As I mentioned before)
    if you use external Vref the results are quite impressive.
    Keep in mind that external Vref should be less than AVcc by 0.6 volts.
    I used these configurations in CodeVision and I nulled the offset using CodeWizard. The results were exact and neat. No problem at all.
    No oscillating (only 1 or 2 because I used Vcc to make Vref) and no wrong numbers.
    So I should correct myself: XMEGA is the ultimate microcontroller.

  5. Sina

    Hi Frank. Thanks alot for your great article.
    I’m using ATXMEGA64A3U and it still has the same issues with the ADC. It’s really annoying because I thought xmega would be the ultimate microcontroller which is perfect in every aspect but it turned out that I was wrong. First I was impressed with it’s 12 bit ADC resolution but when I began my experiments I was disappointed.

  6. Zajc3w

    I’m getting +-2 counts error in differential mode with input range 0-2.4V
    My setup:
    Atxmega128a1.tqfp to 2.54mm headers soldered to prototype stripboard
    external cheap shunt refrence, lm4051-1.2V
    No ferrite bead on AVCC
    Ground wired in “star” style from lt1117-3.3v regulator
    positive adc input connected to external Vref(Vref goes through voltage follower based on LF353)
    ADC in signed mode, so i get -2048 for input connected to gound and +2047 for input at 2.4V

  7. frank Post author

    I don’t know. And it’s been a long time since I last looked at this (the original posting was in Jan 2010, more than 3 years ago, so I expect many things have been improved since then. I suggest you post your question over at avrfreaks.net

  8. abhishek Shakya

    Dear Frank,

    The problem with the ADC and its solution for XMEGA what You have discussed, does holds
    its holds true for AT xmega 128A3U also???

  9. brrr

    Hello.

    First of all , thank You Frank for investigating this problem.

    For now I can tell You that all of Yours “discoveries” apply to xmega16a-au.

    Since the beggining of mine experiments i have tried various mix of hardware and ADC setup. Now i have ADC with external reference (MAX6003 + decopuling caps + AVCC LC filter). ADC is configured to perform signle signed conversion using diferential input without gain with 32MHz/512 clock. With this setup i estimate inaccuracy about +/- 1LSB.

    Accuracy could be improved by using small ceramic cap between ADC* and GNDA lines but notce that it would decrease overall bandwidth 🙂

    Since described problem aren’t the only issue I have decided to forget xmega’s and use ARM instead.

  10. babak

    after lost a week , we found ADC of XMEGA worse and now we think to add LM324 to adj a voltage and use xmega , in my experience :
    USE : in Differential mode with , signed mode , low speed and try to be patient

    thanks for the person who write this page

  11. mark mccaughey

    we have been using the old ATXMEGA128A1 (we used a work around to try and plug the linearity using fixed offsets in C.) and have had some ATXMEGA128A1U samples and these work as they should great.

    ordered a bulk quantity of A1U versions to arrive.

  12. frank Post author

    That is surprising. I’ve personally moved on from the AVR chips – I’ve been using Cortex-M3 and M4 type chips for the last little while now. Vastly more performance, memory, etc, for the same price (they are a little more complex though – the learning curve for the AVR is a little smaller, although not a huge amount smaller). I had been hoping Atmel would fix their AVRs; from what you’re saying, it sounds like maybe not.

  13. llimis

    I have a xmega32A4U Rev E chip and I have to say that the ADC converter still have the same issues than the previous xmega chips. Atmel has not corrected none of its problems. Incredible.

    Best Regards.

    (Sorry for my bad english)

  14. Matic

    Hello,
    first i would like to thank you for very useful advices on using ADC.
    I have one question about XMEGA ADC. I don’t understand whay is in unsigned, single-ended mode negative pin fixed to Vref/2-dV. I would apreciate if someone could explain that.
    Thanks

  15. Edimahler

    Our supplier just informed me today how to interpret the version of the chip looking on the housing only (still don’t know where to read it out of the fuses(?)). On my BGA-Chip the following data is given:

    AT0945A – Manufactured on Week 45 Year 2009, Rev-A
    XMEGA16A4 – Device Name
    CU-K – Package Type – BGA
    9H1120-1 – Lot Number

    So, I’m one of the unlucky ones out there who still has the oldest possible version A of the chip with all the nice bugs inside. 🙁

    Will tell you about news concerning the new version, as soon as I got them and made the first tests with it.

  16. frank Post author

    I’ve read on the avrfreaks forums that revised silicon is coming. I don’t know how to identify the “fixed” silicon, but it’s great you’re hearing it may actually be out. If you ever get the chance to test some new chips, do let us know the results.

  17. Edimahler

    Dear Frank,

    Also many thanks from my side, this page is just great! Had troubles using the ADC also. Because I’m using it already in the differential mode, only the divergences between the single conversions was a bigger problem. I solved it averaging by 16 or even 32 times sampling the same signal using 1MSPS. I found out, that this gives (at least for me) the smaller error and faster result than setting the ADC frequency to a smaller value. I’ve very time critical code there. Averaging using an amount of 2^n (2, 4, 8, 16, 32,… times) has of course the big advantage, that you’re able to do a fast shift operation instead of a really slow division by another value.

    Today we had a supplier in house who told us, that the corrected chips should now be on market. I’m also using the ATxmega16A4 as you did and read a “H” on the housing of the recently (some weeks ago) ordered pieces. Could this already be the new version? Which advanced fuse-register tells me about the version? (I can only see data like “lot number”, “wafer number”, “wafer coordinates” and so on…

  18. frank Post author

    From what I’ve seen, the part has bigger problems to worry about. It’s possible I have seen problems related to this; there’s no way to know. I simply tried a variety of different sample rates and found the best results at 62 kS/s (along with doing the other things mentioned in the posts, ie external 2V reference, averaging of multiple results, etc).

  19. ER!K

    Hello Frank,
    in AVR1300 a minimum ADC clock frequency of 100kHz is recommended (due to self decharging of the S&H capacitor leading to too low results, I guess). Since you recommend less than 100kHz, in fact 62kHz, have you experienced any problems or effects here?
    Thank you and best regards, ER!K

  20. Aziz

    Hello Frank,
    I understand why the unsigned offset exists.
    I just wanted to ask why the Signed offset function exist in Atmel code sample, although the signed differential mode is assumed to have no offset. At the moment, I think signed single-ended measurment mode has a similar offset as the unsigned mode, but I won’t use it anyway.
    Thank you very much for the reply.

  21. frank Post author

    I presume you’re asking about the UNsigned “get offset” function. The unsigned “get offset” performs an unsigned ADC measurement. From my original post:

    “In unsigned mode, the “negative” input of the ADC is tied to an internally generated voltage which is at the approximate midpoint of the ADC reference voltage. The problem is this internally generated voltage is very unstable, which has the effect of producing widely varying ADC outputs for a stable input voltage.”

    That’ll explain why taking an unsigned offset measurement is going to give a crazy result.

  22. Phil

    Thanks Frank
    Great Errata sheet, I am just about to start using these chips, so all very handy to know
    Thanks again

  23. Aziz

    Hello Frank,
    very useful article & blog, thank you very much!
    What did you mean with “Consider assuming the ADC offset is zero.” at summary? Only the differential mode, right?
    Unsigned, single-ended mode has a huge offset, as stated on the application note, around 200 counts. It is proposed to measure this offset by connecting the used external pin at actual configuration to GND, which sucks.
    As you stated, signed, differential mode can be considered as offsetless (as I also found out experimentally), but the funny thing:
    At the given sample driver code, they supply a function;
    int8_t ADC_Offset_Get_Signed(ADC_t * adc, ADC_CH_t *ch, bool oversampling);
    as well as int8_t ADC_Offset_Get_Unsigned(ADC_t * adc, ADC_CH_t *ch, bool oversampling);
    Do you have an idea, what for the signed one does exist? It ruins the measurment result.

  24. frank Post author

    Yes, that’s right. I was finding more consistent results measuring external signals when running the ADC more slowly.

  25. ER!K

    Hello Frank,
    thanks for sharing your Xmega ADC experiences. You write “For measuring internal signals you need to be running the ADC below 100 kHz.” and in your summary you suggest to use 62kHz overall. So you suggest to use the 62kHz clock also for external signals, not only for internals?

  26. frank Post author

    It appears to still be true. Atmel doesn’t appear to be in any hurry to fix these parts. The most recent silicon I’ve seen, and comments from others on avrfreaks, indicates these problems are still current.

  27. Giuseppe

    Gosh!
    Thank you for the article, unfortunately I am unable to use differential mode as I have no more available pins (using an XMega128A1, almost completely full).
    Does someone knows how much of this article is still true at the current date (2010/12/22)???

  28. Jesper

    hi frank,

    nice article.. just a comment: I think you should combine the “single-ended vs differential” and “signed vs unsigned mode” sections as having them separate can be confusing since it’s not possible to combine unsigned and differential modes..

    regarding the calibration values in the signature row; I’ve gone through a bunch of xmega32A4 chips, and they all have the calibration value 0x0444.. so much for calibrating anything..

    also, for people forced to use unsigned mode; the offset that atmel specifies to be around 200 depends on Vref even if the transfer function says it shouldn’t!!! take a look at my thread here: http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=91379

  29. MarkW

    I notice with interest your comments that the Calibration bytes have no appreciable effect on your results. I notice that all of the XMEGA xhips I have tested so far have these calibration bytes set as ADCACAL0=ADCBCAL0=0xff and ADCACAL1=ADCBCAL1=0x00
    I too have confirmed that setting or not setting these calibration bytes seem to have little effect on the results. However 0x00ff (255) is quite a large offset. Perhaps they are not simply a linear offset scale? Perhaps one byte is a zero offset and the other a gain offset? 0x00 (0) for the one and 0xff (-1) for the other. That would explain why I am seeing little change, despite the disparate numbers. Just my 2c worth.

  30. Rob

    Thanks Frank.
    Im using an XMegaA3 device and have changed the design to incorporate all your valuable recommendations!

  31. abey

    is there any circuit for usb flash drive mp3 player,please tell the simple concept about reading mp3 from usb flash drive and how it convert it to sound,

  32. frank

    Hi Martin,

    I don’t know the mask revision, and with the boards now out of my hands I’m unable to hook one up to a programmer to read out any registers. However this is what can be read off the top of the chips:
    ATXMEGA16A4
    AU 0921

  33. martin

    Hi Frank,

    Wow, thanks for all the info. Can you tell me which Xmega and mask revision you did your tests on.
    I’m working with 128A1 and Rev H (I have some Rev G as well). I thought that single ended, unsigned with the internal 1V reference would be OK for what I need to do but I’m having to reconsider that.

  34. Partha

    Hello Frank,
    Thanks a million for sharing your finds on the XMEGA ADC. I had chosen XMEGA for a product of ours and ADC is essential. We are operating XMEGA at 32MHz (internal clock) and when we tried to scale down ADC conversion speed to near about 62k the controller is hanging but at higher speeds it works but not sure about the conversion accuracy.

Leave a Reply

Your email address will not be published. Required fields are marked *