3.12. Analoge Werte mit dem ADC einlesen

Bisher hat die Kamera digitale Signale gelesen – ein Pin ist entweder 0 oder 1, ein Schalter ist offen oder geschlossen. Die meisten Signale, die von realen Sensoren kommen, sind jedoch analog: eine kontinuierliche Spannung, die sich über einen bestimmten Bereich gleichmäßig ändert. Ein Fotowiderstand durchläuft jede Spannung zwischen den Versorgungsschienen, während sich die Umgebungshelligkeit ändert. Der Ausgang eines Temperatursensors driftet um einige Millivolt, wenn sich ein Raum aufheizt. Der Ausgang eines Mikrofons steigt und fällt mit den Geräuschen in seiner Umgebung.

Ein Analog-Digital-Wandler (ADC) bildet die Brücke. Er tastet die Spannung an einem Pin ab und liefert eine Ganzzahl zurück, die Python wie jeden anderen Wert lesen kann.

3.12.1. Quantisierung

Ein digitaler Wert kann eine kontinuierliche Spannung nicht exakt darstellen. Die Aufgabe des ADC ist die Quantisierung – jeden Abtastwert auf den nächstgelegenen aus einer festen Menge von Stufen einzurasten. Ein N-Bit-ADC besitzt 2^N Stufen; ein 12-Bit-Wandler hat 4096 davon, die über seinen Eingangsbereich verteilt sind.

Eine glatte analoge Kurve, aufgetragen über die Zeit, überlagert von einer gestuften digitalen Näherung. Gestrichelte waagerechte Linien markieren die Quantisierungsstufen; die gestufte Kurve rastet an jedem Abtastpunkt auf diejenige Stufe ein, die dem analogen Signal am nächsten liegt.

Quantisierung: Jeder Abtastwert des analogen Signals (durchgezogen) wird auf eine aus einer endlichen Menge von digitalen Stufen (gestufte gestrichelte Linie) gerundet.

Die Spannung zwischen zwei benachbarten Stufen ist die Schrittweite des ADC; alles, was kleiner ist, verschwindet in der Rundung. Ein 12-Bit-ADC über einen Bereich von 3,3 V hat eine Schrittweite von etwa 3.3 / 4096 0.8 mV – fein genug, dass die meisten Signale in der Software praktisch kontinuierlich erscheinen.

3.12.2. Die Klasse machine.ADC

machine.ADC kapselt einen analogen Eingangskanal. Erzeugen Sie sie mit dem Pin, den Sie lesen möchten, und rufen Sie dann read_u16() auf:

from machine import ADC

adc = ADC("P6")
value = adc.read_u16()
print(value)

read_u16() liefert stets eine vorzeichenlose 16-Bit-Ganzzahl zwischen 0 und 65535 zurück. Die native ADC-Auflösung variiert je nach Board (12 Bit auf STM32, andernorts portspezifisch); das Ergebnis wird linksbündig in 16 Bit ausgerichtet, sodass das Hardware-Detail nicht nach Python durchsickert – ein Wert von 65535 entspricht unabhängig vom Chip dem Vollausschlag.

Die Referenzspannung – der Eingang, der dem Vollausschlag entspricht – hängt vom Board ab. Schauen Sie in der OpenMV-Boards nach dem Wert für Ihre Kamera. Alles oberhalb der Referenz wird als Vollausschlag gelesen (und kann den Pin beschädigen, wenn es die absolute Maximaleingangsspannung überschreitet).

3.12.2.1. Zählwerte in Spannung umrechnen

Die Abbildung von Zählwerten auf Spannung ist linear, wobei die Zählwerte des Vollausschlags exakt auf Vref abgebildet werden:

voltage = counts × Vref / 65535

Im Code:

VREF = 3.3  # cam-dependent; see the quickref
counts = adc.read_u16()
voltage = counts * VREF / 65535
print(voltage, "V")

3.12.3. Spannungsteiler

Zwei in Reihe geschaltete Widerstände zwischen einer Versorgungsschiene und Masse bilden einen Spannungsteiler. Der Knoten zwischen ihnen liegt auf einer Spannung, die durch das Verhältnis der beiden Widerstände bestimmt wird:

Ein Spannungsteiler. Vin oben verbindet sich über R1 mit einem Knoten, der als V_out abgegriffen wird und sich dann über R2 mit Masse verbindet.

Ein Spannungsteiler: R1 und R2 in Reihe teilen Vin auf V_out herunter.

V_out = Vin × R2 / (R1 + R2)

Gleiche Widerstände ergeben die halbe Schienenspannung; ist R2 deutlich kleiner als R1, liegt der Abgriff nahe an Masse; ist R2 deutlich größer, liegt er nahe an der Schiene.

Die Formel setzt voraus, dass nichts anderes nennenswerten Strom aus V_out zieht. Ein ADC-Pin ist hochohmig (Megaohm, Nanoampere) und erfüllt dies problemlos, sodass sich ein Teiler, der einen ADC speist, genau so verhält, wie es die Formel vorhersagt.

3.12.4. Potentiometer

Ein Potentiometer ist ein einzelnes physisches Bauteil, das genau einem Spannungsteiler entspricht, mit einem verschiebbaren Schleifer, der den Abgriff zwischen den beiden Enden bewegt. Das Drehen des Knopfes verändert R1 und R2 gemeinsam, während ihre Summe (der Gesamtwiderstand des Potis) konstant bleibt.

Ein Potentiometer, das zwischen 3,3 V und Masse verschaltet ist. Der Schleifer ist auf einen ADC-Pin abgegriffen.

Ein Potentiometer, das als manuelle Spannungsquelle für den ADC verschaltet ist: 3,3 V an einem Ende, Masse am anderen, Schleifer zum Pin.

Ein Poti ist das klassische Eingabegerät zum Ausprobieren des ADC. Verbinden Sie ein Ende mit 3.3 V, das andere mit Masse und den Schleifer mit einem ADC-fähigen Pin; das Drehen des Knopfes führt den Schleifer durch jede Spannung zwischen den Schienen.

import time
from machine import ADC

pot = ADC("P6")
VREF = 3.3

while True:
    counts = pot.read_u16()
    voltage = counts * VREF / 65535
    print(voltage, "V")
    time.sleep_ms(100)

3.12.5. Höhere Spannungen mit einem Teiler messen

Eine Spannung oberhalb von Vref zwingt den ADC auf Vollausschlag und kann den Eingang beschädigen, wenn sie die absolute Maximalbewertung überschreitet. Um eine höhere Quelle zu messen – eine Batterie, einen Sensorausgang, der über Vref hinausreicht – teilen Sie sie mit einem festen Spannungsteiler herunter, bevor sie den Pin erreicht:

Ein Spannungsteiler, der ein hohes V_in auf einen ADC-Pin herunterteilt. R1 verläuft von V_in hinab zu einem Knotenpunkt, der waagerecht zum ADC-Pin abgegriffen wird; R2 setzt sich vom Knotenpunkt aus hinab zur Masse fort.

Eine Hochspannungsquelle auf den ADC anpassen: R1 und R2 bilden einen festen Spannungsteiler, dessen Abgriff den ADC-Pin speist.

Wählen Sie R1 und R2 so, dass die geteilte Spannung bei der höchsten erwarteten Eingangsspannung innerhalb des ADC-Bereichs bleibt:

V_adc = V_in × R2 / (R1 + R2)

Für eine maximale V_in = 12 V und eine Referenz von 3,3 V darf das Verhältnis R2 / (R1 + R2) höchstens 3.3 / 12 0.275 betragen. Eine gängige Wahl mit etwas Reserve ist R1 = 33 , R2 = 10 . Das Verhältnis beträgt 10 / 43 0.233, sodass V_adc bei etwa 12 × 0.233 2.79 V endet – sicher unterhalb von Vref.

Um das ursprüngliche V_in aus einem ADC-Messwert wiederzugewinnen, kehren Sie die Teilerformel um:

V_in = V_adc × (R1 + R2) / R2

Im Code:

from machine import ADC

R1 = 33_000
R2 = 10_000
VREF = 3.3

adc = ADC("P6")

counts = adc.read_u16()
v_adc = counts * VREF / 65535
v_in = v_adc * (R1 + R2) / R2
print(v_in, "V")

Einige praktische Hinweise:

  • Der Teiler zieht ständig V_in / (R1 + R2). Bei R1 + R2 = 43 und V_in = 12 V sind das etwa 280 µA – meist vernachlässigbar, doch wenn die Quelle batteriebetrieben ist, sollten Sie größere Widerstände (100 kΩ bis 1 MΩ) erwägen, um den Ruhestrom zu senken.

  • Die Widerstandstoleranz (typischerweise ±1 % oder ±5 %) geht direkt in die Messgenauigkeit ein. Zwei ±5-%-Widerstände können dem wiedergewonnenen V_in einen Fehler von bis zu rund ±10 % bescheren.

  • Die Quellimpedanz des Teilers bildet zusammen mit jeder Streukapazität einen Tiefpassfilter für den Eingang. Bei schnell veränderlichen Signalen spielt das eine Rolle; bei einer Batteriespannungsprüfung nicht.