3
\$\begingroup\$

I am measuring voltages up to 20V with my ATmega2650 MCU (10-bit ADC).

I'm using 5V precision voltage reference (LT1021 - 0.05%).

Voltage dividers are setup with 1% Panasonic resistors.

Vcc->10kOhm->Measure->3.3kkOhm->GND.

Division ratio: 3.3/13.3=0.248

What I've noticed is the following increasing errors while measuring bigger voltages:

Vmeas ADC Err(%)
3.05 152 -0.013382929
4.09 205 -0.0075968
5.02 253 -0.002075695
6.08 308 0.003057305
7.07 359 0.0054141
8.07 410 0.00595279
9.07 461 0.00637229
10.02 510 0.007764232
11.05 563 0.008777353
12.05 615 0.01046932
13.05 665 0.008925735
14.05 717 0.010366242
15.06 769 0.010955198
16.05 820 0.011495804
17.07 872 0.011368671
18.06 923 0.011826103
19.04 973 0.011739502
19.51 998 0.01271154
19.94 1020 0.012715509

Can someone explain what is causing such non-linearity?

Any hints on math to estimate this (ADC features rather than poly-fit)? Any references to mathematical models would help.

EDIT - errors through all voltage range:
enter image description here

The calculation methodology:

#define PSU_ANALOG_CHANNELS 3
#define PSU_ANALOG_MEASURES 5
#define PSU_ANALOG_MEASURE_DELAY 1
//apply vRef to each pin to measure post-divided ADC reading
const int psu_adc_corr[PSU_ANALOG_CHANNELS] = {250,251,251};
int psu_volts_raw[PSU_ANALOG_CHANNELS];//stores ADC readings
float psu_volts[PSU_ANALOG_CHANNELS] = {0}; //stores final values
float mvAdc[PSU_ANALOG_CHANNELS]; //stores mV per each ADC-channel (to avoid division)
void calcMvADC(){
 for (int i=0; i<PSU_ANALOG_CHANNELS; i++) {
 mvAdc[i] = 5.0 / psu_adc_corr[i];
 }
}
//returns averaged reading for each ADC channel
int readAnalog(int ch) {
 int val = 0;
 for (int i=0; i<PSU_ANALOG_MEASURES; i++) {
 val += analogRead(ch);
 delay(PSU_ANALOG_MEASURE_DELAY);
 }
 return val/PSU_ANALOG_MEASURES;
}
void readADC() {
 for (int i=0; i<PSU_ANALOG_CHANNELS; i++) {
 psu_volts_raw[i]=readAnalog(i);
 }
}
/*
>6 <=7 : -1.1%
>7 <=9: -1.14%
>9 <=13: -1.25%
>13 -1.7%:
*/
float corrVoltage(float V) {
 if (V<6) return V;
 if (V>6 && V<=7) return V*0.989;
 if (V>7 && V<=9) return V*0.9886;
 if (V>9 && V<=13) return V*0.9875;
 if (V>13) return V*0.983;
 return V;
}
void calcVoltages() {
 for (int i=0; i<PSU_ANALOG_CHANNELS; i++) {
 psu_volts[i] = psu_volts_raw[i] * mvAdc[i];
 psu_volts[i] = corrVoltage(psu_volts[i]);
 }
}
void setup (){
 analogReference(EXTERNAL);
 calcMvADC();
 Serial.begin(115200);
}
void loop (){
 readADC();
 calcVoltages();
 for (int i=0; i<PSU_ANALOG_CHANNELS; i++) {
 Serial.println(psu_volts[i]);
 }
 delay(500);
}
asked Feb 23, 2014 at 19:31
\$\endgroup\$
8
  • \$\begingroup\$ How do you calculate the voltage from the ADC result, is it /1023 or /1024 ? \$\endgroup\$ Commented Feb 23, 2014 at 20:07
  • \$\begingroup\$ Maybe I am missing it, but how are you calculating all of these true values for the voltage? \$\endgroup\$ Commented Feb 23, 2014 at 21:40
  • \$\begingroup\$ 1023...or in this case 250, 251 - the reading I get after feeding vRef to apropriate analog channel \$\endgroup\$ Commented Feb 23, 2014 at 21:40
  • \$\begingroup\$ MathEE -> this line: psu_volts[i] = psu_volts_raw[i] * mvAdc[i]; 5.0/250 = 0.02V; 344(ADC reading) * 0.02 = 3.3V \$\endgroup\$ Commented Feb 23, 2014 at 21:44
  • \$\begingroup\$ psu_volts is the experimental voltage calculated from the ADC reading. How are you reading the true voltage sent to the ADC? \$\endgroup\$ Commented Feb 23, 2014 at 21:54

4 Answers 4

3
\$\begingroup\$

ADCs are naturally non-linear. Roughly the transfer function starts at 0, then increases faster than the expected linear transfer function until it reaches #bits/2 and then curves back to where it should be. I can draw a diagram if this explanation is not clear.

The main problem is that you are assuming the converter has the linear transfer function with Voltage = 5V/250*(value from ADC). It does not and the error isn't even linear, as you've already observed. Given the shape of the real transfer function, the data provided by Andy, and the way you are computing the errors at higher voltages the pattern you are seeing is as expected.

I don't think you stated, but let me make a conjecture based on this analysis: Your ADC consistently overestimates the voltages. This is because the slope (5V/250) is near the maximal of the slope of the real transfer function.

Edited to add: Maybe it was clear from your post they are being overestimated since Err % is always> 1. It is clear if Err%=(ATMega reading)/(real value)

2nd edit: Actually, what you can do easily and see how the results change: Put 20V across the voltage divider. You will get a number say $N$ then define psu_adc_corr=N and myAdc = 20/psu_adc_corr=20/N. I would be interested to see what you get.

answered Feb 24, 2014 at 1:29
\$\endgroup\$
1
  • \$\begingroup\$ I've updated the measurements. The slope seems to decrease. To my big surprise I wasn't able to reach 20V (even tough theoretically I should have had 251*4=1004ADC at 20 volts) \$\endgroup\$ Commented Feb 24, 2014 at 15:07
2
\$\begingroup\$

here's probably the best explanation I've found from the vendor

It has a section dedicated to non-linearity with the following conclusion: "Non-linearity cannot be compensated for with simple calculations. Polynomial approximations or table lookups can be used for that purpose."

So I've made int8_t array of 14 ADC error values (that accounts for 1 volt increments). I've applied those corrections on ADC reading and yay! - I do now have the voltage readings with an error of 0 to 1mV.
Furthemore, I can now use single value of mV_per_ADC_step (in respect to my previous version for which I've had dedicated mV value for each ADC channel).

answered Feb 25, 2014 at 14:23
\$\endgroup\$
0
\$\begingroup\$

The ADC in the MCU is specified as having an absolute accuracy of up to 2.5 LSbs at a sampling rate of 200kHz. This is specified at aVref and Vcc voltage of 4V but I'm assuming it's going to be very similar at 5V. If your reference is 5V, 2.5 LSbs is about 12mV. This can manifest itself positively or negatively for a particular input voltage.

I'm not sure how your values are obtained but at 0.248*7V on an input, 12 mV can be an error of 0.69% which is somewhere in the realm that your are seeing.

enter image description here

But if you are using a differential ADC channel this can increase to about 18 LSbs at a gain of unity because there is an internal amplifier adding an error.

answered Feb 23, 2014 at 20:34
\$\endgroup\$
0
\$\begingroup\$

I'm not that familiar with the Atmel ADC converter, but I suggest that maybe the acquisition time for the S&H is not long enough. I think it should be 15usec or greater.

The maximum time constant is 100K\$\Omega\cdot 14\$pF or 1.4usec, so 10 time constants is about 15usec.

Alternately, do you have something like a 5.1V Zener diode on the input? That could easily cause errors in that range.

answered Feb 23, 2014 at 19:45
\$\endgroup\$
7
  • \$\begingroup\$ it would probably have an effect on all readings, wouldn't it? Besides, I've tried delaying the "in-between" adc readings up to 10ms - no impact. \$\endgroup\$ Commented Feb 23, 2014 at 21:14
  • \$\begingroup\$ No zener? No it wouldn't affect all readings equally, from other ADCs I've worked with, I'd expect less effect around midscale. \$\endgroup\$ Commented Feb 23, 2014 at 21:18
  • \$\begingroup\$ nope, no Zener. And yes, up to 5V the readings are so accurate I could use my setup for calibrating cheap voltmeters. \$\endgroup\$ Commented Feb 23, 2014 at 21:38
  • \$\begingroup\$ Have you independently measured the input voltage with a high-impedance voltmeter directly at the chip? \$\endgroup\$ Commented Feb 23, 2014 at 21:44
  • \$\begingroup\$ correct, and this is how I calculated the Err(%) on chart - by comparing the atmega measurements with the voltmeter measurements. \$\endgroup\$ Commented Feb 24, 2014 at 0:06

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.