ADC instability in M0 Pro

I have problems with instability in the ADC conversion value at high conversion rates. I need an ADC conversion time of 5 µs, or less with 12-bit resolution. The 350 ksample/s ADC of the M0 PRO should seems to suggest a conversion time of ~2.8 µs should be achievable. I discovered the conversion value is unstable at high conversion rates.

First, I timed the standard analogue readout code from a single pin (A1) using the code in Sketch 1 and a 100 MHz bandwidth oscilloscope. The conversion time was ~220 µs, which surprisingly, is actually worse than for an Arduino Uno (~120 µs). This is far from my requirement – so I dug into the ADC configuration.

I connected the A1 input pin to the nearest GND pin by a 1.5 cm wire. (I checked and a 50 Ohm source will give about 112 ns settling time according to the manual. [http://www.atmel.com/images/atmel-42181-sam-d21_datasheet.pdf p. 962.) So this should be a very good 0 V input.

So I set up a sketch for testing purposes based on the code on the GitHub site:

  1. Without any modifications of the registers. (see http://www.atmel.com/images/atmel-42181-sam-d21_datasheet.pdf, pages: 846- 885 for description.) I obtained the following hex values for the register contents.

INTFLAG: 8 //Synch read interrupt set
INTENSETs: 0
INTENCLR: 0
EVCTRL: 0 // No events set-up
SAMPLEN: 3F // Max sample length
CTRLB: 520 // Prescale factor 128, 10-bit single-conversion, single ended
AVGCTRL: 11 // Average two successive conversions
SWTRIG: 0
INPUTCTRL: 1802 // ADC1 input pos and internal negative input.
REFCTL: 1 //2.2297 V internal reference
Conversion value: 7 // This is varies by ±1-2 LSB. Just noise- which is OK.

Under these conditions the conversion time is ~210 µs. The slight improvement over Sketch 1 is because intitialisation code is moved so it is only executed in setup().

  1. Changing to remove the averaging over two conversions (AVGCTRL = 0x00) reduces the conversion time to ~53 µs with no noticeable degradation in conversion value.

  2. Increasing the resolution to 12 bits (CTRLB= 0x500) increased the conversion value to ~34 with ± few LSB scatter with no change in conversion time. (53 µs).

  3. Reducing the number of half-cycles to 1 (SAMPLEN=0x01) gave no change in conversion values, ~34±few LSB, but reduced conversion time to 27 µs.

  4. Decreasing the prescale factor to 64X (CTRLB= 0x400) leads to instability that appears as large changes in successive conversion values. (e.g. 1651->0->176…). Conversion time ~15 µs.

  5. Increasing the sampling time to 10 half clock-cycles restored stability in conversion values to ~34±few LSB. This is OK for my purpose. Conversion time 20.2 µs.

  6. Decreasing the prescale factor to 32X (CTRLB= 0x300) and increasing the sample time to the maximum (1 (SAMPLEN=0x3F) ) leads to instability in the conversion values gave larger conversion values ~298. And a longer conversion time 31 µs.

My conclusion from all this is that ADC conversion becomes unstable if the sample time and clock time becomes too short. The best result so far is in Sketch 2.

QUESTIONS
Q1. Has anybody found a solution to this instability problem?

Q2. Presumably, the solution is to optimise the ADC clock, prescale and samples time. With the prescale the ADC clock can only be adjusted in steps of two. I presume this ADC clock can be modified by the Generic clock generator. Does anybody have an experience of this? Or example code?

Q3. Of course, decreasing the conversion time makes the whole ADC more susceptible to spikes on the supply lines. I don't see any visible spikes on the different GND lines. Which one is best to use as analogue ground? (I might suggest for future Arduino designs that one of the three GND pins on the standard pin-out is dedicated to the GNDANA pin on the chip. This should reduce pick-up.)

Many thanks in advance for any advice and help!

Harry J. Whitlow

Sketch_1.ino (321 Bytes)

Sketch_2.ino (4.9 KB)

Hi Harry!

thanks for the analysis, we can surely pick some better defaults for the Zero boards from it.

About the ADC accuracy, you said that:

whitlow:
5. Decreasing the prescale factor to 64X (CTRLB= 0x400) leads to instability that appears as large changes in successive conversion values. (e.g. 1651->0->176…). Conversion time ~15 µs.

this makes me think that you may have issues with your analog source's impedance, I had similar problems with the Arduino Due: increasing the sampling frequency lead to lose accuracy on the ADC in particular with rapidly-changing signals. The solution was to put an analog buffer to make the signal source stronger.

It may be?

The SAMD21 datasheet (at page 955) provides some formulas to calculate the maximum impedance for a given accuracy and sample rate.

36.9.4.3 Inputs and Sample and Hold Acquisition Times
The analog voltage source must be able to charge the sample and hold (S/H) capacitor in the ADC in order to achieve
maximum accuracy. Seen externally the ADC input consists of a resistor ( R SAMPLE ) and a capacitor ( C SAMPLE ). In addition, the source resistance ( R SOURCE ) must be taken into account when calculating the required sample and hold time. Figure 36-5 shows the ADC input channel equivalent circuit.

Thanks for the comment. You are absolutely correct. I already found out the hard way about the problem of the source impedance limitation and charging the sample-hold capacitor. For my application I currently use a Due driven by a fast op-amp configured as a voltage follower - exactly as you suggest - and this works fine, even when I speeded up the clock on the Due.

For my application I want to use the Zero/M0 Pro instead of the Due because I can cut the external component count by using the on-chip discriminators and analogue reference voltage. I did the calculation in the data sheet and 50 Ohms source impedance; then the ADC should work fine at 350 ksamples/s - but I have not yet succeeded to get this speed. (I need about 5-10 µs conversion time for 12 bits.) I also tried with a short circuit to the ground line and also observe the same instability.

I am still working on the ADC clock to try change it from the 8 MHz to a prescaled-down 96 or 48 MHz clock.source to try to see if I can speed things up this way. I presume, this will require a different set of ADC presets.

I am also planning to investigate at the other end of the scale by configuring the ADC to use the 16X gain and differential input capability of the the Zero/M0 Pro to measure type-K thermocouple voltages directly.

Harry J. Whitlow

Do you really need differential for the K-thermocouple? you cannot get a negative voltage from it. Note that
Voltage vs Temperature is non-linear.

I used your code as boilerplate and got 23.8 uSec for single-conversion, close to what I need. I plan next to run the ADC in free-run mode and expect a substantial increase in sample rate. Then to learn how to connect the ADC to the DMA so I can grab a series of conversions with no intervention of the program. That should top-out the conversion rate.

How are you powering your board? Other posts suggest that the USB port is inadequate for stable ADC reference voltage and that 7-12 VDC is better. I have not yet investigated it.

First the thermocouple question: Using differential inputs is less sensitive to pickup as voltage picked up on both leads will be canceled out in a differential measurement. I am not sure if the Zero/M0 Pro is truly differential and will accept a voltage less than zero on the negative differential input, or if input voltages must always be positive. You are correct, the thermocouple voltage is non-linear. The simplest thing is to calculate, using a polynomial, the temperature from the measured thermocouple voltage. Polynomial coefficients up to 9th order are given by NIST and specified at: http://www.keysight.com/upload/cmc_upload/All/5306OSKR-MXD-5501-040107_2.htm. Here it is important to bear in mind that the Arduino float has just 6-7 digits of precision. It may be enough to truncate the polynomial after just a few terms.

DMA should give some improvement in ADC speed. I don't think very much according to the manual as once the start bit in SWTRIG has been set, the ADC readout according to the manual is antonymous of the processor till the result is ready. DMA will improve processor throughput by removing wait states - but no do much for the intrinsic speed of the ADC.

It could be the power supply that causes the instability as you suggest. I just use the USB. Clocking digital electronics faster increases power consumption and speeding up the ADC could be enough to cause the stability of the power line to degrade. I am on work travel right now - so I cannot test this for the next week or so.

Harry J. Whitlow

I patched in an external power supply and saw the ADC noise decrease dramatically. I used 8v, just as well could have been anything 7-12 volts. I am sampling two 60 Hz signals alternately and getting over 2000 samples per 16 mSec. Fast enough for me.

I don't see anything in the chip specs that allows for a negative voltage on any ADC input. Differential uses two channels and both must be at or above zero. You might have to use an op amp to convert the differential signals to single-ended.

Hi Whitlow, Great discussion! I just today picked up a Feather M0 with the SAMD21. I hate the bootloader - may see about changing to the ZERO bootloader.

Where do I find the settings for the registers, etc, and how do I set them?

I have about 50 mV reading at zero V input. I am using analogReference(AR_EXTERNAL); This works but the actual voltage seems to be lower than at the 3.318 level on the pin next to ARef. The UNO has a resistance to ground as I understand, that forms a voltage divider. Grrrr. Not sure about this but probably... Let's see if I can suss out the right value.

I also want to do differential reads - how to do that? I am appalled by the lack of standard documentation on the Arduino website. Got to be a better reference somewhere!

Thanks for listening!