[SAMC] ADC 12 bit result accumulation of 4095 or 4096? - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: April 25, 2025, 07:38:58 am
content: I'm struggling to wrap my head around this. I expect it applies to any ADC with result accumulation.Do I take it that given the ADC is 12 bit and that I can accumulate up to 16 of those results into the 16 bit result register before overflowing is the total range 16 * 4095 = 65520 or 16 * 4096 - 1 = 65535 ?using 4095 sounds right to me in terms or basic math but some in my head says that as this has taken me out of round binary numbers it can't be right.

author: Kleinstein
author_rank: Super Contributor
timestamp: April 25, 2025, 08:25:09 am
content: The largest number possible with 12 bits is 4095 = 2^12 - 1. So the sum of 16 such values can not be larger then 16*4095.

author: pcprogrammer
author_rank: Super Contributor
timestamp: April 25, 2025, 09:08:10 am
content: Another way to look at it is in hexadecimal.Max number in 12 bit is 0x0FFF. Shift this 4 bits to the left, which is the same as multiplying by 16, you get 0xFFF0, which is 65520.

author: Simon
author_rank: Unknown
timestamp: April 28, 2025, 01:51:58 pm
content: Yes of course, thank you for proving I am semi sane

author: Siwastaja
author_rank: Super Contributor
timestamp: April 28, 2025, 02:21:10 pm
content: Rangeis 2^n, butlargest possible numberis 2^n- 1, because smallest possible number is 0.Therefore, if you do stuff like divide by range (to get percentage), you need to divide by 2^n, not 2^n -1.

author: cv007
author_rank: Frequent Contributor
timestamp: April 28, 2025, 02:51:27 pm
content: QuoteHow is this handled in practice? My adc gives me a value of 4095, which I would consider 100%, but now when I convert to percent- 4095*100/4096 I get 99%. Using 4095 will get me the 100% and make little difference for anything in between, but of course my underpowered mcu would rather divide by 4096. What is normally done with this type of thing?

author: mikerj
author_rank: Super Contributor
timestamp: April 28, 2025, 03:38:29 pm
content: It's easy to handle because the ADC will hit full scale (4095 in this case) with an input 1 LSB below Vref* i.e. dividing by 4096 gives the correct result. This is the same with most DACs, the maximum code doesn't give you Vref on the output, but 1 LSB below Vref.*Ideally an ADC transitions at the 1/2 LSB point, so it should hot full scale 1.5 LSBs below Vref.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 28, 2025, 03:53:16 pm
content: Quote from: cv007 on April 28, 2025, 02:51:27 pmI'm assuming you meant "99%" here as a figure of speech. For 12-bit samples, it's actually ~99.98%.So yes, this is 1 LSB below Vref. Something to know, although for it to begin to even matter, you'd need to consider ADCs with better than 1 LSB accuracy, which is not something that usual.

author: Simon
author_rank: Unknown
timestamp: April 28, 2025, 05:03:39 pm
content: err, if I were treating my result as a percentage or range it would be 4095 as the top, for the same reason that my top value is 4095*16 when oversampling.There are 4096 Values a 0-100% range has 101 values, we don't use 101 in calculations, so why use 4096 instead of 4095?

author: Siwastaja
author_rank: Super Contributor
timestamp: April 28, 2025, 05:37:13 pm
content: Quote from: Simon on April 28, 2025, 05:03:39 pmBecause there isn't. If you look how a typical SAR ADC operates, measurement range does not include a separate code for Vcc.The small error is hidden in noise, gain errors, inaccuracies and nonlinearities, but using wrong multiplier addseven moreerror, so don't do that.Assume imaginary highly accurate 4-bit SAR ADC in a microcontroller powered from 1.6V supply voltage. Code 0 corresponds to 0 to 0.1V, code 1 to 0.1 to 0.2V, and so on, until code 15 corresponds to 1.5V to 1.6V. Therefore, when you convert to percentages or voltages, you divide by 16:Code 1: 1/16 * 1.6V = 0.1VCode 15: 15/16 * 1.6V = 1.5VIn both cases that would be the start of the bin.If you divide by 15, you will get the ends wrong by different amount:Code 1: 1/15 * 1.6V = 0.10666VCode 15: 15/15 * 1.6V = 1.6000VNow if you redefine the meaning to be the "upper side of the bin", then code 15 appears "correct", but code 1 is then incorrect to that definition. You added gain error.You get bonus points of using the correct divider in that it can be implemented as a simple bit shift way more efficiently than a division, which most simple microcontollers lack.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 28, 2025, 05:49:44 pm
content: Yes, by defining 1/(2^N - 1) as the LSB, you're getting it wrong and will introduce a gain error.The fact that the full scale of an ADC doesn't fully reach Vref is something that tickles many people for no good reason apart from a misunderstanding of how most ADCs work. Then they divide by (2^N - 1), which is not just wrong in terms of gain, but also expensive (instead of dividing by 2^N, which is cheap and correct), and they feel better. Maybe. And since as we said, it's often drown in noise and various errors, they don't even notice.When you're most likely to really notice (unless of course you design precision equipment) is when using a very low-resolution ADC, like 4 bits. Then that becomes very obvious. Note that some of these, often made for high-speed stuff, may not have the typical 1/2^N scale and may not even be linear (specialty stuff). So, as always, read the F.. uh, the datasheet.

author: Siwastaja
author_rank: Super Contributor
timestamp: April 28, 2025, 06:02:32 pm
content: The thinking error there is fundamental, not limited to ADCs by any means. Discretized data represented as single numbers is confusing. Think about 0-100% Simon mentioned. We think about it as a continuous range with infinite resolution, which is why we think 0% is the minimum, "nothing", and 100% is the maximum, "all of it".Yet, if you discretize that to 1% wide bins, then the first bin is 0% to 1% and the last bin must be 99% to 100% and so there are 100 of them, not 101. Now what happens if you name the bins with a single number? Do you use the beginning of the bin as the name? Then your 100 bins are labelled from 0% to 99%. Or maybe you use the end of the bin as label? Then you have 100 bins labelled from 1% to 100%. But you never ever will have 0% to 100%, unless you suddenly change the rounding rules (how the bin is labelled) somewhere in the middle of the range. Which is exactly the gain error.

author: Simon
author_rank: Unknown
timestamp: April 28, 2025, 07:44:37 pm
content: Yes I suppose to think of the numbers as representing a ranges of Vref/2^n is more realistic. So if any value is so +/-0.5 bits does this mean that 0 is 0 +/-0.5 and that the for 12 bits 4095 = Vref / 4096 * 4095 +/- 0.5 bit

author: cv007
author_rank: Frequent Contributor
timestamp: April 28, 2025, 10:26:00 pm
content: QuoteYou are truncating the value to two decimal places (and also indicating this is not the actual value with ~), and I'm truncating to an integer, neither one is the actual value but we both made a choice about how much resolution we wanted. A percentage is not a scale designed for maximum precision, so a decimal place or two is probably the most precision one will see when viewing some information given as a percentage.I understand the adc value of 4095 is not technically 100% of vref, and there is no adc value that will represent it, but as a practical matter where this adc value is presented to the outside as a percentage (of something) it would seem to me a consumer of this percentage information would like to see when max is reached so 0-100% and not 0-99%. Indicating 99% (or 99.9755...) seems to imply to the viewer the max has not been reached although it has (and maybe exceeded). So my question was about how to deal with getting to that 100% which can never be reached if following the /N^2 'rule'. It appears simply using /4095 is the answer, which will get the 100% and make no difference to any introduced error which was already lost in the conversion to a lower resolution.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 28, 2025, 11:02:17 pm
content: Quote from: cv007 on April 28, 2025, 10:26:00 pmNo, I actually rounded it. You truncated it. If you had rounded it like I did, you would have found 100%.

author: cv007
author_rank: Frequent Contributor
timestamp: April 29, 2025, 12:13:29 am
content: QuoteTrue.So a better alternative to /4095 would be to use extra precision to get a rounding value, and apply it?res=4095*1000/4096; //res=999if( (res % 10) >= 5 ) res += 10; //res= 1009res /= 10; //res = 100I realize in this case discarding so much precision means it makes little difference which method is used, but maybe it becomes more important when there is a conversion where less precision is lost. A method that always works would be preferable to one that happens to work ok in a specific instance.I have an LDR in use at the moment where I was using the adc value to show a display output value as 0-100%, and of course was using /4096 which prevented me from seeing 100%.

author: mikerj
author_rank: Super Contributor
timestamp: April 29, 2025, 09:16:24 am
content: Quote from: Simon on April 28, 2025, 07:44:37 pmThe transition between codes occurs with the input 0.5 LSB below/above a nominal code value. Zero and full scale are special cases since there is no code below zero and no code above full scale to transition to or from. This is assuming single ended unipolar ADC, obviously you can go below zero with bipolar or differential input, but the reduced range still applies to the full scale codes.

author: radiolistener
author_rank: Super Contributor
timestamp: May 03, 2025, 01:52:21 am
content: Quote from: Simon on April 25, 2025, 07:38:58 amYes, you can.A 12-bit ADC produces values from 0 to 4095, which is a total of 4096 distinct values. So:Maximum sum of 16 samples = 16 * 4095 = 65520Maximum possible value in 16 bits = 2^16 - 1 = 65535This means you can safely accumulate up to 16 full-scale 12-bit samples in a 16-bit unsigned register without overflow.Quote from: Simon on April 25, 2025, 07:38:58 amThe confusion between 4095 and 4096 arises because:- 4095 is the maximum value of a 12-bit ADC.- 4096 is the number of possible values, from 0 to 4095.When calculating LSB weight, the full scale is divided by 4096, not 4095, because 0 is a valid value.Also, note that if you're summing signed 12-bit values (from a differential ADC or after offset correction), keep in mind that the negative range goes from -2048 to +2047. This asymmetry means the maximum absolute value is smaller on the positive side, and special care must be taken when scaling or accumulating such values to avoid overflow or bias. So, with signed integers things is a little bit more complicated.

author: Simon
author_rank: Unknown
timestamp: May 04, 2025, 06:51:28 am
content: err yea, now you come to mention it, I am using +/- output. But an error of 1 bit in 4095/6 or 65535/6 is really neither here nor there as my numbers are actually being used as -100/+100.Now I'm not sure about the 4096 thing. I have a potentiometer on the 3.3V supply that acts as the reference for one of the ADC's, the oversampled result on full travel to the 3.3V side is exactly 65520 rather than 65535.

author: SiliconWizard
author_rank: Super Contributor
timestamp: May 04, 2025, 04:27:25 pm
content: Quote from: Simon on May 04, 2025, 06:51:28 amThat's mixing a different set of problems. Potentiometers rarely fully reach a state of "zero" resistance at each end of their travel.

author: cv007
author_rank: Frequent Contributor
timestamp: May 04, 2025, 10:59:31 pm
content: QuoteThe hardware is doing what you would otherwise do in software (but in software you would not place a 16bit restriction on the accumulated value)-uint32_t adcAcc = 0; for( unsigned i = 0; i<16; i++ ) adcAcc += adcRead();which leaves you with a max accumulated value of 16*4095 which is 65520. The hardware uses the 16bit RESULT register for its accumulation so has to deal with preventing overflow while accumulating values so it will do shifting as needed which the datasheet describes (and for 16 samples, no shifting required).Your +/- output is a scaling issue separate from your use of a single ended adc input (according to your accumulated value you must be doing, otherwise that unsigned 65520 would be a signed -16).