3.12. Analoog lezen met de ADC

Tot nu toe heeft de camera digitale signalen gelezen – een pin is ofwel 0 of 1, een schakelaar is open of gesloten. De meeste signalen die van echte sensoren afkomen zijn analoog: een continue spanning die geleidelijk over een bepaald bereik varieert. Een fotoweerstand doorloopt elke spanning tussen de rails naarmate de omgevingshelderheid verandert. De uitgang van een temperatuursensor verschuift een paar millivolt als een ruimte opwarmt. De uitgang van een microfoon stijgt en daalt met het geluid eromheen.

Een analoog-naar-digitaal-omzetter (ADC) is de brug. Hij bemonstert de spanning op een pin en geeft een geheel getal terug dat Python net als elke andere waarde kan lezen.

3.12.1. Kwantisatie

Een digitale waarde kan een continue spanning niet exact weergeven. De taak van de ADC is kwantiseren – elk monster vastklikken op het dichtstbijzijnde van een vaste set niveaus. Een N-bit ADC heeft 2^N niveaus; een 12-bit omzetter heeft er 4096 verspreid over zijn ingangsbereik.

Een vloeiende analoge curve uitgezet tegen de tijd, overlay met een getrapte digitale benadering. Gestreepte horizontale lijnen markeren de kwantisatieniveaus; de getrapte curve klikt op het niveau dat het dichtst bij het analoge signaal ligt op elk bemonsteringspunt.

Kwantisatie: elk monster van het analoge signaal (doorgetrokken) wordt afgerond naar een van een eindige set digitale niveaus (getrapte gestreepte lijn).

De spanning tussen twee aangrenzende niveaus is de stapgrootte van de ADC; alles wat kleiner is verdwijnt in afronding. Een 12-bit ADC over een bereik van 3,3 V heeft een stapgrootte van ongeveer 3.3 / 4096 0.8 mV – fijn genoeg dat de meeste signalen in software vrijwel continu lijken.

3.12.2. De klasse machine.ADC

machine.ADC omhult één analoog ingangskanaal. Construeer hem met de pin die je wilt lezen en roep dan read_u16() aan:

from machine import ADC

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

read_u16() geeft altijd een unsigned 16-bit geheel getal terug tussen 0 en 65535. De native ADC-resolutie verschilt per board (12-bit op STM32, poortspecifiek elders); het resultaat wordt linksuitgelijnd in 16 bits zodat het hardwaredetail niet naar Python lekt – een waarde van 65535 is volle schaal ongeacht de chip.

De referentiespanning – de ingang die overeenkomt met volle schaal – hangt af van het board. Raadpleeg de OpenMV-boards voor de waarde op jouw cam. Alles boven de referentie leest als volle schaal (en kan de pin beschadigen als het de absolute maximale ingangsspanning overschrijdt).

3.12.2.1. Tellingen omzetten naar spanning

De afbeelding van tellingen naar spanning is lineair, waarbij tellingen op volle schaal exact overeenkomen met Vref:

voltage = counts × Vref / 65535

In code:

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

3.12.3. Spanningsdelers

Twee weerstanden in serie tussen een spanningsrail en aarde vormen een spanningsdeler. Het knooppunt ertussen bevindt zich op een spanning die wordt bepaald door de verhouding van de twee weerstanden:

Een spanningsdeler. Vin bovenaan verbindt via R1 met een knooppunt dat wordt afgetakt als V_out, dat vervolgens verbindt via R2 met aarde.

Een spanningsdeler: R1 en R2 in serie schalen Vin omlaag naar V_out.

V_out = Vin × R2 / (R1 + R2)

Gelijke weerstanden geven de helft van de railspanning; R2 veel kleiner dan R1 plaatst de aftakking dicht bij aarde; R2 veel groter plaatst hem dicht bij de rail.

De formule veronderstelt dat niets anders noemenswaardige stroom uit V_out trekt. Een ADC-pin is hoogohmig (megaohm, nanoampère) en voldoet daar gemakkelijk aan, dus een deler die een ADC voedt gedraagt zich zoals de formule voorspelt.

3.12.4. Potentiometers

Een potentiometer is een enkel fysiek component dat precies een spanningsdeler is, met een schuivende loper die de aftakking tussen de twee uiteinden verplaatst. Het draaien van de knop verandert R1 en R2 samen terwijl hun som (de totale weerstand van de pot) constant blijft.

Een potentiometer bedraad tussen 3,3 V en aarde. De loper is afgetakt naar een ADC-pin.

Een potentiometer bedraad als handmatige spanningsbron voor de ADC: 3,3 V aan één uiteinde, aarde aan het andere, loper naar de pin.

Een pot is het standaard invoerapparaat om de ADC uit te proberen. Bedraad het ene uiteinde naar 3.3 V, het andere naar aarde, en de loper naar een ADC-geschikte pin; het draaien van de knop laat de loper door elke spanning tussen de rails lopen.

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. Hogere spanningen lezen met een deler

Een spanning boven Vref zal de ADC vastzetten op volle schaal en kan de ingang beschadigen als het de absolute maximumwaarde overschrijdt. Om een hogere bron te lezen – een batterij, een sensoruitgang die buiten Vref reikt – schaal die omlaag met een vaste spanningsdeler voordat die de pin bereikt:

Een spanningsdeler die een hoge V_in omlaag schaalt naar een ADC- pin. R1 loopt van V_in omlaag naar een knooppunt, dat horizontaal wordt afgetakt naar de ADC-pin; R2 loopt verder van het knooppunt omlaag naar aarde.

Een hoogspanningsbron schalen om in de ADC te passen: R1 en R2 vormen een vaste spanningsdeler waarvan de aftakking de ADC-pin voedt.

Kies R1 en R2 zo dat de gedeelde spanning binnen het bereik van de ADC blijft bij de hoogste ingangsspanning die je verwacht:

V_adc = V_in × R2 / (R1 + R2)

Voor een maximum V_in = 12 V en een referentie van 3,3 V mag de verhouding R2 / (R1 + R2) maximaal 3.3 / 12 0.275 zijn. Een gangbare keuze met wat marge is R1 = 33 , R2 = 10 . De verhouding is 10 / 43 0.233, dus V_adc loopt op tot ongeveer 12 × 0.233 2.79 V – veilig onder Vref.

Om de oorspronkelijke V_in te herstellen uit een ADC-meting, keer je de delerformule om:

V_in = V_adc × (R1 + R2) / R2

In 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")

Een paar praktische opmerkingen:

  • De deler trekt continu V_in / (R1 + R2). Met R1 + R2 = 43 en V_in = 12 V is dat ongeveer 280 µA – meestal verwaarloosbaar, maar als de bron batterijgevoed is, overweeg dan grotere weerstanden (100 kΩ tot 1 MΩ) om de rustverbruik te verminderen.

  • Weerstandstolerantie (doorgaans ±1 % of ±5 %) werkt direct door in de meetnauwkeurigheid. Twee weerstanden van ±5 % kunnen de herstelde V_in een worst-case fout van ongeveer ±10 % geven.

  • De bronimpedantie van de deler combineert met eventuele strooicapaciteit om de ingang laagdoorlaat te filteren. Voor snel veranderende signalen maakt dat uit; voor een batterijspanningscontrole niet.