11 posts / 0 new
Author
Message

Disclaimer:Thread title copied verbatim with no permission from owner.

I found the above post quite interesting, and not being a member of the above forum, I thought you may find it interesting and comment on it a bit.

I found it strange that anyone would divide by 1023 in the first place. Then in the second post I find this line.

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 disadvantages is also there. But the most interesting bit for me is the last heading at the end of the second post.

I'm quoting it all here highlighting what I what like to be lectured about.

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;

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

• The ADC causes rounding down .... I guess that is true for AVR as well as I suppose its the same type of ADC and nothing to be done about it, unless perhaps feed a slightly lower reference voltage?

• /n (divide by n) causes a rounding down in integer math ....  true.

• And then the final result, i.e. the formula in turquoise...  1024/2048 should always return 0 in integer maths? I am not so   sure if the formula is correctly printed there.
• I think it should read (((ADC*2) +1) * 500 + 1024) / 2048

Crickey, I've never even given it the slightest consideration when converting, and I doubt it will make a difference in most projects, but considering a perfect environment, will it actually make a difference?

Lets say your scale you convert to is like 100V, i.e. 25 times up, that could mean quite a bit of an error.

Or the other problem, with high resolution ADC's, I guess that would also cause inaccuracies if we go convert in the usual fashion.

What say ye? Is it something to consider?

Last Edited: Mon. Sep 12, 2016 - 10:08 AM

This is quite straightforward.   The correct scaling is ADCW/1024.  You would round the result.

Remember that every ADC has a real-world accuracy and every ADC will always have a suspect "least significant bit"

And the VREF accuracy has a tolerance.

So yes,  a human punter wants to see 5.00 instead of 4.99.   You provide the human-friendly display.

However,  you will never convince the "Moon is made of green cheese" believers.

David.

This rounding error depends on how the ADC is implemented and must be checked on the datasheet. For the ATMega 328P, for example, there is no rounding down, but round to nearest. The true value is between ± 0.5 LSB of the output code. I'll cite from the datasheet:

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.

If the ADC was rounding down, it would say the true values were "Always 0 to +1 LSB". In fact most successive approximation ADCs I know round to nearest.

In fact most successive approximation ADCs I know round to nearest.

Is that the same as just dropping off the fraction or does it get rounded in true style. I think the ADC will only output integers, i.e a value of 3.999 would come out as 3, no matter how many nines you add after the decimal.

I understand that an ADC step could be in a range of say 4.883mV to 9.766mV. Now as you ramp up the input, at 9.766 it could be the next bit as well. Or more simply put a value between 4.883m and 9.766mV is guaranteed to be 1, the closer to the middle the more guaranteed it becomes. LOL! Don't quote me on that though!

input voltages (1 LSB wide) will code to the same value. Always ±0.5 LSB.

Is'nt that the whole point of the formula? To ensure rounding to known value. The maths seems to add up and remember, I am talking ideal world.

Bergie5737 wrote:
El Tangas wrote:
In fact most successive approximation ADCs I know round to nearest.

Is that the same as just dropping off the fraction or does it get rounded in true style. I think the ADC will only output integers, i.e a value of 3.999 would come out as 3, no matter how many nines you add after the decimal.

Does 3 look like the nearest integer to 3.999 to you?

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Mon. Sep 12, 2016 - 06:56 PM

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

I knew there would be tons of debate on the 1023 vs 1024, but I am questioning the merit of the writer, especially the formula, which I later found I was correct in this form:

In fact most successive approximation ADCs I know round to nearest.

Ok, I accept that, so no need for the +1 in the formula? In fact then the whole formula falls away as its is supposed to take care of rounding down and for AVR it seems to round to nearest.

What you are saying in this case, in ideal circumstances my result won't be more accurate with the above formula. Right?

Your formula should do you fine.   Note that you need to use uint32_t maths to avoid overflow problems.  e.g.

`(((ADCW*2)+1) * 500uL +1024) /2048`

It will give you 0 when ADCW = 0 and 500 when ADCW = 1023.   It should keep most humans happy.

You might just as well say:

`ADCW * 0.4887586`

or

`ADCW * (500.0/1023)`

Quite honestly,  the f-p multiplication will not cost you much in time or flash.   It is all dependent on 500V creating an ADCW value of 1023.   This is dependent on your potential divider creating exactly the same voltage as your reference source.

In practice,  you might use a 1.1V reference, AVCC reference, external AREF reference, ...

Also note that any physical resistors might not be rated for 500V.

David.

Also note that any physical resistors might not be rated for 500V.

The post refers to 500 as 5.00V,  (to near deci) no chance of me so daft as to measure 500VDC intentionally! I did stumble upon the post as I was seeking a solution to get more accurate voltages for a 60V supply. I know I had a gain issue, but was unsure as to how to fix it. The attached post was quite significant as I never saw that discussion before. (I'm not talking about the plain 1023 vs 1024 debate, but a bit of a fresh approach worth reading.) So I am trying the arguments on a much more experienced and knowledgeable crowd than myself. If it improves accuracy for just a few extra overheads, which I usually have spare in my projects, then why not?

Once again, I doubt if any environment is so clean as to make this more worthwhile than oversampling and averaging. I wish someone with lab can post results so that the knowledge can be out there.

Just a bit off topic:

`(((ADCW*2)+1) * 500uL +1024) /2048`

I see a lot of old timers use the "U", "L" or in this case "UL" when using constants. Is this still a requirement with new compilers? I only use it in #define but don't want to get in a bad habit.

If you are measuring 5.00V, the scaling factor would be 0.004887586 or (5.0 / 1023)
.
The reason for my 500uL was to tell the compiler that I was using (uint32_t)500
500uL looks a lot cleaner.
.
if I do not do this 1023 × 500 = 511500 which is clearly going to overflow a 16-bit int.
Note that you don't need to cast every member of the expression. Just choose one that is going to be evaluated first. The compiler will then make sure that the other components are cast automatically.
.
David.
.
p.s. if you use a f-p scaling constant, you never need to worry about overflows and underflows.

Last Edited: Tue. Sep 13, 2016 - 10:26 AM

Bergie5737 wrote:

I knew there would be tons of debate on the 1023 vs 1024, but I am questioning the merit of the writer, especially the formula, which I later found I was correct in this form:

In fact most successive approximation ADCs I know round to nearest.

Ok, I accept that, so no need for the +1 in the formula? In fact then the whole formula falls away as its is supposed to take care of rounding down and for AVR it seems to round to nearest.

What you are saying in this case, in ideal circumstances my result won't be more accurate with the above formula. Right?

Exactly, for the AVR, you don't need to apply this "correction", in fact it is counterproductive.

Ok, I did a deeper investigation, and concluded that for old PICs, you indeed had to make the correction. Check out the transfer function from this old document:

See the first transition at 1LSB? Now check out the datasheet from a modern PIC:

In this case the first transition is at 0.5LSB, so no correction is needed anymore, even for PICs. Now, check out the datasheet from a ATMega:

See, it also has the first transition at 0.5LSB, though they don't mark it explicitly on the graph, but you can plainly see it is 0.5LSB. So, hopefully you will finally be persuaded that no correction is needed.

So, always read the datasheet very carefully, and don't trust everything people write in forums (fora?...). Including everything I write

BTW, regarding the less interesting topic question, the correct answer is 1024...