4.14. Grunderna i CSI

Modulen csi är hur Python-kod styr kamerasensorn. Varje skript som fångar en bildruta följer samma tredelade form: importer högst upp, engångskonfiguration i mitten och en while True-slinga längst ner som hämtar bildrutor från kameran en i taget.

4.14.1. Den typiska slingan

import csi, image, time

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()
    # process img here
    print(clock.fps())

4.14.2. Vad varje anrop gör

import csi, image, time

Tar in tre moduler. csi styr sensorn, image definierar klassen Image som snapshot() returnerar, och time tillhandahåller hjälpfunktionen time.clock() som används för att mäta bildrutor per sekund.

csi.CSI()

Konstruerar en CSI-instans som omsluter en fysisk kamerasensor. Konstruktorn tar i anspråk kamerakringutrustningen och registrerar den sensorvisa konfigurationen. Kameror med en enda sensor har en CSI-instans; kameror med två sensorer (färg plus termisk, färg plus händelse) har två, var och en vald med ett cid-argument till konstruktorn.

csi0.reset()

Strömsätter och konfigurerar sensorn. Som standard pulsar den sensorns reset-stift och skriver sedan sensorns I2C-register till ett känt startläge. Efterföljande konfigurationsanrop – pixformat, framesize, autostyrningsrattarna – skickar fler registerskrivningar över samma I2C-styrbuss.

csi0.pixformat(csi.RGB565)

Skriver de sensorregister som väljer utdatapixelformatet. De tillgängliga valen är de format som sidan pixelformat introducerade: RGB565, GRAYSCALE, BAYER, YUV422 och JPEG på de sensorer som stöder det.

csi0.framesize(csi.QVGA)

Skriver de register som väljer utdataupplösningen. QVGA är 320 × 240; de namngivna storlekarna går upp till WQXGA2 (2592 × 1944, ungefär 5 MP) på sensorer som stöder dem. En egen (width, height)-tupel fungerar också, så länge den stämmer med sensorns utdataförmåga.

clock = time.clock()

Skapar en clock-hjälpare. Varje anrop till clock.tick() inuti slingan registrerar iterationens starttid; time.clock.fps() rapporterar den senaste slinghastigheten i bildrutor per sekund.

img = csi0.snapshot()

Fångar en bildruta från sensorn och returnerar den som en Image. Mekaniken bakom hur den bildrutan hamnar i minnet är värd en närmare titt.

4.14.3. Hur snapshot fyller minnet

Sensorn levererar pixlar på pixeldatabussen som beskrivs i sensorbussar med hastigheter på hundratals megabyte per sekund – alldeles för snabbt för att CPU:n ska kopiera pixel för pixel i mjukvara.

I stället avlastar MCU:n överföringen till Direct Memory Access (DMA) – en hårdvarumotor skild från CPU:n som kopierar bytes från en plats till en annan inuti MCU:n utan att alls involvera CPU:n. Kameraingångskringutrustningen fångar varje inkommande pixelbyte i en liten FIFO på chippet; de ISP-steg som körs på MCU-sidan bearbetar data på vägen igenom; och DMA-motorn skriver de färdiga pixlarna till en bildbuffert i RAM vid motsvarande pixeloffset. Inget i den kedjan behöver CPU:n när väl DMA-kanalen har programmerats.

När snapshot() anropas:

  1. CSI-drivrutinen programmerar DMA-motorn med bildbuffertens adress, överföringslängden (en bildrutas värde av pixlar) och ett återanrop för DMA-klart-avbrottet.

  2. Drivrutinen aktiverar kameraingångskringutrustningen och väntar på att sensorn ska signalera starten på nästa bildruta.

  3. När sensorn strömmar ut bildrutan skickar kringutrustningen varje pixelbyte genom ISP:n och vidare till DMA-motorn, som skriver resultatet till RAM vid nästa bildbuffertoffset. CPU:n är fri att köra annan kod under överföringen.

  4. När bildrutans sista pixel anländer utlöser DMA:n sitt klart-avbrott, drivrutinen omsluter bildbufferten i en Image, och snapshot() returnerar den till användarkoden.

Den Image som returneras äger inte en kopia av pixeldata – den pekar på en av kamerans bildbuffertar i RAM. Hur många bildbuffertar kameran håller, och hur de lämnas mellan DMA:n och användarkoden vid varje anrop till snapshot(), beror på det buffringsläge som tillämpningen har valt via framebuffers().