Analog input issues with DUE

My (genuine) Arduino DUE board gives inconsistent analog input readings.

An analog voltage equal to exactly 1/2 of the 3.3V reference is connected to A1, read 500 times in a loop, and then averaged to improve accuracy. The voltage is obtained by resistively dividing the reference with two precision 2K000 resistors, and is filtered with a 1uF capacitor mounted directly on the input pin.

As 500 readings are taken, with a delay between readings of 1 mS, average readings are displayed every 0.5 seconds. The consistency of successive averaged readings is less than 0.1 count, as it should be given there are 500 readings in each average.

However, is observed that the averaged value varies by up to 2LSB, depending on the presence or absence of program code that has nothing whatsoever to do with reading the voltage or calculating the average.

For example, when the second-last line of code is “Serial.print(a)”, the average voltage reads 2047.4, which is pretty much exactly what you would expect, being almost exactly half of the full scale ADC count of 4095.

However, when that line of code is changed to “Serial.print(a/4.0)”, the average voltage changes to 2049.5, an inexplicable and unacceptable change of ~2LSB.

There is nothing ‘special’ about this line particular of code that changes the analog reading. In general, it is found that adding, removing or altering ANY line of code slightly changes the voltage reading. If you use the serial monitor to look at the individual readings rather than the average, then you observe the same thing. The voltage returned by analogRead() varies according code in the program that has absolutely nothing to do with reading the voltage.

Something is not right here, and this strange effect is a real PITA, making it impossible to obtain meaningful, accurate voltage readings.

I also have a UNO board, and it behaves perfectly. The simple program code is given below.

What is going on here???


/*
This program illustrates a problem with reading analog inputs on the Arduino DUE
*/

int sum; // DUE has 32 bit integers, so don’t need ‘long’
int a,b;
int n=500;
float Vave;

void setup() // the setup routine runs once when you press reset:
{
analogReadResolution(12);
Serial.begin(9600); // initialize serial communication at 9600 bits per second:
}

void loop()
{
sum=0;
b=0;
for (int i=1; i <= n; i++) {
delayMicroseconds(1000); // 1 kHz read rate, analog read needs this delay
sum = sum + analogRead(A1); // read the voltage on analog pin 1
} // Next i

Vave=sum/float(n); // Vaverage in units of ADC counts
a=b/float(n); // Arbitrary, typical line of code

Serial.print( Vave );
Serial.print(" ");

Serial.print( a/4.0 ); // Changing ( a ) to ( a/4.0 ) significantly changes the average voltage reading!!??!!
Serial.println(" ");
}

b=0;
// ...
a=b/float(n);

Wouldn't "a" always be 0 since b is?

[quote author=James C4S link=topic=194308.msg1435066#msg1435066 date=1382231746]

b=0;
// ...
a=b/float(n);

Wouldn't "a" always be 0 since b is? [/quote]

Yes. The only thing that matters is the Vave, the average voltage being measured.

a and b are just 'typical' variables that are irrelevant to Vave.

However, changing any of the code involving a or b, or indeed adding any other code of any sort, changes the voltage read back by analogRead(). Arguably the effect is small at typically 1 to 2 LSB and most people would never notice, but it is real, should not happen, and is a right PITA.

Where the fault lies is unclear to me at this stage ....

int sum;          // DUE has 32 bit integers, so don't need 'long'

However putting 'long' wouldn't hurt and then the code is more portable. Best not to rely on special knowledge like that. If you want a 32-bit integer, say so.

  delayMicroseconds(1000);           // 1 kHz read rate, analog read needs this delay

I'm not a Due expert, but why does it need this delay? That's a whole millisecond.

qwerty99: Yes. The only thing that matters is the Vave, the average voltage being measured.

Okay, I don't understand.

The example code you posted has:

Serial.print( Vave );
Serial.print("  ");

Serial.print( a/4.0 );              //  Changing ( a ) to ( a/4.0 ) significantly changes the average voltage reading!!??!! Serial.println("  ");

You are saying that the Serial.print(a/4.0) changes the serial print of Vave?

 analogReadResolution(12);

How does it perform with this line omitted?

The code has been cut down from a much longer program that runs on my UNO, and originally variable ‘sum’ was ‘long’. I replaced it with ‘int’ on the off chance that the Due didn’t like ‘long’ because it already uses 32 bits for integers as standard. Either way made no difference to the problem.

The millisecond delay has been carried over from the Uno. The faster Due appears to read accurately with considerably smaller delays, but the Uno actually needs a 1 mS delay or the reading will be slightly wrong. I know this from experience, as well as from looking at sample Arduino code.

The faster Due appears to read accurately with considerably smaller delays, but the Uno actually needs a 1 mS delay or the reading will be slightly wrong. I know this from experience, as well as from looking at sample Arduino code.

Can you define “slightly wrong”? Does it change over time, or readings without the delay are different to ones with it?


I tested this claim on my Uno:

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  }  // end of setup

const int COUNT = 1000;

void loop ()
  {
  unsigned long total = 0;
  
  for (int i = 0; i < COUNT; i++)
    {
    total += analogRead (A0);
    }
    
  Serial.print ("Without delays: ");
  Serial.println (total / COUNT);

  total = 0;
  
  for (int i = 0; i < COUNT; i++)
    {
    total += analogRead (A0);
    delayMicroseconds (1000);
    }
    
  Serial.print ("With    delays: ");
  Serial.println (total / COUNT);

  delay (1000);
  }  // end of loop
Without delays: 516
With    delays: 516
Without delays: 516
With    delays: 516
Without delays: 516
With    delays: 516
Without delays: 516
With    delays: 516
Without delays: 516
With    delays: 516
Without delays: 516
With    delays: 516

So I’ll assert that this claim is dis-proven.

[quote author=James C4S link=topic=194308.msg1435090#msg1435090 date=1382232871]

qwerty99: Yes. The only thing that matters is the Vave, the average voltage being measured.

Okay, I don't understand.

The example code you posted has:

Serial.print( Vave );
Serial.print("  ");

Serial.print( a/4.0 );              //  Changing ( a ) to ( a/4.0 ) significantly changes the average voltage reading!!??!! Serial.println("  ");

You are saying that the Serial.print(a/4.0) changes the serial print of Vave? [/quote]

I am saying exactly what I am saying, no more nor less.

Adding, removing or changing ANY code that is not associated with reading or averaging of the voltage, in general slightly changes the value of the analog voltage read by analogRead().

As a typical (but not unique) example, changing (a) to (a/4.0) in the 2nd last line changes the voltage, as returned by analogRead(), by about 2 LSB.

The actual input voltage does not change, as confirmed by a 6.5 digit DVM.

Hope the description of the problem is clear, and it is strange indeed, not to mention annoying.

[quote author=Nick Gammon link=topic=194308.msg1435123#msg1435123 date=1382234420]

The faster Due appears to read accurately with considerably smaller delays, but the Uno actually needs a 1 mS delay or the reading will be slightly wrong. I know this from experience, as well as from looking at sample Arduino code.

Can you define "slightly wrong"? Does it change over time, or readings without the delay are different to ones with it?

I tested this claim on my Uno:

So I'll assert that this claim is dis-proven. [/quote]

The claim is not dis-proven. What you have shown is that in some situations the delay may not be necessary, but I assure that in some other situations it is, and I agree the onus is on me to show that.

However, it is irrelevant to the discussion at hand, so I suggest that we temporarily shelve that debate, get to the bottom of the problem at hand, and perhaps return to the issue of delay before ADC read in a separate thread. Readings without the delay can be different (and wrong) compared to those with the delay. This is well known, and you will find similar requiriments for a delay prior to reading the ADC in the documentation for professional level data acquisition boards made by National Instruments etc. Try reading a number of different voltages without a delay and see how how you go. :)

I've reproduced your problem, to an extent. I used 2 x 1K resistors, giving me a predicted 1.65V and a measured 1.6516V on A1.

Running with the line:

Serial.print( a/4.0 );

I get:

2041.75  0.00  
2041.95  0.00  
2041.85  0.00  
2041.77  0.00  
2041.89  0.00  
2042.07  0.00  
2042.30  0.00  
2041.95  0.00  
2042.06  0.00  
2042.01  0.00  
2042.07  0.00  
2042.09  0.00  
2041.93  0.00  
2041.91  0.00  
2041.85  0.00  
2041.95  0.00  
2041.96  0.00  
2042.20  0.00  
2041.79  0.00  
2041.97  0.00  
2041.59  0.00  
2041.89  0.00  
2041.53  0.00  
2041.84  0.00  
2042.19  0.00  
2041.67  0.00

Average= 2041.92

Running with the line:

Serial.print( a );

I get:

2042.38  0  
2041.89  0  
2042.22  0  
2042.51  0  
2042.30  0  
2042.10  0  
2042.29  0  
2042.24  0  
2042.25  0  
2041.82  0  
2042.18  0  
2042.37  0  
2042.28  0  
2042.18  0  
2042.07  0  
2042.28  0  
2042.31  0  
2042.31  0  
2042.43  0  
2042.34  0  
2042.52  0  
2042.73  0  
2042.20  0  
2042.23  0  
2042.34  0  
2042.47  0  
2042.24  0  
2042.23  0

Average = 2042.27


being almost exactly half of the full scale ADC count of 4097.

4095, surely?


The difference between the two averages is:

2042.27 -  2041.92 = 0.35

Dividing into the larger one, say:

0.35 / 2042.27 = 0.00017

And a one-bit error out of 4095 would be:

 1 / 4095 = 0.00024

So you are getting less than a one-bit error, aren't you? (Or [u]I[/u] am anyway).

[quote author=Nick Gammon link=topic=194308.msg1435093#msg1435093 date=1382232902]

 analogReadResolution(12);

How does it perform with this line omitted? [/quote]

Good question, but unfortunately the problem is still there. In this case changing (a) to (a/4.0) has only a small effect, but other program changes (not related to reading or averaging the voltage) have a significant effect, such as changing a=b/float(n); to a=b/float(500);

The general observation remains unchanged. Adding, deleting or changing code (not related to reading or averaging the voltage) has a small effect on the voltage read back from analogRead(), varying from undetectable to a worst case of around 2 counts in a full scale of 4095.

While I was awaiting your response I tested it myself, and got similar results. One reading was consistently lower than the other.

I don't know where to take it from here. I am not a Due expert and don't really intend to look into how its libraries are written. Conceivably there is a bug in the way it gets the analog readings, but how would that be affected by unrelated code elsewhere?

All I can suggest is trying one of the sleep modes which takes better analog readings (if there is one), such as what the Atmega328P has. For now, it would appear that in 12-bit mode you maybe can only rely on the same 10-bit accuracy you get from your Uno.

I can't see offhand in the datasheet where the claimed accuracy is, so perhaps the chip is actually performing within specs.

[quote author=Nick Gammon link=topic=194308.msg1435162#msg1435162 date=1382237547] While I was awaiting your response I tested it myself, and got similar results. One reading was consistently lower than the other.

I don't know where to take it from here. I am not a Due expert and don't really intend to look into how its libraries are written. Conceivably there is a bug in the way it gets the analog readings, but how would that be affected by unrelated code elsewhere?

All I can suggest is trying one of the sleep modes which takes better analog readings (if there is one), such as what the Atmega328P has. For now, it would appear that in 12-bit mode you maybe can only rely on the same 10-bit accuracy you get from your Uno.

I can't see offhand in the datasheet where the claimed accuracy is, so perhaps the chip is actually performing within specs. [/quote]

Thanks for taking the trouble to test my claims.

We are on the same wavelength, and getting broadly similar results.

In my case, the average size of the effect was around one count in 4095, with a worst case of 2 counts in 4095, which is really is quite significant, and as you say, starts to make the claimed 12 bits a bit hollow, and arguably not really much better than the Atmega328.

A related observation that I did not mention, is that the Due ADC is horribly noisy compared to my UNO. Even in ABSOLUTE terms it is noiser than the UNO, which is a poor reflection indeed considering that the Due has 2 more bits of resolution. Whether this is fundametally because of the chip itself, or the layout of the board, I do not know.

The sad reality is, that with averaging, I get better accuracy with the UNO than with the Due, which does piss me off somewhat. The Due is faster to be sure, but in terms of useable ADC resolution and accuracy is actually a step backwards.

If you have any further thoughts I would be interested.

A related observation that I did not mention, is that the UNO ADC is horribly noisy compared to my UNO. Even in ABSOLUTE terms it is noiser than the UNO,

You mean: the Due ADC is horribly noisy … ?

I'm not even sure about the claimed need to allow 1 mS between readings on the Uno. Surely, having to wait such a long time renders the ADC fairly useless? Even qualifying it with "if the voltage changes"? Surely the voltage would change, or you wouldn't need to measure it.

The fact is that the ADC should take 104 µS to do a reading, with no extra delay needed. You can also do other work in the meantime as I have shown on my interrupts page.

http://www.gammon.com.au/interrupts

The only qualification in the (Atmega328) datasheet is that it takes a few more microseconds to do the first conversion, after the ADC is switched on.

Also you can put it into sleep mode (SLEEP_MODE_ADC) to reduce noise.

Check out the Due page:

http://arduino.cc/en/Main/ArduinoBoardDue

Q. What is the maximum sample rate possible at one Analog Input?

A. It depends. The core CPU supports sampling at 1 sample per microsecond (1Ms/S). But the AnalogRead() function has so much library overhead that you will not get more than about 50ks/S (50,000 samples per second). If you examine the library code, you can find the main source statements to put in your own file, but that will take some skill. If you’re a C programmer, it’s a piece of cake. If you’re not, it’s a bucket of spaghetti! I hope this helps.

I’ll just quote that again:

it’s a bucket of spaghetti!

Plus other comments further down about the ADC sampling rate.

Yes, sorry for my carelessness. I edited the post to correct the error.

Despite the fact that several independent ‘official’ program examples within the Arduino IDE show a delay of 1 or 2 mS “for the ADC to stabilize”, I agree with you that a delay of this magnitude should not be necessary.

When I referred to ‘reading a number of different voltages’, I meant the situation where you may need a delay when switching from one analog input to another, because it takes time for the voltage on internal S/H capacitor to stabilize, especially with high source impedances. Keep in mind, there is only one ADC, so you are actually asking a lot to abruptly switch a new and different voltage to the S/H capacitor at the input to the single ADC, and expect that new voltage to be instantly stable. Even so, calculation shows that we are talking sub microsecond and, in any event, my particular example does not switch between inputs.

I had a recollection of observing small errors without the delay, but it does seem that my recollection is wrong, because when I hooked up the Uno a few minutes ago and removed the delay, I got exactly the same reading. Actually, I measure the voltage on 2 channels. I use two sequential read loops, precisely to ensure that the input is not rapidly switched. However, if I interleave the measurements in a single loop, so that the poor old MUX is swapping between inputs for every analogRead(), then I do get a small (0.1 LSB) error when reading 2 different voltages, just as you would expect, and this error is eliminated with a 1 mS delay.

Bottom line is that the 1 mS delay is not necessary in most practical situations, which is good news, because it means I can remove the delay and take more samples per second.

Glad to hear that. Had you said "different inputs" then I would have agreed that there might be a slight settling time as the MUX adjusts to the new channel. In fact the datasheet mentions it.

Not every claim or code example you find is correct, which is one of the reasons I challenge it, otherwise this might be yet another case of someone saying "oh I read it on the Arduino forum".