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.
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: 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 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 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 kΩ, R2 = 10 kΩ. 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). MetR1 + R2 = 43 kΩenV_in = 12 Vis 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_ineen 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.