Arduino Nano ADC

Despite the many articles and videos about the arduino ( nano ) AD converter,
I still have trouble getting the exact values which I expect.
I don't want to use the 5 volt ref. Or internal 1.1 v ref or the 3.3v ref.
Instead I want to use a 1.024 v EXTERNAL ref in order to measure 0 - 1023 mV in 1 mV steps.
I take the 3.3v out, with a resistor and a potentiometer to set the 1.024 v. for the AREF
This wordks reasonable accurate, within 1 mV.
The sketch is very simple. All I do is:
pinMode (A0, INPUT);
analogReference(EXTERNAL);
and I initialise the serial communication.
In the loop I do an ANALOG_In =analogRead(A0);
I use a PM2480, as a very accurate voltage source, even below 1 mV.
Here is the result as shown in the serial monitor:
Input: Reading:
1000 mV 1000
500 = 498
100 = 96
50 = 46
20 = 16
10 = 6
I think it's quite a deviation below 100mV.
Am I too demanding or is this to be expected ?

You are at the edge and noise of any type will give you problems. Be sure everything is well bypassed and avoid long wires.

Remember that any A/D converter only gives you a reading which is +/- the least significant bit. Then there is the noise to consider.

I would think it is what is to be expected from an A/D built into a noisy digital chip.

About the reading being +/_ the LSB:
That is true, but sine the reference is 1024 mV, with 10 bits
the LSB should be only 1 mV.
About the noice: I have decoupling capacitors about everywhere:
Directly connected on the power terminals of the nano board
Also 1 on the reference input and 1 on the 3.3v output.
Besides, I noticed that a 2k7 resistor before the AN0 input and
a 1 - 10nF between AN0 input and ground reduces noise dramatically:
without, the measured value jumps at least +/- 5 counts,
with this low pass filter, applying 800 mV the reading is steady at 800.

You have a zero offset error.

image

PM2480 - lovely piece of kit

You are not too demanding, but you have not calibrated the ADC for zero offset.
Which is why you should show all your code.

I guess you havent read this:

1 Like

Does data sheet mention the range of external voltage that can be connected with VREF pin of the ADC via AREF-pin? Can we connect 512 mV to get a resolution of 0.5 mV?

Though the data sheets does not say anything in this regard, I would not connect less than 1.1V at the AREF-pin. The range is: 1.1V to 5V.

GolamMostafa, now that is the question I wanted to ask.
I have really no idea if the ref can be as low as. What... anything ?
I might considder a ref of 2.048 v to measure in steps of 2 mV

johnerrington: you asked for all my code ? Well here it is.
Hardly more than I mentioned in my initial post.
Let me say it's a"First attempt" to get things tested.
I used an int for ANA_In, but using a float instead makes no difference
int ANA_In;
void setup()
{
Serial.begin(115200);
pinMode (A0, INPUT);
analogReference(EXTERNAL);
Serial.println("Set-Up Done");
}
void loop()
{
ANA_In =analogRead(A0);
Serial.println(ANA_In);
delay(1000);
}

Dividing off the 3.3v output won’t be as accurate /stable as the internal reference . As mentioned the A/D may not be accurate at very low voltages ( see the 328 spec)
When ever you use the A/D circuit it’s best to have a calibration routine as there will be resistor tolerance , reference voltage etc - it’s not a DVM out of the box !
You can write a routine where you type in the voltage value that is connected and use the map function to adjust the scaling ( preferably a hi and lo value .
( you maybe able to change values in registers ??).
a/D optimised for impedances <10k
Stick with integers !!

GolamMostafa,
I tried right away with 2.048v REF.
So now the range is 0-2047 mV step 2 mV
Applying 800mV should show 400 and it does.
Now I multiplied the value with 2 and you know what ?
The result is EXACTLY the same as mentioned in my first post.
So I think 1024 mV is not too low as reference.

Hammy: I think stability is not the problem. The measured values
in all cases are very stable. Also the reference itself is stable.
I think the accuracy at low voltages may be the reason.
You are writing about modifying registers and scaling ??
For a relative beginner like me that is a bit too much asked.
I can hardly write 1 line of code in C without errors... :grinning:

Data sheets tells about 1.1V for the VREF pin. If you go below it, the measured values exceeds the allowable +/- 2LSB error which is due to noise pick up as pointed in post #2 @gilshultz. So, 2.048V is a good choice for VREF pin , which gives a resolution of 2 mV.

Use code tags (</>) while posting codes:

int ANA_In;

void setup()
{
    Serial.begin(115200);
    //pinMode (A0, INPUT);     //no need; A0 is automatically input in ADC mode
    analogReference(EXTERNAL);
    Serial.println("Set-Up Done");
}

void loop()
{
   ANA_In = analogRead(A0);
   Serial.println(ANA_In);
   delay(1000);
}

Reading the attached file might be worth.
Ch-5ADCLec.pdf (687.0 KB)

1 Like

Yes it has something to say and no you can't connect it to 512 mV.
Note the bottom line of this screen shot of the data sheet.

1 Like

Well, regarding the reference: 1024 mV is therefore (just) within the range.
The datasheet also says something about the absolute accuracy.
Can the ADC clock be adjusted in SW ? And can noise reduction be set ON/OFF ?
Maybe this way the result can be improved ?

Yes it can be both speeded up and slowed down by adjusting the prescaller register. If you go too fast you loose accuracy.

What noise rejection is this then? How do you distinguish between noise and fast signal. The more filtering you apply to the input the lower is the noise, but you pay for that in not being able read fast signals. Like most things in life electronics is full of trade offs between two or more variables.

1 Like

As johnerrington said "You have a zero offset error." It seems about 4mV.
A shared ground via a breadboard could do this (ground voltage offset).

A Nano v3 steals 3.3volt from the USB chip, and is therefore not very stable.
But that would affect the whole scale, not just zero.

If you want an exact millivolt scale, then try a 15-bit ADS1115 breakout board.
Get it from Adafruit or Sparkfun, because all others most likely sell fake.
Leo..

3 Likes

What type of input signal you have for the ADC? Is it unipolar and slowly varying - a picture would illustrate nicely? Why do you want to change the frequency of the conversion clock from the default 125 kHz?

If you hope to get exact readings accurate to 1mV you are expecting too much. Firstly it is beyond the spec of the ADC

ATmega48A/PA/88A/PA/168A/PA/328/P
205ATmega328P [DATASHEET]
7810D–AVR–01/15
23. Analog-to-Digital Converter
23.1 Features
â—Ź 10-bit resolution
â—Ź 0.5 LSB integral non-linearity
● ±2 LSB absolute accuracy

you can find explanations for those terms on the data sheet.

the data sheet gave no further details, so we can assume they apply equally to the DEFAULT and INTERNAL references.

So the worst case error is 2LSB or (with a reference of 1024mV) 2mV.

Second - the 3V3 is NOT a reference - its just provided by a voltage regulator. I measured it as 3.360V and agsin a little later at 3.351V but there is no guarantee it is accurate, noise free or stable. So your 1024mV "reference" can not be relied upon for better results than you would get by using the "INTERNAL" reference.

I checked the reading with A0 grounded and read zero; but to measure the zero offset you would need (as I have above) to graph Vin vs Vreading.

1 Like

Hi, @rawwar
First;
To add code please click this link;

It will place your code in a readable scrolling window.
Also before posting your code press, [ CTRL ] [ T ] to format your code to make it easier to read.

I do think you are expecting too much, you need a Precision ADC, these are stand alone ICs and you connect them to the controller.
They are usually a bit more expensive than a 328 IC.

Why do you need this sort of performance?
What is your application?

Can you please tell us your electronics, programming, arduino, hardware experience?

Thanks.. Tom.... :smiley: :+1: :coffee: :australia:

Hi TomGeorge,
uploading code: I'll do better next time. I was attended to it but I thought "Hey, 10 lines of code..."
"About me", is a little bit of topic but in short:
I am 66, finished technical school in 1978 and worked in electronics ever since. It is also my hobby.
From the very beginnig I programmed in assembler, for the 8085, 6502, 6809, 80c32, 80c535 / 552 and sometimes a little in BASIC. I am addicted to those "old school" processors. Love them.
Have a bunch of them in the drawer.
More or less forced towards arduino, since programming the BME280, nRF24 etc with all the available libraries should make it easier, faster and al lot smaller. I don't have the time to develop all this code for the 80c32 type processors. However I have no knowledge of C, so it is a struggle sometimes. Most of the time copy/paste and try to adapt the code, making 3 errors in 1 line....

And now again about my "chalange" I think I should indeed go for a higher resolution ADC.
Or accept it as it is. I'II think about it a bit more.
I have some tmp36 temp. sensors. They measure 0.1 degree C in steps of 1 mV. Since they give 750mV at 25 C, in my case it can measure accurate, it is not likely that I will ever come close to 50 mV or even below. So the accuracy at less than 50 mV is not THAT important. Just tried...
I saw youtube videos by people who managed a temperatue reading, jumping somewhere between 19.6 and 21.8 degrees and I thougt are they serious ? I can do better than that. In case of a Nano I managed a very stable 0.2 degree stability but I tried to go for more. My nano gets its power from a 7805 and is exactly 5.008v And I have now used an external low drop 3.3 v stabiliser which gets the 5 v IN from the 7805 and is really stable as reference. Looks promesing..
I think I just go on experimenting but I think I got the most out of it by now.
But I do want to thank all the people who took the time to give me tips and advise etc.

2 Likes

Don't try to force the A/D into one step per mV for that.
Just use 1.1volt Aref, and calibrate Aref offset and TMP36 error in one go.
Try this sketch. The magic number comes from 100*(Aref/1024.0) = 0.1039 (actual Aref).
Then stomp on it and buy a digital temp sensor, like the DS18B20 (1/16th degree C).
Leo..

const byte tempPin = A0; // connect TPM36 to 3.3volt A0 and (not-shared) ground
float calibration = 0.1039; // calibrate temp by changing the last digit(s) of "0.1039"
float tempC;

void setup() {
  Serial.begin(9600);
  analogReference(INTERNAL); // use internal 1.1volt Aref
}

void loop() {
  tempC = (analogRead(tempPin) * calibration) - 50.0;
  Serial.print("Temperature:  ");
  Serial.print(tempC, 1); // one decimal place precision is all you get
  Serial.print(" C");
  delay(1000); // use a non-blocking delay when combined with other code
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.