Go Down

Topic: analogRead - divide by 1023 or 1024? (Read 38367 times) previous topic - next topic

nickgammon

Feb 24, 2015, 01:33 am Last Edit: Feb 24, 2015, 01:34 am by Nick Gammon
Following on from this thread I've been trying to get my head around converting the result from analogRead to a voltage.

Let's suppose:

Code: [Select]

int value = analogRead (A0);  


And imagine we get the value of 1000.

Now (ignoring the fact that integers don't have decimal places) is the voltage (assuming we have a 5V reference):

Code: [Select]

 1000.0 / 1024 * 5.0 = 4.8828125

or:

 1000.0 / 1023 * 5.0 = 4.8875855


In other words, divide by 1023 or 1024? I should point out that dividing by 1024 immediately gives a "wrong" answer for measuring 5V:

Code: [Select]

 1023.0 / 1024 * 5.0 = 4.9951171


This is because the maximum reading is 1023.

Browsing the web:




http://arduino.cc/en/Tutorial/ReadAnalogVoltage

Quote
To scale the numbers between 0.0 and 5.0, divide 5.0 by 1023.0 and multiply that by sensorValue
One vote for 1023.




http://forum.arduino.cc/index.php?topic=200447.0

Multiple schools of thought, including:

You divide by 1024 if you are a mathematician or if you truly understand ADC successive approximation.



http://blog.codeblack.nl/post/NetDuino-Getting-Started-with-ADC.aspx

Hedging his bets:

Quote
The number 0 represents 0V and 1023 represents Aref. The voltage level at any ADC port can be determined as follows:
 
Code: [Select]
float analogValue = (float)digitalValue * (3.3f/ 1023f)

In theory, you should divide by 1024, because there are that many steps.



http://forums.netduino.com/index.php?/topic/2216-adc-value-divide-by-1024-or-1023/

Different opinions:

Quote
The ADC returns a value from 0 to 1023 not 0 to 1024. 0 to 1023 is 1024 different values because zero is a value too. ... That is why you divide by 1023 and not 1024.
Quote
Not exactly.
Consider the nominal "width" of the ADC reading: it is 1024. That is strictly related to Vref (typically 3.3V).
Now we must slice that segment in many smaller parts: in total are 1024 "tiles" (if you like numbered from 0 to 1023).



http://forum.arduino.cc/index.php?topic=252276.0

The divisor (1023.0) in that tutorial is wrong.  The correct value is 1024.0...

Code: [Select]
float voltage= sensorValue * (5.0 / 1024.0);

The maximum value returned by the analog-to-digital converter (1023) is correct.



http://www.electro-tech-online.com/threads/why-adc-1024-is-correct-and-adc-1023-is-just-plain-wrong.132570/

Quote
The correct scaling math; *x/n or *x/1024 correctly scales all the output data in size, but gives an average rounding error of 0.5 ADC counts (always rounding down).



So, how come we get a "wrong" result for a value of 1023?

It seems that the ADC can return 1024 "slots" of values (from 0 to 1023) where each slot represents 1/1024 of the reference voltage.

So for a 5V reference voltage the slot width is:

Code: [Select]

 5 / 1024 = 0.0048828125V


Thus a result of 0 could be: 0V to 0.00488V
A result of 1 could be: 0.00488V to 0.009766V
...
A result of 1023 could be: 4.99511V to 5V

So really the "wrong" result for 5V is more that the voltage is not necessarily 5V, it could be 4.995.

In addition to that, the hardware rounds down the result, so you could compensate by adding in an average error of 0.5.

Code: [Select]

 1023.5 / 1024 * 5.0 = 4.99756V


So this is reasonably close to 5V. Similarly a reading of 0 should probably be treated as possibly halfway between 0 and 1, thus it could on average be:

Code: [Select]

 0.5 / 1024 * 5.0 = 0.002441V





Oh yes, and the Atmega328 datasheet mentions in the section "23.7 ADC Conversion Result" that the figure is 1024.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

RayLivingston

Personally, I would say D) None of the Above.  If you're looking for that kind of accuracy, calibrate it by putting a precisely known voltage on the input, and seeing what readings you get.  You're almost certain to get at least one bit of "dither" as well, as you saw in the that test you ran in another thread yesterday.

Regards,
Ray L.

Peter_n

#2
Feb 24, 2015, 02:42 am Last Edit: Feb 24, 2015, 02:42 am by Peter_n
Nick, what is your verdict ?

When an analog input is clipped to 5.000V, I would like to have 5.000V in my calculation. So I use 1023. I read that the ADC successive approximation requires 1024. However, I didn't think of the 0.5 bit rounding down, and I don't want it to be rounding down.

Talking about the last bit does make sense to me. With many samples for the average, it is possible to get more "relative" accuracy than 10 bits.

runaway_pancake

Any division of an ADC represents a range, not one particular voltage.
An analogRead that returns a "0" doesn't mean 0V but 0 to around 4.89mV

Refer the chart attached, sorry my rounding got a little sloppy.

"Who is like unto the beast? who is able to make war with him?"
When all else fails, check your wiring!

TomGeorge

#4
Feb 24, 2015, 02:47 am Last Edit: Feb 24, 2015, 02:48 am by TomGeorge
Hi,

The way I see it is that you have 1024 levels, GND is zero, so 1023 will be FullScale.
There are only 1023 steps.
You are counting quantization steps not the resulting levels.
Zero is a number.
Tom...... :)
Everything runs on smoke, let the smoke out, it stops running....

Peter_n

Thank you Runaway Pancake.

Then this is what I want (adding half a bit, to avoid rounding down)
float voltage = ((float) rawADC  + 0.5 ) / 1024.0 * 5.0;

The voltage would never be 0.0 and never be 5.0

nickgammon

Nick, what is your verdict ?
First, let's go back to the datasheet:

Quote
0x000 represents analog ground, and 0x3FF represents the selected reference voltage minus one LSB.
So (contrary to what you might expect) 0x3FF (ie. 1023) does not claim to represent Vref, but rather the reference voltage minus one bit (which in the case of 5V is 5/1024 = 4.88 mV).

Thus, the fact that dividing 1023/1024 and multiplying by 5 does not give 5 is entirely consistent with the fact that it does not claim to represent Vref.

The datasheet goes on (on the previous page):

Quote
Quantization Error: Due to the quantization of the input voltage into a finite number of codes, a range of input voltages (1 LSB wide) will code to the same value. Always ±0.5 LSB.
So there is an expected error of +/- 2.44 mV.


Therefore I agree with Peter_n's formula:

Code: [Select]

float voltage = ((float) rawADC  + 0.5 ) / 1024.0 * 5.0;
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

One of the reasons I was wondering this was that in the other thread my observations were slightly different to the calculated value, namely:

Code: [Select]

Analog port = 14, average result = 508    // 2.5V input
Analog port = 15, average result = 1022   // 5V input
Analog port = 16, average result = 672    // 3.3V input


Using the formula in the datasheet we would expect 2.5V input to give:

Code: [Select]

 2.5 * 1024 / 5 = 512


However I got 508 above.

So I checked the 5V pin with a meter and found it read 5.026V. Substituting now:

Code: [Select]

 2.5 * 1024 / 5.026 = 509.3


Much closer to the observed output (508). And since we can also factor in a 0.5 bit quantization error, that brings it down to 508.85 which, truncated, now agrees with the reading I got.

Similarly for the 3.3V input (actually measured to be 3.306V) so now we have:

Code: [Select]

 3.306 * 1024 / 5.026 = 673.57


Again that is pretty close to the program output (well, out by one).
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Coding Badly

Quote
Therefore I agree with Peter_n's formula:
If we're being pedantic, Peter_n's formula is incomplete.  @Runaway Pancake nailed it.  For a given ADC value a range of voltages is possible.

Code: [Select]
float voltageLo = ((float) (rawADC  + 0) ) / 1024.0 * 5.0;
float voltageHi = ((float) (rawADC  + 1) ) / 1024.0 * 5.0;


Where the measured voltage is greater than or equal to voltageLo and less than voltageHi.  (With successive approximation converters we can never tell that we've reached exactly the voltage reference.)

Including an offset (0.5 in Peter_n's formula) can cause serious problems for some applications; like PID control.  It is assumed that an ADC value of zero is close enough to zero to be considered zero.  If that assumption is not true a converter with more resolution is needed.

In other words, if adding an offset solves a problem the problem is much better solved with a better converter.


Coding Badly

#9
Feb 24, 2015, 03:43 am Last Edit: Feb 24, 2015, 03:44 am by Coding Badly
One of the reasons I was wondering this was that in the other thread my observations were slightly different to the calculated value, namely:
Some things I've noticed when trying to get very precise readings from an AVR based Arduino...

• USB power can be electrically noisy

• USB voltage can vary significantly with minor load changes

• Performing an analog conversion can cause the USB voltage to vary skewing the reading

• The AVR processor is electrically noisy which can affect a conversion; using the ADC Noise Canceler can make a significant difference

• A nearby pin outputting a PWM signal can wreak havoc on a conversion

• Something like this... http://www.adafruit.com/product/2200 ...plus noise cancelling gives very good results even when the board is USB powered

• When powered from USB and not using noise cancelling ±2 bits is about as good as it gets

• The 3.3V regulator makes a fairly good reference

• Simple averaging improves the accuracy


nickgammon

I mentioned above that each reading represented a range, but I agree it is worth being aware of what you mentioned. As an approximation (eg. to display the temperature) "going halves" and assuming that it is in the middle of the range is probably an OK assumption.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

I re-ran the test in the other thread, also measuring against Gnd, and always got a reading of 0, so it is interesting that I got 1022 (not 1023) for 5V, but Gnd was correct.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

michinyon

If it is any consolation,  this has been bugging me for 30 years and I am still not convinced.

A idealised ADC converter would have an output which,  if you plotted it on a graph as a function of output versus input,   would be a straight diagonal line.

An actual adc output looks like the side view of a staircase.     The theoretical diagonal line might be touching the top corners of each step,   or the bottom corner of each step,   or slicing through the middle or each tread and riser.    

Also,  the top or bottom step might be the same width as the others,  or only half the width.    Also,  the staircase might end at the top end with a vertical step rather than a horizontal one,    which might be incapable of being represented in the output available.    After all,   there is no really good reason why 1024 could not be a valid output for 5.0 V,   it is a 16 bit number after all.

If I was to answer this question,  I'd get a very stable adjustable voltage source,   and actually measure the behaviour of the device.   Is the bottom step a full step wide,  or only half a step wide ?  And so on.   Once I had deduces the apparent position of the staircase, relative to the nominal diagonal line,   then you are in a position to map the adc outputs back to the nominal voltage,  represented by the midpoint of each step.






nickgammon

#13
Feb 24, 2015, 04:29 am Last Edit: Feb 24, 2015, 09:46 pm by Nick Gammon
Well this is quite amusing. I tried to do it with noise cancelling (by going to sleep).

Code: [Select]

#include <avr/sleep.h>

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  }  // end of setup

const int ITERATIONS = 500;
unsigned long totals [6];

// when ADC completed, take an interrupt
EMPTY_INTERRUPT (ADC_vect);

int takeReading (byte port)
  {
  ADMUX = bit (REFS0) | ((port - A0) & 0x07);  // AVcc
  
  // ensure not interrupted before we sleep
  noInterrupts ();
  set_sleep_mode (SLEEP_MODE_ADC);    // sleep during sample
  sleep_enable(); 
  
  // start the conversion
  ADCSRA |= bit (ADSC) | bit (ADIE);
  interrupts ();
  sleep_cpu ();     
  sleep_disable ();


  // reading should be done, but better make sure
  // maybe the timer interrupt fired

  // ADSC is cleared when the conversion finishes
  while (bit_is_set (ADCSRA, ADSC))
    { }

  byte low  = ADCL;
  byte high = ADCH;

  // combine the two bytes
  return (high << 8) | low;
    
  }
  
void loop ()
  {
  for (int whichPort = A0; whichPort <= A3; whichPort++)
    totals [whichPort - A0] = 0;
  
  for (int i = 0; i < ITERATIONS; i++)
    {
    for (int whichPort = A0; whichPort <= A3; whichPort++)
       {
       int result = takeReading (whichPort);
       totals [whichPort - A0] += result;
       }
    }

  for (int whichPort = A0; whichPort <= A3; whichPort++)
     {
     Serial.print ("Analog port = ");
     Serial.print (whichPort);
     Serial.print (", average result = ");
     Serial.println (totals [whichPort - A0] / ITERATIONS);
     }
    
  Serial.println ();
  delay (1000);
  }  // end of loop


Output:

Code: [Select]
Analog port = 14, average result = 506
Analog port = 15, average result = 1022
Analog port = 16, average result = 670
Analog port = 17, average result = 0
(repeated)


Those figures are noticeably lower than before (506 compared to 508, and 670 compared to 672). But! During sleep mode, the load on the 5V bus decreased, and the 5V pin jumped to 5.028V. So recalculating with the new reference voltage gave similar answers to before.

This would appear to strongly support Coding Badly's suggestion of using a proper reference voltage, as clearly the 5V voltage isn't really 5V.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

christop

I support 1024 as it's mathematically "clean". The ADC gives integer results in the half-open interval [0, 1024) which map to the half-open interval [0, Vref).

Go Up