4.14. Nozioni di base sulla CSI¶
Il modulo csi è il modo in cui il codice Python pilota il sensore della camera. Ogni script che acquisisce un frame segue la stessa struttura in tre parti: gli import in cima, la configurazione una tantum nel mezzo, e un loop while True in fondo che preleva i frame dalla camera uno alla volta.
4.14.1. Il loop tipico¶
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. Cosa fa ogni chiamata¶
import csi, image, timeImporta tre moduli.
csicontrolla il sensore,imagedefinisce la classeImagechesnapshot()restituisce, etimefornisce l’helpertime.clock()usato per misurare i frame al secondo.csi.CSI()Costruisce un’istanza
CSIche incapsula un sensore fisico della camera. Il costruttore reclama la periferica della camera e registra la configurazione specifica del sensore. Le camere con un singolo sensore hanno un’istanzaCSI; le camere con due sensori (colore più termico, colore più eventi) ne hanno due, ciascuna selezionata tramite un argomentocidpassato al costruttore.csi0.reset()Alimenta e configura il sensore. Per impostazione predefinita invia un impulso al pin di reset del sensore, poi scrive i registri I2C del sensore in uno stato iniziale noto. Le successive chiamate di configurazione –
pixformat,framesize, le manopole di controllo automatico – inviano altre scritture nei registri sullo stesso bus di controllo I2C.csi0.pixformat(csi.RGB565)Scrive i registri del sensore che selezionano il formato dei pixel di output. Le scelte disponibili sono i formati introdotti nella pagina formati dei pixel:
RGB565,GRAYSCALE,BAYER,YUV422eJPEGsui sensori che lo supportano.csi0.framesize(csi.QVGA)Scrive i registri che selezionano la risoluzione di output.
QVGAè 320 × 240; le dimensioni con nome arrivano fino aWQXGA2(2592 × 1944, circa 5 MP) sui sensori che le supportano. Funziona anche una tupla(width, height)personalizzata, purché sia compatibile con le capacità di output del sensore.clock = time.clock()Crea un helper clock. Ogni chiamata a
clock.tick()all’interno del loop registra l’ora di inizio dell’iterazione;time.clock.fps()riporta il tasso recente del loop in frame al secondo.img = csi0.snapshot()Acquisisce un frame dal sensore e lo restituisce come
Image. Vale la pena esaminare più da vicino il meccanismo con cui quel frame finisce in memoria.
4.14.3. Come snapshot riempie la memoria¶
Il sensore consegna i pixel sul bus dei dati dei pixel descritto in bus del sensore a velocità di centinaia di megabyte al secondo – troppo veloce perché la CPU li copi pixel per pixel via software.
Invece, l’MCU delega il trasferimento al Direct Memory Access (DMA) – un motore hardware separato dalla CPU che copia i byte da un punto all’altro all’interno dell’MCU senza coinvolgere affatto la CPU. La periferica di input della camera cattura ogni byte di pixel in arrivo in una piccola FIFO sul chip; gli stadi ISP eseguiti sul lato MCU elaborano i dati lungo il percorso; e il motore DMA scrive i pixel finiti in un framebuffer in RAM all’offset di pixel corrispondente. Nulla in quella catena ha bisogno della CPU una volta che il canale DMA è stato programmato.
Quando viene chiamato snapshot():
Il driver CSI programma il motore DMA con l’indirizzo del framebuffer, la lunghezza del trasferimento (i pixel di un intero frame) e una callback per l’interrupt di completamento del DMA.
Il driver abilita la periferica di input della camera e attende che il sensore segnali l’inizio del frame successivo.
Mentre il sensore trasmette il frame, la periferica passa ogni byte di pixel attraverso l’ISP e poi al motore DMA, che scrive il risultato in RAM al successivo offset del framebuffer. La CPU è libera di eseguire altro codice durante il trasferimento.
Quando arriva l’ultimo pixel del frame, il DMA genera il suo interrupt di completamento, il driver incapsula il framebuffer in un
Image, esnapshot()lo restituisce al codice utente.
L”Image restituito non possiede una copia dei dati dei pixel – punta a uno dei framebuffer della camera in RAM. Quanti framebuffer la camera mantiene, e come vengono passati tra il DMA e il codice utente a ogni chiamata a snapshot(), dipende dalla modalità di buffering che l’applicazione ha selezionato tramite framebuffers().