Author Topic: Nonlinear ADC function calibration (Resistance measurement)  (Read 1520 times)

0 Members and 1 Guest are viewing this topic.

Offline forrestcTopic starter

  • Supporter
  • ****
  • Posts: 674
  • Country: us
Nonlinear ADC function calibration (Resistance measurement)
« on: September 02, 2019, 08:13:32 am »
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.
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6539
  • Country: fi
    • My home page and email address
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #1 on: September 02, 2019, 11:58:56 am »
Nonlinear?  How?

Let's say you have R0 between the ADC pin and ground, and R between the ADC pin and VREF, i.e. your ADC is referenced to the supply you use for resistor R.

Since the voltage VOUT seen by the ADC is
    VOUT = VREF R0 / ( R0 + R)
solving for R yields
    R = R0 (VOUT / VREF) - R0

Calibration can be done using a single precise resistor R:
    R0 = R / ( VREF / VOUT - 1 )

If the ADC has range 0..M, inclusive, ADC reading is A = M(VOUT / VREF). Then,
    R = R0 (M - A) / A
where A = 0 is a special case (open circuit), and
    R0 = R A / (M - A)
where A = M is a special case (open circuit).

If your unknown resistor is between ADC input and ground instead, just use R0 for the unknown resistor (and precise calibration resistor), and R for the fixed resistor.
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3294
  • Country: gb
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #2 on: September 02, 2019, 12:12:58 pm »
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.
 

Offline magic

  • Super Contributor
  • ***
  • Posts: 6927
  • Country: pl
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #3 on: September 02, 2019, 12:34:44 pm »
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+offset

So 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.
« Last Edit: September 02, 2019, 12:36:32 pm by magic »
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3294
  • Country: gb
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #4 on: September 02, 2019, 12:41:33 pm »
There are three independent and unknown error sources: ADC offset error, ADC gain error, reference resistance error. That's assuming you can ignore INL.

A good point, I was assuming the gain and offset cal was already taken care of by the voltage measurement cal.
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6539
  • Country: fi
    • My home page and email address
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #5 on: September 02, 2019, 02:21:53 pm »
If you want to calibrate gain and offset too, then let AREF be the ADC reading when the pin is connected to VREF, and AGND when the ADC pin is connected to ground.  Then, the ADC measurement A is described by
    A = (AREF - AGND) R0 / (R0+R) + AGND
which we can easily solve for R:
    R = R0 (AREF - A) / (A - AGND)
or for R0:
    R0 = R (A - AGND) / (AREF - A)

You'll need three measurements: AREF by measuring VREF, AGND by measuring GND, and one to measure the fixed resistor, by measuring a known precise resistor; either R or R0, depending on how you label the unknown and fixed resistors.  Here, R is between ADC and VREF, and R0 is between ADC and GND.
 

Offline forrestcTopic starter

  • Supporter
  • ****
  • Posts: 674
  • Country: us
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #6 on: September 03, 2019, 06:41:56 am »
If you want to calibrate gain and offset too, then let AREF be the ADC reading when the pin is connected to VREF, and AGND when the ADC pin is connected to ground.  Then, the ADC measurement A is described by
    A = (AREF - AGND) R0 / (R0+R) + AGND
which we can easily solve for R:
    R = R0 (AREF - A) / (A - AGND)
or for R0:
    R0 = R (A - AGND) / (AREF - A)

You'll need three measurements: AREF by measuring VREF, AGND by measuring GND, and one to measure the fixed resistor, by measuring a known precise resistor; either R or R0, depending on how you label the unknown and fixed resistors.  Here, R is between ADC and VREF, and R0 is between ADC and GND.

Ok, I think this gets me to where I need to be.  Just to summarize for my own benefit:

I should be able to measure AREF by leaving the unknown resistor out of the circuit -  I currently have the soldered-in-place resistor, R as a 'pullup' to VREF, and without  R0 in place, the measured value should be effectively VREF.    I should also be able to measure AGND by inserting a short in place of the unknown resistor,  R0.   And finally, by inserting a known high precision reference resistor as R0, (probably with a similar nominal value as the known resistor), I should get the value for A.  I can then use the now known values of A, AREF, AGND and R0, to determine the actual value of R.   Once I have all of those values, I can then replace R0 at will and use the previously determined values to calculate the value of R0 given a newly measured value of A.

I may find after a certain amount of testing that AREF and AGND are 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 measure R in 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.

 

Offline magic

  • Super Contributor
  • ***
  • Posts: 6927
  • Country: pl
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #7 on: September 03, 2019, 07:09:02 am »
I may find after a certain amount of testing that AREF and AGND are 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.
You better find this on the ADC's spec sheet, not by measuring a few samples from the same production batch :)

Also, 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.
 

Offline forrestcTopic starter

  • Supporter
  • ****
  • Posts: 674
  • Country: us
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #8 on: September 03, 2019, 08:07:03 am »
I may find after a certain amount of testing that AREF and AGND are 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.
You better find this on the ADC's spec sheet, not by measuring a few samples from the same production batch :)

I'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 determine AREF and AGND using 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.
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6539
  • Country: fi
    • My home page and email address
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #9 on: September 03, 2019, 08:41:23 pm »
Let's say RFIX is a fixed resistor between ADC and VREF, and you have three precise calibration resistors R1, R2, and R3, that you place between ADC and GND in turn; with A1, A2, and A3 as the respective ADC values.    (Obviously, you'll want R1, R2, and R3, to cover the expected measurement range.)

Now, the ADC reading is defined by
    A = (AREF - AGND) R / (RFIX + R) + AGND

The three measurements fix the constants AREF, AGND, and RFIX via
    RFIX = ( R1·R2·(A2 - A1) + R1·R3·(A1 - A3) + R2·R3·(A3 - A2) ) / ( (R2 - R3A1 + (R3 - R1A2 + A3·(R1 - R2) )
    AREF = ( A1·A2·(R2 - R1) + A1·A3·(R1 - R3) + A2·A3·(R3 - R2) ) / ( (R2 - R3A1 + (R3 - R1A2 + 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, resistor R between ADC and GND, given ADC reading A, is
    R = 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.
« Last Edit: September 03, 2019, 08:43:34 pm by Nominal Animal »
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6539
  • Country: fi
    • My home page and email address
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #10 on: September 04, 2019, 11:58:29 am »
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 is
        A = (AREF - AGND) · R / (RFIX + R) + AGND
    where A is the reading, R is the quantity to be measured (the resistor between ADC and GND), and AREF, AGND, and RFIX are 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 are
        A1 = (AREF - AGND) · R1 / (RFIX + R1) + AGND
        A2 = (AREF - AGND) · R2 / (RFIX + R2) + AGND
        A3 = (AREF - AGND) · R3 / (RFIX + R3) + AGND
    These 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 the algebraic solutions (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 both Ai and Ri having 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.
« Last Edit: September 04, 2019, 12:00:04 pm by Nominal Animal »
 
The following users thanked this post: forrestc, techman-001

Offline forrestcTopic starter

  • Supporter
  • ****
  • Posts: 674
  • Country: us
Re: Nonlinear ADC function calibration (Resistance measurement)
« Reply #11 on: September 04, 2019, 09:53:57 pm »
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.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf