4.14. Základy CSI

Modul csi je způsob, jakým Python kód řídí senzor kamery. Každý skript, který zachytává snímek, má stejnou třídílnou strukturu: importy nahoře, jednorázová konfigurace uprostřed a smyčka while True dole, která vytahuje snímky z kamery jeden po druhém.

4.14.1. Typická smyčka

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. Co dělá každé volání

import csi, image, time

Načte tři moduly. csi ovládá senzor, image definuje třídu Image, kterou vrací snapshot(), a time poskytuje pomocníka time.clock() používaného k měření počtu snímků za sekundu.

csi.CSI()

Vytvoří instanci CSI, která obaluje jeden fyzický senzor kamery. Konstruktor zabere periferii kamery a zaznamená konfiguraci specifickou pro daný senzor. Kamery s jediným senzorem mají jednu instanci CSI; kamery se dvěma senzory (barevný plus termální, barevný plus událostní) mají dvě, každou vybranou argumentem cid konstruktoru.

csi0.reset()

Napájí a konfiguruje senzor. Ve výchozím nastavení vyšle puls na resetovací pin senzoru a poté zapíše I2C registry senzoru do známého počátečního stavu. Následná konfigurační volání – pixformat, framesize, ovládací prvky automatiky – posílají další zápisy do registrů přes stejnou řídicí sběrnici I2C.

csi0.pixformat(csi.RGB565)

Zapíše registry senzoru, které vyberou výstupní formát pixelů. Dostupné volby jsou formáty představené na stránce formáty pixelů: RGB565, GRAYSCALE, BAYER, YUV422 a JPEG na senzorech, které jej podporují.

csi0.framesize(csi.QVGA)

Zapíše registry, které vyberou výstupní rozlišení. QVGA je 320 × 240; pojmenované velikosti sahají až po WQXGA2 (2592 × 1944, přibližně 5 MP) na senzorech, které je podporují. Funguje také vlastní n-tice (width, height), pokud odpovídá výstupním možnostem senzoru.

clock = time.clock()

Vytvoří pomocníka clock. Každé volání clock.tick() uvnitř smyčky zaznamená čas začátku iterace; time.clock.fps() hlásí nedávnou rychlost smyčky v počtu snímků za sekundu.

img = csi0.snapshot()

Zachytí jeden snímek ze senzoru a vrátí jej jako Image. Mechanika toho, jak se tento snímek dostane do paměti, stojí za bližší pohled.

4.14.3. Jak snapshot plní paměť

Senzor dodává pixely na datové sběrnici pixelů popsané v sběrnice senzoru rychlostí stovek megabajtů za sekundu – příliš rychle na to, aby je CPU kopíroval pixel po pixelu v softwaru.

Místo toho MCU přenechá přenos přímému přístupu do paměti (DMA) – hardwarovému stroji odděleného od CPU, který kopíruje bajty z jednoho místa na druhé uvnitř MCU bez jakéhokoli zapojení CPU. Vstupní periferie kamery zachytává každý příchozí bajt pixelu do malé čipové fronty FIFO; libovolné stupně ISP, které běží na straně MCU, data cestou zpracují; a stroj DMA zapisuje hotové pixely do framebufferu v RAM na odpovídající offset pixelu. Nic v tomto řetězci nepotřebuje CPU, jakmile je kanál DMA naprogramován.

Když je zavoláno snapshot():

  1. Ovladač CSI naprogramuje stroj DMA s adresou framebufferu, délkou přenosu (pixely jednoho snímku) a callbackem pro přerušení dokončení DMA.

  2. Ovladač povolí vstupní periferii kamery a čeká, až senzor signalizuje začátek dalšího snímku.

  3. Jak senzor vysílá snímek ven, periferie předává každý bajt pixelu skrz ISP a dále stroji DMA, který zapisuje výsledek do RAM na další offset framebufferu. CPU je během přenosu volný a může vykonávat jiný kód.

  4. Když dorazí poslední pixel snímku, DMA vyvolá své přerušení dokončení, ovladač obalí framebuffer do Image a snapshot() jej vrátí uživatelskému kódu.

Vrácený Image nevlastní kopii dat pixelů – ukazuje na jeden z framebufferů kamery v RAM. Kolik framebufferů kamera udržuje a jak se předávají mezi DMA a uživatelským kódem při každém volání snapshot(), závisí na režimu bufferování, který aplikace vybrala prostřednictvím framebuffers().