Nonlinear ADC function calibration  (Resistance measurement) - Page 1
Forum section: electronics / general

author: forrestc
author_rank: Unknown
timestamp: September 02, 2019, 08:13:32 am
content: I'm measuring a resistance with a simple voltage divider consisting of a fixed resistor and an unknown resistor. The resulting voltage is fed into an ADC input. This works perfectly acceptably for my application, and coming up with a formula which determines the value of the unknown resistor is fairly simple algebra.But now, I need to figure out how to calibrate this at manufacturing time. For more linear inputs (i.e. measuring voltage), I just measure the voltage to ADC ratio at two points and then calculate slope and offset and store that in an appropriate region in flash. The code then uses these values to compute the input voltage using these values. This is simple and effective, and has the side effect that it also effectively calibrates out any errors in the voltage reference, divider resistors, Vref source, etc.I'm struggling with figuring out a similar, easy to accomplish, method with this resistance measurement. Since the ratio between the ADC reading and the unknown resistance isn't linear, obviously a simple slope/offset (aka equation of a line) calibration isn't going to work here.I guessing I'm going to have to do some sort of curve fitting based on at least 2-3 points and then probably stick the resulting values in some sort of polynomial. Or maybe there's an easier way I'm overlooking. I'm hoping that someone here has encountered this particular issue and has a shortcut so I don't have to bury my head in an appropriate set of math books to figure this out.The end goal here is to have a method which I can hook up a certain relatively-low quantity of known reference resistors to the input, and then use the resulting ADC values to calculate calibration values for that device which then is used by the firmware.I realize that this would be easier if I used a constant current source, however there are some reasons why that isn't a good solution for this particular device.

author: Nominal Animal
author_rank: Super Contributor
timestamp: September 02, 2019, 11:58:56 am
content: Nonlinear? How?Let's say you haveR0between the ADC pin and ground, andRbetween the ADC pin and VREF, i.e. your ADC is referenced to the supply you use for resistorR.Since the voltage VOUTseen by the ADC isVOUT= VREFR0/ (R0+R)solving forRyieldsR=R0(VOUT/ VREF) -R0Calibration can be done using a single precise resistorR:R0=R/ ( VREF/ VOUT- 1 )If the ADC has range 0..M, inclusive, ADC reading isA=M(VOUT/ VREF). Then,R=R0(M-A) /AwhereA= 0 is a special case (open circuit), andR0=RA/ (M-A)whereA=Mis a special case (open circuit).If your unknown resistor is between ADC input and ground instead, just useR0for the unknown resistor (and precise calibration resistor), andRfor the fixed resistor.

author: mikerj
author_rank: Super Contributor
timestamp: September 02, 2019, 12:12:58 pm
content: As the nominal animal says, this is a purely ratiometric measurement so the main thing you need to calibrate is the fixed resistor value. Depending on ADC performance and accuracy requirements you may also want to calibrate out INL though this is unlikely to be a simple straight line fit.

author: magic
author_rank: Super Contributor
timestamp: September 02, 2019, 12:34:44 pm
content: There are three independent and unknown error sources: ADC offset error, ADC gain error, reference resistance error. That's assuming you can ignore INL.ADC output = Rx/(Rref+Rx)*gain+offsetSo in theory three measurements of different Rx are sufficient and then you solve a set of three simultaneous equations to figure out the exact Rref, gain and offset of given unit. It should be possible to automate this calculation once a pattern of solving it is established.It may be a good idea to asses how much the outcome is influenced by noise and tolerance of the calibration resistances.Things are simplified if the ADC has overrange capability so that two of the measurements can be short and open circuit. Or if Rref tolerance is low enough not to need calibration.

author: mikerj
author_rank: Super Contributor
timestamp: September 02, 2019, 12:41:33 pm
content: Quote from: magic on September 02, 2019, 12:34:44 pmA good point, I was assuming the gain and offset cal was already taken care of by the voltage measurement cal.

author: Nominal Animal
author_rank: Super Contributor
timestamp: September 02, 2019, 02:21:53 pm
content: If you want to calibrate gain and offset too, then letAREFbe the ADC reading when the pin is connected to VREF, andAGNDwhen the ADC pin is connected to ground. Then, the ADC measurementAis described byA= (AREF-AGND)R0/ (R0+R) +AGNDwhich we can easily solve forR:R=R0(AREF-A) / (A-AGND)or forR0:R0=R(A-AGND) / (AREF-A)You'll need three measurements:AREFby measuring VREF,AGNDby measuring GND, and one to measure the fixed resistor, by measuring a known precise resistor; eitherRorR0, depending on how you label the unknown and fixed resistors. Here,Ris between ADC and VREF, andR0is between ADC and GND.

author: forrestc
author_rank: Unknown
timestamp: September 03, 2019, 06:41:56 am
content: Quote from: Nominal Animal on September 02, 2019, 02:21:53 pmOk, I think this gets me to where I need to be. Just to summarize for my own benefit:I should be able to measureAREFby leaving the unknown resistor out of the circuit - I currently have the soldered-in-place resistor,Ras a 'pullup' to VREF, and withoutR0in place, the measured value should be effectively VREF. I should also be able to measureAGNDby inserting a short in place of the unknown resistor,R0. And finally, by inserting a known high precision reference resistor asR0, (probably with a similar nominal value as the known resistor), I should get the value forA. I can then use the now known values ofA,AREF,AGNDandR0, to determine the actual value ofR. Once I have all of those values, I can then replaceR0at will and use the previously determined values to calculate the value ofR0given a newly measured value ofA.I may find after a certain amount of testing thatAREFandAGNDare close enough to full range and zero that I should just ignore those values, since the ADC should be full scale at VREF, and zero at GND. In which case calibration does get quite a bit simpler.Thanks to everyone, this makes a lot more sense now. I had figured out some of the underlying math, but I think I was stuck on how to measureRin place, and how to deal with gain and offset errors if enough exists to matter.Oh, and to clarify my earlier statement about "nonlinear", I was simply referring to the fact that the solution to this problem isn't as simple as using slope/offset, i.e. the mathematical function of a line. I should have thought to better clarify what I meant.

author: magic
author_rank: Super Contributor
timestamp: September 03, 2019, 07:09:02 am
content: Quote from: forrestc on September 03, 2019, 06:41:56 amYou better find this on the ADC's spec sheet, not by measuring a few samples from the same production batchAlso, measurement at GND and REF requires the ADC to have overrange capability.Example:Offset error is 1mV and the ADC reads 0 at 1mV. The ADC doesn't have overrange and also reads 0 at 0mV instead of -1. You measure a short and get a 0 reading. You assume that the reading at 1mV will be 1, you are wrong.

author: forrestc
author_rank: Unknown
timestamp: September 03, 2019, 08:07:03 am
content: Quote from: magic on September 03, 2019, 07:09:02 amI'd probably not drop the additional calibration until a long, proven run over several months with data collected from calibrating dozens of units, and simulating various scenarios.However, due to the uncertainty (as you brought up and I was already figuring out) of measuring things near the rails I'm actually now working through the possibility of actually measuring 2 or 3 reference resistors which would result in high, mid, and low-scale readings and seeing if I calibrate using those measurements. It looks like I should be able to either substitute out or mathematically determineAREFandAGNDusing a system of equations.There is a good chance that I'll do 5 measurements regardless (short, open, and 3 resistance values), and store them in the calibration flash for possible future use. Doing these measurements will also provide me with data to be able to improve the calibration math in the future.

author: Nominal Animal
author_rank: Super Contributor
timestamp: September 03, 2019, 08:41:23 pm
content: Let's say RFIXis a fixed resistor between ADC and VREF, and you have three precise calibration resistorsR1,R2, andR3, that you place between ADC and GND in turn; withA1,A2, andA3as the respective ADC values. (Obviously, you'll wantR1,R2, andR3, to cover the expected measurement range.)Now, the ADC reading is defined byA= (AREF- AGND)R/ (RFIX+R) + AGNDThe three measurements fix the constants AREF, AGND, and RFIXviaRFIX= (R1·R2·(A2-A1) +R1·R3·(A1-A3) +R2·R3·(A3-A2) ) / ( (R2-R3)·A1+ (R3-R1)·A2+A3·(R1-R2) )AREF= (A1·A2·(R2-R1) +A1·A3·(R1-R3) +A2·A3·(R3-R2) ) / ( (R2-R3)·A1+ (R3-R1)·A2+A3·(R1-R2) )AGND= (A1·A2·R3·(R2-R1) +A1·A3·R2·(R1-R3) +A2·A3·R1·(R3-R2) ) / (R1·R2·(A1-A2) +R1·R3·(A3-A1) +R2·R3·(A2-A3) )Then, resistorRbetween ADC and GND, given ADC readingA, isR= RFIX(A- AGND) / (AREF-A)Note that this formulation is nice in that at runtime the resistance is easy to calculate (two subtractions, a multiplication, and a division), but moreover, all three calibration constants (RFIX, AREF, and AGND) have known/expected values. As a sanity check, you can check the computed values of those constants after calibration.

author: Nominal Animal
author_rank: Super Contributor
timestamp: September 04, 2019, 11:58:29 am
content: I realized it might be useful to describe exactly how one can go about to find a solution to this kind of problem.Create a function that describes how you get a reading. This function should be precise, and contain enough constants (whose values you may be unsure of initially) to describe the operation exactly.In the above case, this isA= (AREF- AGND) ·R/ (RFIX+R) + AGNDwhereAis the reading,Ris the quantity to be measured (the resistor between ADC and GND), and AREF, AGND, and RFIXare the constants to be determined via calibration.Form a set of equations, describing each calibration measurement. You need one measurement for each constant to be determined.In the above case, the three calibration measurements areA1= (AREF- AGND) ·R1/ (RFIX+R1) + AGNDA2= (AREF- AGND) ·R2/ (RFIX+R2) + AGNDA3= (AREF- AGND) ·R3/ (RFIX+R3) + AGNDThese form your system of three equations in three unknowns.Solve the system of equations for the constants.There is no need to do this by hand, as there are several free and commercial CAS software packages (computer algebra systems) you can use. I like Maple a lot, but it is commercial and can be quite expensive depending on your use case. For example, on Maxima (free, GPL; included in e.g. Ubuntu repositories):A(R) = (A_REF - A_GND) * R / (R_FIX + R) + A_GND;solve([ A(R1)=A1, A(R2)=A2, A(R3)=A3 ], [ R_FIX, A_REF, A_GND ]);gives the same solution as shown in my previous post in this thread. I used Maple:A := R -> (A_REF - A_GND) * R / (R_FIX + R) + A_GND:solve([ A(R1)=A1, A(R2)=A2, A(R3)=A3 ], [ R_FIX, A_REF, A_GND ]);These give thealgebraicsolutions (as opposed to numerical ones): the equations you need to implement the calibration on a microcontroller.(Note that you will have to do all three measurements before calculating any of the constants.)To explore what kind of accuracy you can get in real life, I prefer to do brute-force numerical analysis. Simply put, I write a small program with a pseudorandom number generator (typically an Xorshift variant, as they're fast and random enough) with a suitable distribution (often limited Gaussian, centered around the desired value, with a standard deviation corresponding to the range covering 63.8% of its values; sometimes uniform within a range). A few thousand to a few million samples will give a very good picture of how the result is distributed.(You can, of course, add error terms to the equations, and handle it all algebraically. However, the algebraic form of the error bars doesn't tell much to me or any other non-mathematician; a simple plot of the result distribution gives a much better intuitive and analytical picture, if the random number distributions used are sane and clearly described.)The brute-force numerical analysis works for analysing the calibration, too. There, you generate pseudorandom measurements (with bothAiandRihaving some pseudorandom error or noise thrown in), and compare the constants each calibration process generates, to the expected ones. You can even go further, by using each set of constants to "test" a number of prototype resistors, and gather the result distribution for each separately. This will give you a plot of each prototype resistor measurement, when some randomness is included in the calibration process (and thus the constants).In fact, just straightforward plotting the results (say, prototype resistor resistance on the horizontal axis, measured resistance divided by prototype resistance on the vertical axis) for each error distribution in the calibration measurements, gives a very good picture of the expected relative error (or precision) within the range covered by the prototype resistors. Such a test program is near-trivial to write in C (or Python or whatever language you prefer), and you can use e.g. Gnuplot to plot the results (as say SVG images), to show to the Powers That Be how the device is expected to perform, given the expected randomness/error distributions in the measurements.Such test programs are, in fact, numerical simulators.

author: forrestc
author_rank: Unknown
timestamp: September 04, 2019, 09:53:57 pm
content: Thank you Nominal Animal for the lengthy explanation. Your solution saved me a lot of time, plus the explanation of how to set this up will save me a lot of pondering in the future.I wasn't relishing spending enough time with a CAS to figure this out, although it looks like the methodology is more straightforward than I would have thought when using a CAS.