• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Why ADC/1024 is correct, and ADC/1023 is just plain wrong!

Mr RB

Well-Known Member
Thread starter #1
It's common to need to scale a microcontroller ADC result to give a value in some other scale. One example is to scale the ADC to read in actual volts like read from 0.00v -> 5.00v.

People sometimes use ADC *500 /1023 as this gives a reading of 500 (5.00v) for the top ADC value of 1023.

Doing that math; *x/(n-1) or *x/1023 does perform a kind of rounding function, but it "fudges" the rounding and gives both scaling and rounding errors.

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).

To magnify and show the errors caused by *x/(n-1) this table shows a simple ADC that has 5 values. (This is just like a PIC ADC but has 5 possible ADC values, not 1024).

Code:
[b]First the incorrect *x/(n-1) math;[/b]
input		ADC	math		result	average		output
voltage		value	ADC*x/(n-1)		error		result
4.00-4.99	4	*5 /4		5.00	+0.50		5
3.00-3.99	3	*5 /4		3.75	+0.25		3
2.00-2.99	2	*5 /4		2.50	 0.00		2
1.00-1.99	1	*5 /4		1.25	-0.25		1
0.00-0.99	0	*5 /4		0.00	-0.50		0
Note that for the 5 possible ADC values, the output scale now has 6 values (0-5) and the value 4 can never occur! And although the average error might look to be nicely distributed +/- the centre of scale, the actual output result proves to be much uglier.

So if you had a slowly increasing voltage, the ADC would read; 0, 1, 2, 3, 5!!

The maximum error in real world use is 2v, because that very tiny change of 3.999v - 4.001v would cause an output change of 3v -> 5v or a change of 2v!

Code:
[b]Here is the correct scaling math *x/n;[/b]
input		ADC	math		result	average		output
voltage		value	ADC*x/n			error		result
4.00-4.99	4	*5 /5		4.00	-0.50		4
3.00-3.99	3	*5 /5		3.00	-0.50		3
2.00-2.99	2	*5 /5		2.00	-0.50		2
1.00-1.99	1	*5 /5		1.00	-0.50		1
0.00-0.99	0	*5 /5		0.00	-0.50		0
All output values are properly scaled and all are represented. The average error is never greater than 0.5 (no more average error than the /(n-1) example), and the average error is always one fixed value (-0.5) making it very easy to compensate (see below).

The maximum error at any time is 1v, this is half the max error of the /(n-1) example, which can introduce extra error up to 1 in high value ADC readings.

Understanding the *x/(n-1) problem.

The problem with /(n-1) is that it corrupts the SCALING of data. Because it forces the top ADC unit value to be the top of the scale the scale no longer is an accurate conversion of data.



This corruption means the true scale of 1024:5 is being represented as 1023:5 so the data on the output is now larger than life;



However if the correct scaling is used; ADC*5/1024, then any input data is correctly represented at the output;



All data is rounded down to the nearest ADC unit, so all the output data is correctly scaled but will have an average error of -0.5 ADC units.

This error is actually a property of the ADC module hardware AND the ratio scaling math. This is because the ADC hardware rounds all voltages down to the nearest ADC unit, and because the integer division (/1024) also rounds data down. (More on compensating for this later).
 

Mr RB

Well-Known Member
Thread starter #2
Understanding ratios and ratio math!

A ratio is one scale compared to another. The result can always be calculated as *x /y (or *y /x) and will always appear as a linear line on a graph.

Forget ADCs for the moment and let's look at ratio scaling two other real world scales. For instance scaling Hz to RPM. The ratio is known; 1 Hz = 60 RPM, so the ratio is 60:1 and we can convert Hz to RPM by doing the math; Hz *1 /60 = RPM.

This math will give perfect scaled conversion of Hz to RPM. Note that there is NO correct solution using /(n-1) and *1 /59 will NOT properly convert Hz to RPM!

With a PIC ADC module there are exactly 1024 ADC units (numbered 0-1023) for every 5v. The ratio
to calculate voltage from ADC units is 1024:5 and that ratio is performed with this math; *5 /1024.

This ratio can be shown as a graph;



If the ADC happens to have 11bits (has 2048 ADC units) and runs from 10v the ratio remains the same, as the relationship of ADC units to voltage is still 1024:5.

Doing the incorrect math of ADC *5 /1023, gives ONE advantage, it means that the top ADC reading of 1023 will give an output result of "5v". The problem is that doing this kludge in code gives incorrect scaling of ALL the ADC data by adding more +error to the data as the values increase;



In some cases you may be able to tolerate this error, if you don't mind the two problems;
Problem 1. Higher values are always rounded up, lower values are always rounded down.
Problem 2. The output data (waveforms etc) are shown to be larger than reality (scaling error).

Doing the ratio scaling math right!

First let's clear one thing up, the ratio scaling math of *x /n is perfect, it has no error.

But there are two rounding down errors that we need to deal with;

1. The ADC hardware itself causes a rounding down error. Every possible input voltage is reounded down to the nearest ADC unit. This error occurs BEFORE any ratio math is done, and can be compensated by adding 0.5 ADC counts to every ADC sample BEFORE any conversion.

Since it is impossible to add 0.5 in integer math the best way is to double the ADC value, and then just add 1, ie; (ADC*2)+1

2. The math *x /n does not introduce error with the *x but the /n operation using integer math causes a rounding down error to the nearest n unit. This integer division rounding down error can be compensated by adding half the divisor; +0.5n /n which in our case is +512 /1024.

However since we have the ADC value already doubled from the previous operation, we need to divide by double, or 2048. So it becomes; +1024 /2048.

Putting it all together.

To get a reading of 0.00v to 5.00v from the PIC ADC can be done using the correct data scaling of all samples, and properly compensated integer rounding on all samples, by the following integer math;

((ADC*2)+1) *500 +1024 /2048

Using *x of *500 means we are converting 1024 ADC units to 500 output units (which represent 0-500 ie 0.00v to 5.00v).
 

Nigel Goodwin

Super Moderator
Most Helpful Member
#3
Nice, and WELL worth posting here (I'll make it a 'sticky' - oops! - already done), but it doesn't solve the problem with the last digit of resolution, which won't vary smoothly, dependent on the actual scaling ratio.
 

Mr RB

Well-Known Member
Thread starter #4
Thank you Nigel and Ian Rogers for making it a sticky. :)

Re the last digit, of course you are right that the last digit won't vary that smoothly dut to the difference in size of the input integer units and output integer units. It's not so bad converting 1024 -> 5.00v but is quite noticeable converting 1024 -> 5.000v as for every increment in the ADC value the output value last digit goes up by roughly 5!

But with the rounding handled correctly, the last digit SHOULD represent the best possible choice (ie; the closest digit to the perfect result). Or at least gives the best choice possible from that simple math done at that low integer resolution.
 

hugo

New Member
#6
Hello Roman,

This info is great thank you for sharing ...
So let's take a clasic example of reading an analog temperature sensor LM35 10 mV output 'C.
The c code ( mikroc ) I use is this :

Code:
unsigned long temp; 

temp = Adc_Read(0);
temp = (temp*500);
temp = temp >>10;
How would be the right way to rewrite it ?

Thank you
 

Mr RB

Well-Known Member
Thread starter #7
Well with a LM35 at 10mV per degree C, there will be 500 degrees C for a full ADC scale of 5v.

So as you showed, the math would be; degreesC = ADC *500 /1024.

If you wanted to fix the two rounding issues you could do it like this;

Code:
unsigned long temp; 
temp = Adc_Read(0);
temp = (temp*2) +1;        // compensate ADC hardware rounding
temp = (temp*500) +1024;   // is *500 and compensate /2048 rounding
temp = temp >>11;	   // finally /2048
And if you wanted to simplify that code, the ADC hardware rounding does not need to be done as a separate multiply, it can be done as part of the existing *500 multiply.

Basically instead of adding the 0.5 to the ADC valuebefore the multiply, it can be added after the multiply by adding half of the multiplicand;

Code:
unsigned long temp; 
temp = Adc_Read(0);
temp = (temp*500) +250 +512;  // is *500 and adds both conpensations
temp = temp >>10;	      // finally /1024
(ADC*500) + 250 is the same result as;
(ADC+0.5) *500

As the scale remains fixed for this app (ie we ALWAYS multiply by 500) then simply adding 250 will be fine.
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
#8
Hello Roman,

This info is great thank you for sharing ...
So let's take a clasic example of reading an analog temperature sensor LM35 10 mV output 'C.
The c code ( mikroc ) I use is this :

Code:
unsigned long temp; 

temp = Adc_Read(0);
temp = (temp*500);
temp = temp >>10;
How would be the right way to rewrite it ?

Thank you

Nothing wrong here... That's the way I do it.. The thing Roman is saying... You'll never display 5.00v with this equation ( linear result ) ... 4.995 will be the max.
 

jpanhalt

Well-Known Member
Most Helpful Member
#9
Here's another discussion: http://www.edaboard.com/thread203325.html . Apparently, not all ADC's use equal steps. I liked the term, "pious fraud." Start around post #10.

For those who seldom check links, here is the quote from the ADC0808 datasheet (page 5):

The bottom resistor and the top resistor of the ladder network in Figure 1 are not the same value as the remainder of the network. The difference in these resistors causes the output characteristic to be symmetrical with the zero and full-scale points of the transfer curve. The first output transition occurs when the analog signal has reached +½ LSB and succeeding output transitions occur every 1 LSB later up to full-scale.
John
 

Mr RB

Well-Known Member
Thread starter #10
Thanks for the information, that is good to know!

From what I understand the PIC ADC uses successive approximation based on binary steps so there are 1024 equally sized steps, at least that's how it was always represented in the midrange reference manual etc. If they had changed to some non binary-sized ADC steps then Microchip would likely have made some type of fuss about it in the documentation? That's the kind of thing users would really need to be informed about.
 

be80be

Well-Known Member
#11
Thanks RB I have done this both ways always wondered why the reslults where better using the 1024 now I know.
 

Mr RB

Well-Known Member
Thread starter #12
...
For those who seldom check links, here is the quote from the ADC0808 datasheet (page 5):

"The bottom resistor and the top resistor of the ladder network in Figure 1 are not the same value as the remainder of the network. The difference in these resistors causes the output characteristic to be symmetrical with the zero and full-scale points of the transfer curve. The first output transition occurs when the analog signal has reached +½ LSB and succeeding output transitions occur every 1 LSB later up to full-scale."
...
Thanks again for that John, I have been reading up from a few different sources, and have basically found two ways they implement the ADC transfer function.

PICs (earlier ones) have always used the "ideal ADC" model with 1024 equally sized steps. From the TI ADC math white paper a second type is sometimes used, called a "ideal ADC with 1/2 LSB offset".

Basically they move the entire scale down 0.5 bits, so the bottom bit is 0.5 LSB in size, then 1022 normal LSB sized bits, then the top bit is 1.5 LSB in size. This preserves the /1024 relationship so important for accuracy (since most of the industry uses 4.096v or 2.048v references to give exactly 0.004v/bit or 0.008v/bit).

Microchip shows this in some of their newer datasheets, but unfortunately most of their ADC transfer function charts shown in their datasheets have typos and errors.

The reference manual for PIC 24 series explains the transfer function for 10bit mode as having 0.5 LSB at the bottom and 1.5 LSB at the top, but on the next page the 12bit transfer function has a bad typo saying there is 0.5 LSB bottom and 0 LSB top, which means it would never reach the top count.

One PIC 24 datasheet I have clearly shows the transfer function is a typical "ideal ADC" and has 1 LSB bottom and 1 LSB top and no offset.

Some of the PIC 18F datasheets seem to be saying there is 0.5 LSB bottom step and 0.5 LSB top step, which again I think is a typo because there are usually other mistakes in their transfer function diagram, I think the guy who makes the diagram did not know how it worked?

If I get some time I will cut and paste a few transfer diagrams into this thread.

Regarding the ADF0808 quote supplied by Jpanhalt above;
"...The first output transition occurs when the analog signal has reached +½ LSB and succeeding output transitions occur every 1 LSB later up to full-scale."

That describes an ideal ADC with the added 0.5 LSB offset, which would have a bottom step of 0.5 LSB size, and top step with 1.5bit size, like some of the newer PICs claim.

All that does is remove the 0.5 LSB average rounding down produced by the typical ADC hardware. So if using one of those you don't need to do the first rounding compensation as it is already fixed in the ADC hardware.
 

jpanhalt

Well-Known Member
Most Helpful Member
#13
Yes, I went to the Analog Devices data sheets before posting and checked several. The "mechanics" are described subtly in some and not at all in others. It is just something worth considering.

John
 

Mr RB

Well-Known Member
Thread starter #14
Same with the PICs! Many PIC datasheets don't show the transfer function at all, so people are left guessing. And the ones that do show it don't inspire confidence in the chart.

The worst errors are the ones that show a PIC ADC with 0.5 LSB bottom bit and 0.5 LSB top bit, which would give a full scale size of 1023 bits, but it does not make sense that Microchip would make an ADC do that because 1; it's not an industry standard in ADC operation, 2; each bit would be 1/1023 in size so all the precision voltage references would be useless (and Microchip also makes these references!) and 3; this distributes the rounding error +/- of the centre scale.
 

misterT

Well-Known Member
Most Helpful Member
#15
Same with the PICs! Many PIC datasheets don't show the transfer function at all, so people are left guessing. And the ones that do show it don't inspire confidence in the chart.

The worst errors are the ones that show a PIC ADC with 0.5 LSB bottom bit and 0.5 LSB top bit, which would give a full scale size of 1023 bits, but it does not make sense that Microchip would make an ADC do that because 1; it's not an industry standard in ADC operation, 2; each bit would be 1/1023 in size so all the precision voltage references would be useless (and Microchip also makes these references!) and 3; this distributes the rounding error +/- of the centre scale.
If you move all bits down 0.5 LSB, you end up with 1023 "full" bits and two "half" bits (or steps). One bit is still 1/1024 of the full scale (10bit ADC). I think this kind of ADC is quite standard and it is also statistically correct way if you compare it to how histograms are calculated correctly.

Your two original posts support this. With 5 volt reference you never reach exactly 5V result.. it would require transition from 1023 to 1024. This would be the transition to the 0.5 LSB top bit. But you never get there with 10 bit ADC, so the max. result you can get is 4.995V. That result is centered to the last full bit (1023). The remaining 0.5 LSB top bit is centered at 5V.

Centering the first bit around zero is important if you want to call it 0V with +/- 0.5 LSB error.

In your first (correct) example you say that max error at any time is 1V.. but, if you center the first bit around zero, then the max error at any time is 0.5V.

0V is the actual reference voltage. All measurements are relative to 0V. Vref (5V in the examples) just sets the resolution. When you choose Vref to be as close as possible to your maximum signal voltage, then you get best performance out of your ADC.
 
Last edited:

NorthGuy

Well-Known Member
#16
It all would be important, but unfortunately all ADC have considerable error - 10 to 20 counts, sometimes much more, plus the error induced by the circuicity. Getting the last 1/2 bits right doesn't improve accuracy in this case.

In most cases, you can improve accuracy by calibration or auto-calibration. When you do that, you assume that there are large systematic gain and offset errors, and measure them with ADC and then scale your results using reference measurement. In this process it is only important that the references are measured and processed the same way as real measurements, because all the linear errors will be corrected anyway.
 

misterT

Well-Known Member
Most Helpful Member
#17
It all would be important, but unfortunately all ADC have considerable error - 10 to 20 counts, sometimes much more, plus the error induced by the circuicity. Getting the last 1/2 bits right doesn't improve accuracy in this case.

In most cases, you can improve accuracy by calibration or auto-calibration. When you do that, you assume that there are large systematic gain and offset errors, and measure them with ADC and then scale your results using reference measurement. In this process it is only important that the references are measured and processed the same way as real measurements, because all the linear errors will be corrected anyway.
Of course there are many different error sources. I was talking about "ideal" design of ADC. I thin MrRBs examples are wrong because the first conversion result 0, is not centered around 0V. MrRB makes a valid point why dividing by 1023 is wrong and 1024 is right, but the "simple" examples are not quite right.
Of course the situation is different if he is talking about ADC that is designed so that the first bit is not centered around 0V. But that kind of design is very strange on my opinion.
 

MrAl

Well-Known Member
Most Helpful Member
#18
Hi,

Hey it's interesting that this thread got revived somehow. It's been a long time since i looked at this myself but i remember talking about something like this several years back.

More recently however i have read in the Atmel data sheet that the ADC range is 0 to Vadc minus 1 LSB. So they do mention something about this.

Also, bias the analog ground to -2.5 millivolts. Now everything reads 1/2 LSB higher :)
 
#19
Many thanks to Mr RB. It's very useful. But I found that the ADC math is not quite universal. I think I comprehend it well - with your explanation. But problem comes when you want a scale (your x (in ...*x/n)) greater than 1024 (in examples here). In the case with larger scale (x) your max. value (>1024) goes down and min. value (zero) goes up.
E.g. we can choose a scale x=5000 and then we are able to view only values from 2 to 4098 ( and we want 0 to 5000).
Is there a possibility to solve it right?

BTW: There is a mistake in an expresion in 2nd post (and nobody alerts to it):
((ADC*2)+1) *500 +1024 /2048
because there is an example of it like "(((ADC*2)+1) *500 +1024 )/2048" in next posts (in code example frames) which is right (but accurate only to 1024 scale).
 

MrAl

Well-Known Member
Most Helpful Member
#20
Hi,

Unfortunately MrRB left the forum a while back for some reason, so it's doubtful if he will reply.

If you want to give an example we can talk about it. I dont see what you mean by scaling by 5000, do you mean divide that by the actual ADC count, like 5000/1023 for example?

With a 5v reference the max truly readable voltage i think is 5*1023/1024 which equals 4.9951171875 in decimal.

If we used a programmatical scaling factor of 5000, then we would convert an ADC count of 1023 to:
5000*1023/1024=4995.1171875 which rounds to 4995.
 
Last edited:

Latest threads

EE World Online Articles

Loading

 
Top