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:
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);
}
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.
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.
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 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.
alexan_e
EinigeEE
Flegmatoides Zoid
Flegmatoides Zoid
EinigeEE
EinigeEE
Flegmatoides Zoid
EinigeEE