3.1. Mikrokontroller¶
OpenMV Cam körs på en mikrokontroller (MCU): ett enda chip som kombinerar en CPU, arbetsminne (RAM), programlagring (flashminne) och en uppsättning kringutrustning – maskinvarublock för att interagera med omvärlden.
Kringutrustningen är den intressanta delen. Var och en är en bit kisel ägnad åt en uppgift: att driva ett stift högt eller lågt, att mäta en analog spänning, att klocka ut byte över en seriell buss. CPU:n konfigurerar och läser varje kringutrustning genom register – fasta minnesadresser som maskinvaran bevakar och uppdaterar.
MicroPython kapslar in dessa register i klasser inuti modulen machine. machine.Pin(...) returnerar ett objekt som styr ett general-purpose input/output-stift (GPIO) – en ledning som chipet kan hålla högt (omkring 3,3 V) eller lågt (omkring 0 V), eller läsa som ett av dessa två tillstånd när något externt driver den. machine.ADC(...) exponerar analog-till-digital-omvandlaren, som mäter spänningen på ett stift och rapporterar den som ett tal. machine.UART(...) kör en universal asynchronous receiver/transmitter (UART) – en kringutrustning som skickar och tar emot byte en bit i taget över ett par ledningar, TX (sändning) och RX (mottagning). Andra klasser täcker resten av kringutrustningen. Skriptet läser och skriver Python-objekt; MicroPython översätter varje åtkomst till motsvarande registerläsningar och -skrivningar, och dessa flyttar bitar på fysiska ledningar.
En MCU paketerar CPU, minne och kringutrustning i ett enda chip. Varje kringutrustning exponeras för Python av en klass i modulen machine.¶
3.1.1. Huvudslingan¶
Nästan alla mikrokontrollerprogram har samma form: engångskonfiguration högst upp i skriptet (importera moduler, konfigurera stift, öppna bussar), följt av en oändlig while True:-slinga längst ned. Inuti slingan läser programmet ingångar, fattar beslut och uppdaterar utgångar om och om igen. Slingan är programmet; när skriptet avslutas slutar enheten att göra någonting.
# setup, runs once
from machine import Pin
led = Pin("P0", Pin.OUT)
# main loop, runs forever
while True:
led.value(1)
# ... do work ...
led.value(0)
# ... do other work ...
Denna form – konfigurera en gång, sedan loopa för evigt – är mönstret med en huvudslinga. Allt som följer handlar om vad som ska finnas inuti den.
3.1.2. Realtidsstyrning¶
Ett skrivbordsprogram körs jämsides med många andra. Operativsystemet schemalägger dess arbete över en eller flera trådar – oberoende exekveringsströmmar som det växlar mellan millisekund för millisekund. När en tråd väntar på I/O (disk, nätverk, att användaren flyttar musen) lämnar operativsystemet CPU:n till en annan. Programmet är till största delen händelsestyrt: fönsterhanteraren anropar din kod när inmatning anländer, HTTP-biblioteket återupptar din kod när byte anländer på socketen. Något större anropar dig.
Ett mikrokontrollerprogram är det motsatta. Som standard finns inget operativsystem, ingen schemaläggare och ingen annan tråd. Huvudslingan som just visades är den enda slingan. Kringutrustning utlöser avbrott eller exponerar statusflaggor; slingan pollar dem eller hanterar avbrotten direkt. Om slingan fastnar i ett time.sleep_ms(1000) gör enheten ingenting under den sekunden; det finns ingen annan tråd som fyller luckan.
Två konsekvenser följer och gäller överallt:
Tiden är verklig. Att läsa ett stift två gånger i en tät slinga tar mikrosekunder; att sova i tio millisekunder innebär tio millisekunder då ingenting annat händer. Mönstret icke-blockerande tidtagning är svaret.
Maskinvaran är verklig. Att sätta
machine.Pin.valuetill1lägger ungefär 3,3 V på en fysisk ledning; att sätta det till0lägger ungefär 0 V där. Andra delar av kretsen ser den spänningen omedelbart – inklusive komponenter som stiftet kan skada om det drivs felaktigt.