Tutorial · Einsteiger · 30 Min.

Analoge Spannungen lesen: ADC-Referenz und Teiler

analogRead liefert ein Verhältnis, keine Spannung. Referenz wählen, die nicht einbricht, Teiler dimensionieren und einen Batteriewächter bauen, der nicht lügt.

Einführung

analogRead() ist die freundlichste Funktion auf einem Arduino und eine der am leisesten irreführenden. Sie liefert eine Zahl zwischen 0 und 1023 zurück, jeder behandelt diese Zahl als Spannung, und meist geht nichts schief.

Schief geht es auf einem Roboter, weil ein Roboter Motoren hat – und Motoren genau das bewegen, wogegen diese Zahl gemessen wird.

analogRead liefert ein Verhältnis

Der ADC misst keine Volt. Er vergleicht seinen Eingang mit einer Referenz und meldet das Verhältnis:

reading = 1023 × V_in / V_ref

Auf einem Arduino Uno ist V_ref standardmäßig AVCC – die 5-V-Schiene des Boards. analogRead() beantwortet also eigentlich „welcher Bruchteil der 5-V-Schiene ist das?“, und die Umrechnung in Volt per Multiplikation mit 5/1023 stimmt nur, solange die Schiene exakt 5 V hat.

Du weißt bereits, dass sie das nicht hat, sobald die Motoren laufen.

Die Referenz ist der Fehler

Verdrahte einen Akku über einen Teiler an A0 und beobachte die Zahl, während die Motoren anlaufen.

Zwei gestapelte Diagramme. Das obere zeigt ADC-Zählwerte gegen Akkuspannung von 6 bis 8,4 Volt für zwei Designs: eine 5-Volt-Schienenreferenz mit 20k/10k-Teiler steigt von etwa 409 auf 573 Zählwerte, und eine interne 1,1-Volt-Referenz mit 68k/10k-Teiler steigt von etwa 715 auf 1002. Das untere Diagramm zeigt die gemeldete Akkuspannung für einen konstanten 7,4-Volt-Akku, während die echte 5-Volt-Schiene von 4,2 bis 5,2 Volt variiert: Das schienenreferenzierte Design fällt von 8,8 Volt auf 7,1 Volt, während das Design mit interner Referenz flach bei 7,4 bleibt.
Unten: Der Akku bewegt sich nie. Während die Schiene unter Motorlast von 5,0 V auf 4,2 V einbricht, meldet Code mit 5-V-Referenz den Akku steigend von 7,4 V auf 8,8 V. Die Bandgap-Referenz meldet durchgehend 7,40 V. Oben: dieselben 10 Bit verteilt auf 286 statt 164 Zählwerte, weil der Teiler so dimensioniert wurde, dass er die Referenz füllt. SVG herunterladen

Eine Unterspannungsabschaltung auf dem ersten Design tut das Gegenteil ihrer Aufgabe: Je härter der Roboter arbeitet, desto gesünder wirkt der Akku. Und der Fehler ist lautlos, weil jeder einzelne Messwert ein völlig gültiges Verhältnis ist.

Die Lösung ist eine Referenz, die sich nicht bewegt. Der ATmega328P hat eine 1,1-V-Bandgap-Referenz an Bord:

analogReference(INTERNAL);   // 1.1 V on an Uno; INTERNAL1V1 on a Mega
analogRead(A0);              // discard — the reference needs a moment to settle

Verwirf nach dem Umschalten der Referenz immer den ersten Messwert. Interne Referenz und Eingangskondensator brauchen Zeit, und die erste Abtastung erfolgt, bevor sie sie hatten.

Den Teiler dimensionieren

Die Referenz ist nun 1,1 V und dein Akku 8,4 V, der Teiler muss also die Obergrenze knapp unter die Referenz bringen:

V_adc = V_pack × R2 / (R1 + R2)

Mit R1 = 68 kΩ und R2 = 10 kΩ beträgt das Verhältnis 0,128, ein voller 8,4-V-Akku liefert also 1,077 V – knapp unter der Referenz, fast die ganze Skala nutzend. Drei Zahlen, die du an jedem selbst entworfenen Teiler prüfen solltest:

Prüfung Dieser Teiler Warum
Obergrenze vs. Referenz 1,077 V vs. 1,1 V Über der Referenz klemmt er flach
Ruhestrom 108 µA Zieht 2,6 mAh pro Tag – vernachlässigbar gegen 2000 mAh
Ausgangsimpedanz (R1∥R2) 8,7 kΩ Unter ca. 10 kΩ halten, siehe unten

Vergleiche das mit der naiven Variante – einem 20-kΩ/10-kΩ-Teiler gegen die 5-V-Schiene gelesen. Sie legt einen vollen Akku auf 2,80 V, also 573 von möglichen 1023 Zählwerten, und nutzt über den gesamten Akkubereich von 6,0–8,4 V 164 Zählwerte gegen 286 beim besseren Design. Fast doppelte Auflösung für dieselben zwei Widerstände und eine Codezeile.

Auflösung ist nicht Genauigkeit

Zehn Bit gegen 5-V-Referenz sind 4,88 mV pro Schritt; gegen 1,1-V-Referenz 1,07 mV. Keine der beiden Zahlen ist deine Genauigkeit.

Was dich wirklich begrenzt:

  • Referenztoleranz. Die Bandgap-Referenz liegt nominell bei 1,1 V und streut einige Prozent zwischen Chips. Das ist ein fester Skalenfehler, also miss sie einmal pro Board: Lege eine bekannte Spannung an, lies die Zählwerte und löse nach der echten Referenz auf. Speichere diese Konstante. Das ist ein Kalibrierschritt, keine optionale Verfeinerung.
  • Rauschen. Die Versorgung eines Roboters ist verrauscht, und Mittelung ist die billige Antwort. Das Mitteln von N Abtastungen senkt zufälliges Rauschen um √N – 16 Abtastungen sind 4× leiser, ein nutzbares Bit Auflösung mehr. Gegen einen Skalenfehler tut es nichts.
  • Quellimpedanz. Der ADC lädt einen kleinen Abtast-Halte-Kondensator über das, woran er hängt. Eine hochohmige Quelle füllt ihn in der erlaubten Zeit nicht, und der Rest des zuvor gelesenen Kanals steckt noch darin.

Letzteres erzeugt einen markanten Fehler mit eigenem Namen.

Kanäle, die wie ihre Nachbarn lesen

Lies A0 und A1 abwechselnd mit hochohmigen Quellen, und jeder Messwert wird zum vorherigen hingezogen. Drehe ein Poti an A0 und beobachte, wie A1 mitwandert. Nichts ist falsch verdrahtet.

Zwei Lösungen, und du kannst beide nutzen:

int readSettled(uint8_t pin) {
  analogRead(pin);        // charge the sample-and-hold on this channel
  return analogRead(pin); // then take the one you keep
}

Zweimal lesen und das erste Mal verwerfen gibt dem Kondensator ein zweites Abtastfenster, um die neue Spannung zu erreichen. Die andere Lösung ist, die Quellimpedanz zu senken – darum nutzt der obige Teiler 68 kΩ/10 kΩ statt der 100 kΩ/100 kΩ, die ordentlicher aussehen und 50 kΩ Ausgangsimpedanz ergäben.

Wo das auf einem Roboter auftaucht

  • Batterieüberwachung wie oben – und sie macht eine Unterspannungsabschaltung erst vertrauenswürdig genug, um einen Li-Ion-Akku zu schützen.
  • Analoge Liniensensoren. Ein IR-Reflexionsarray liefert einen kontinuierlichen Wert pro Sensor, womit ein Linienfolger eine glatte Position statt einer Links-rechts-Flagge berechnet. Diese Sensoren werden gegen dieselbe Schiene gelesen, die die Motoren einbrechen lassen – ein Grund, warum die Kalibrierung eines Linienfolgers driftet, während der Akku leer wird.
  • Strommessung per Shunt oder Hallsensor, dessen Ausgang eine Spannung ist.
  • Potentiometer als Stellknöpfe – der freundlichste Weg, eine PID-Verstärkung ohne Neuflashen abzustimmen.
  • Nicht für einen 5-V-Sensor am 3,3-V-Board. Ein ESP32 oder Raspberry Pi braucht den Teiler zum Schutz, nicht nur für den Bereich.

Wenn es schiefgeht

Symptom Meistens
Akkuanzeige steigt, wenn die Motoren anlaufen Auf einbrechende Schiene referenziert; INTERNAL nutzen
Messwert klemmt bei 1023 Eingang über der Referenz – Teilverhältnis zu hoch
Ein Kanal echot den zuvor gelesenen Quellimpedanz zu hoch; zweimal lesen und verwerfen
Messwerte springen um Dutzende Zählwerte Versorgungsrauschen; mitteln und Versorgungsschienen prüfen
Spannung konstant um wenige Prozent daneben Unkalibrierte interne Referenz – einmal messen
Erster Messwert nach analogReference() ist falsch Erwartet; verwerfen
Liniensensor-Kalibrierung driftet mit leerem Akku Schienenreferenzierte Messwerte auf fallender Schiene
Nur 15 % des Bereichs genutzt Teiler für 5 V dimensioniert, aber gegen 1,1 V gelesen

Die Regel, die das meiste davon verhindert: Entscheide erst, was deine Referenz ist, bevor du sonst etwas entscheidest. Jede Zahl des ADC ist relativ zu ihr, eine wandernde Referenz macht also aus jedem Messwert eine Geschichte über zwei Dinge zugleich.

Den Graphen erkunden

Teil dieser Bauprojekte

Projekte und Lernpfade, die dieses Tutorial enthalten.

Weiterführende Literatur

Quellen