Nichtlinearer Fehler in ADC-Messwerten

Ich messe Spannungen bis zu 20 V mit meiner ATmega2650-MCU (10-Bit-ADC).

Ich verwende eine 5-V-Präzisionsspannungsreferenz (LT1021 - 0,05 %).

Spannungsteiler sind mit 1% Panasonic-Widerständen aufgebaut.

Vcc->10kOhm->Messen->3,3kOhm->GND.

Teilungsverhältnis: 3,3/13,3 = 0,248

Was mir aufgefallen ist, sind die folgenden zunehmenden Fehler beim Messen größerer Spannungen:

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

Kann jemand erklären, was diese Nichtlinearität verursacht?

Gibt es mathematische Hinweise, um dies abzuschätzen (ADC-Funktionen statt Poly-Fit)? Alle Verweise auf mathematische Modelle würden helfen.

EDIT - Fehler durch alle Spannungsbereiche:
Geben Sie hier die Bildbeschreibung ein

Die Berechnungsmethodik:

#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);
}
Wie berechnen Sie die Spannung aus dem ADC-Ergebnis, ist es /1023 oder /1024?
Vielleicht vermisse ich es, aber wie berechnen Sie all diese wahren Werte für die Spannung?
1023 ... oder in diesem Fall 250, 251 - der Messwert, den ich bekomme, nachdem ich vRef an einen geeigneten analogen Kanal angelegt habe
MathEE -> diese Zeile: psu_volts[i] = psu_volts_raw[i] * mvAdc[i]; 5,0/250 = 0,02 V; 344 (ADC-Lesung) * 0,02 = 3,3 V
psu_volts ist die experimentelle Spannung, die aus dem ADC-Messwert berechnet wird. Wie lesen Sie die wahre Spannung, die an den ADC gesendet wird?
Außerdem weisen alle ADCs eine Nichtlinearität in der Übertragungsfunktion auf. Stellt sich hier die Frage, ob das ungewöhnlich ist oder wie man diese Nichtlinearität modelliert?
MathEE - das wahre Lesen geschieht in readADC()->readAnalog()->analogRead(). Der ADC liest Werte genau, aber nur solche, die 5V nicht überschreiten (Achtung - alle Werte kommen über Spannungsteiler), daher vermute ich, dass ADC nichts damit zu tun hat, aber der Spannungsteiler - schon.
Ich denke, Sie haben meine Frage in den Kommentaren zu Spheros Antwort beantwortet. Ihre wahren Messungen wurden mit einem Voltmeter erhalten.

Antworten (4)

ADCs sind von Natur aus nichtlinear. Die Übertragungsfunktion beginnt ungefähr bei 0, steigt dann schneller als die erwartete lineare Übertragungsfunktion an, bis sie #bits/2 erreicht, und kehrt dann dorthin zurück, wo sie sein sollte. Ich kann ein Diagramm zeichnen, wenn diese Erklärung nicht klar ist.

Das Hauptproblem besteht darin, dass Sie davon ausgehen, dass der Wandler die lineare Übertragungsfunktion mit Spannung = 5 V / 250 * (Wert vom ADC) hat. Dies ist nicht der Fall, und der Fehler ist nicht einmal linear, wie Sie bereits festgestellt haben. Angesichts der Form der realen Übertragungsfunktion, der von Andy bereitgestellten Daten und der Art und Weise, wie Sie die Fehler bei höheren Spannungen berechnen, ist das Muster, das Sie sehen, wie erwartet.

Ich glaube nicht, dass Sie das gesagt haben, aber lassen Sie mich basierend auf dieser Analyse eine Vermutung anstellen: Ihr ADC überschätzt die Spannungen ständig. Dies liegt daran, dass die Steigung (5 V/250) nahe dem Maximum der Steigung der realen Übertragungsfunktion liegt.

Bearbeitet, um hinzuzufügen: Vielleicht war aus Ihrem Beitrag klar, dass sie überschätzt werden, da Err% immer > 1 ist. Es ist klar, ob Err% = (ATMega-Messwert) / (realer Wert)

2. Edit: Eigentlich, was Sie einfach tun können und sehen, wie sich die Ergebnisse ändern: Legen Sie 20 V über den Spannungsteiler. Sie erhalten eine Zahl, sagen Sie $N$, dann definieren Sie psu_adc_corr=N und myAdc = 20/psu_adc_corr=20/N. Mich würde interessieren was du bekommst.

Ich habe die Maße aktualisiert. Die Steigung scheint abzunehmen. Zu meiner großen Überraschung konnte ich 20 V nicht erreichen (auch wenn ich theoretisch 251 * 4 = 1004 ADC bei 20 Volt hätte haben sollen)

Hier ist wahrscheinlich die beste Erklärung, die ich vom Anbieter gefunden habe

Es hat einen Abschnitt, der der Nichtlinearität gewidmet ist, mit der folgenden Schlussfolgerung: "Nichtlinearität kann nicht mit einfachen Berechnungen kompensiert werden. Zu diesem Zweck können polynomiale Annäherungen oder Tabellensuchen verwendet werden. "

Also habe ich ein int8_t-Array mit 14 ADC-Fehlerwerten erstellt (das entspricht 1-Volt-Schritten). Ich habe diese Korrekturen auf das ADC-Lesen angewendet und yay! - Ich habe jetzt die Spannungsmesswerte mit einem Fehler von 0 bis 1 mV.
Darüber hinaus kann ich jetzt einen einzelnen Wert von mV_per_ADC_step verwenden (in Bezug auf meine vorherige Version, für die ich einen dedizierten mV-Wert für jeden ADC-Kanal hatte).

Der ADC in der MCU ist mit einer absoluten Genauigkeit von bis zu 2,5 LSbs bei einer Abtastrate von 200 kHz spezifiziert. Dies ist bei einer Vref- und Vcc-Spannung von 4 V angegeben, aber ich gehe davon aus, dass es bei 5 V sehr ähnlich sein wird. Wenn Ihre Referenz 5 V beträgt, entsprechen 2,5 LSbs etwa 12 mV. Dies kann sich für eine bestimmte Eingangsspannung positiv oder negativ äußern.

Ich bin mir nicht sicher, wie Ihre Werte erhalten werden, aber bei 0,248 * 7 V an einem Eingang können 12 mV ein Fehler von 0,69 % sein, was irgendwo in dem Bereich liegt, den Sie sehen.

Geben Sie hier die Bildbeschreibung ein

Wenn Sie jedoch einen differenziellen ADC-Kanal verwenden, kann dies bei einer Verstärkung von Eins auf etwa 18 LSbs ansteigen, da ein interner Verstärker einen Fehler hinzufügt.

Ich bin mit dem Atmel-ADC-Konverter nicht so vertraut, aber ich schlage vor, dass die Erfassungszeit für den S & H möglicherweise nicht lang genug ist. Ich denke, es sollte 15usec oder mehr sein.

Die maximale Zeitkonstante beträgt 100K Ω 14 pF oder 1,4 us, also 10 Zeitkonstanten sind etwa 15 us.

Haben Sie alternativ so etwas wie eine 5,1-V-Zenerdiode am Eingang? Das könnte leicht zu Fehlern in diesem Bereich führen.

es würde sich wahrscheinlich auf alle Messwerte auswirken, oder? Außerdem habe ich versucht, die "Zwischen"-ADC-Messwerte um bis zu 10 ms zu verzögern - keine Auswirkung.
Kein Zener? Nein, es würde nicht alle Messwerte gleichermaßen beeinflussen, von anderen ADCs, mit denen ich gearbeitet habe, würde ich einen geringeren Effekt im mittleren Bereich erwarten.
Nein, kein Zener. Und ja, bis zu 5 V sind die Messwerte so genau, dass ich mein Setup zum Kalibrieren billiger Voltmeter verwenden könnte.
Hast du die Eingangsspannung selbstständig mit einem hochohmigen Voltmeter direkt am Chip gemessen?
richtig, und so habe ich das Err (%) auf dem Diagramm berechnet - durch Vergleich der Atmega-Messungen mit den Voltmeter-Messungen.
@FlegmatoidZoid Okay, aber ich glaube nicht, dass Sie 16 V "direkt am Chip" gemessen haben, also haben Sie am Eingang des Teilers gemessen und davon ausgegangen, dass die Chipspannung das 0,248-fache der gemessenen Spannung war, oder haben Sie eine Messung am vergrößert Chip um 1/0,248?
rechts, die wahre Messung wurde mit dem Voltmeter von ungeteiltem Wert am Eingang durchgeführt. Die Atmega-Messung wurde am geteilten Wert durchgeführt und proportional hochskaliert (5,0/250*ADC_reading). Ich habe versucht, verschiedene Referenzpunkte zu setzen (12.05/615, 19.94/1020), aber das beseitigt den Fehler nur für diesen bestimmten Messwert.