8.14. AsyncCSI¶
Ett typiskt OpenMV Cam-skript slutar med while True: img = csi0.snapshot() – en blockerande stillbildsloop som inte alls behöver asyncio. I samma ögonblick som applikationen måste göra något annat parallellt med tagningarna – lyssna efter en knapp, skicka data till en motpart, köra en bakgrundsuppgift – står det blockerande anropet i vägen. Medan snapshot väntar på nästa bildruta körs inte händelseloopen, så varje annan korutin i programmet är frusen tills bildrutan kommer.
Den här sidan bygger ett litet omslag kring CSI som förvandlar snapshot till en await-vänlig korutin. Resultatet är en direkt ersättning som låter en tagningsloop samexistera med resten av ett asyncio-program.
8.14.1. Delarna¶
En del av CSI-API:et gör det mesta av jobbet – snapshot() i sitt icke-blockerande läge. Att anropa snapshot(blocking=False) returnerar antingen nästa bildruta (om en är klar) eller None (om inte). Det första icke-blockerande anropet startar också kamerans DMA-tagning om den inte redan kördes, så omslaget behöver inte göra något särskilt för att komma igång.
Den andra delen är asyncio.sleep_ms(). Omslaget pollar icke-blockerande stillbilder i en loop och lämnar över till händelseloopen med await asyncio.sleep_ms(0) mellan kontrollerna, så att varje annan redo korutin får en chans att köra innan nästa pollning.
8.14.2. Omslaget¶
import asyncio
import csi
class AsyncCSI:
def __init__(self, *args, **kwargs):
self._csi = csi.CSI(*args, **kwargs)
def __getattr__(self, name):
return getattr(self._csi, name)
async def snapshot(self):
while True:
img = self._csi.snapshot(blocking=False)
if img is not None:
return img
await asyncio.sleep_ms(0)
Konstruktorn omsluter en CSI-instans. __getattr__ vidarebefordrar varje attribut som omslaget inte självt definierar – reset, pixformat, framesize, alla sensorreglage – till den underliggande CSI, så omslaget ser identiskt ut med det oomslutna objektet förutom den enda metod som spelar roll.
async def snapshot är den nya delen. Den anropar snapshot(blocking=False); om anropet returnerar en bild returnerar korutinen den. Annars lämnar den tillbaka till händelseloopen med await asyncio.sleep_ms(0) så att andra korutiner får en chans att köra, och loopar sedan tillbaka och försöker igen. Den första iterationen startar DMA:n; efterföljande iterationer plockar upp bildrutor allteftersom de blir tillgängliga.
8.14.3. En stillbildsloop med sällskap¶
Med omslaget på plats passar en stillbildsloop in i ett större asyncio-program på samma sätt som vilken annan korutin som helst. Exemplet nedan kör tre korutiner samtidigt: tagningsloopen, en LED-blinkare och ett hjärtslag som skriver ut hello en gång i sekunden:
import asyncio
import csi
from machine import LED
async def capture_loop(cam):
while True:
img = await cam.snapshot()
# process img here
async def blinker(led, period_ms):
while True:
led.on()
await asyncio.sleep_ms(period_ms)
led.off()
await asyncio.sleep_ms(period_ms)
async def hello(period_s):
while True:
print("hello")
await asyncio.sleep(period_s)
async def main():
cam = AsyncCSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.QVGA)
asyncio.create_task(blinker(LED("LED_BLUE"), 200))
asyncio.create_task(hello(1))
await capture_loop(cam)
asyncio.run(main())
Alla tre korutinerna gör framsteg på samma händelseloop. Medan capture_loop lämnar över mellan icke-blockerande stillbildspollningar växlar blinker LED:en och hello skriver ut. Medan blinker och hello sover pollar capture_loop kameran. Pollningsintervallet är kort – ett enda händelseloops-tick – så det lägger till försumbar latens till när applikationen ser en ny bildruta.
Tagningsloopen blockerar inte händelseloopen. Att lägga till mer samtidigt arbete – till exempel en UART-klient – är bara ytterligare ett create_task()-anrop inuti main.
Anteckning
Inställningen framebuffers har fortfarande betydelse i den här formen. Enbuffertläge gör att snapshot(blocking=False) returnerar None tills nästa bildruta fångas; dubbel- eller trippelbuffring jämnar ut det så att omslaget vanligtvis hittar en buffrad bildruta som väntar vid den första pollningen efter att den föregående bildrutan har bearbetats.