4.14. CSI alapok

A csi modullal a Python kód a kamera érzékelőjét vezérli. Minden szkript, amely képkockát rögzít, ugyanazt a háromrészes alakot követi: az importok legfelül, az egyszeri konfiguráció középen, és egy while True ciklus legalul, amely egyenként húzza be a képkockákat a kamerából.

4.14.1. A jellemző ciklus

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. Mit csinál az egyes hívások

import csi, image, time

Behoz három modult. A csi az érzékelőt vezérli, az image definiálja az Image osztályt, amelyet a snapshot() ad vissza, az time pedig biztosítja a másodpercenkénti képkockák mérésére használt time.clock() segédfüggvényt.

csi.CSI()

Létrehoz egy CSI példányt, amely egy fizikai kamerás érzékelőt csomagol be. A konstruktor lefoglalja a kamera perifériát, és rögzíti az érzékelőnkénti konfigurációt. Az egyetlen érzékelővel rendelkező kameráknak egy CSI példányuk van; a két érzékelővel rendelkező kameráknak (szín plusz hő, szín plusz esemény) kettő, amelyeket a konstruktor egy cid argumentuma választ ki.

csi0.reset()

Tápfeszültséget ad és konfigurálja az érzékelőt. Alapértelmezés szerint impulzust ad az érzékelő reset lábára, majd beírja az érzékelő I2C regisztereit egy ismert kiindulási állapotba. A további konfigurációs hívások – pixformat, framesize, az automatikus vezérlési gombok – további regiszterírásokat küldenek ugyanazon az I2C vezérlőbuszon.

csi0.pixformat(csi.RGB565)

Beírja azokat az érzékelőregisztereket, amelyek a kimeneti képpontformátumot választják ki. Az elérhető lehetőségek a képpontformátumok oldalon bemutatott formátumok: RGB565, GRAYSCALE, BAYER, YUV422 és JPEG az ezt támogató érzékelőkön.

csi0.framesize(csi.QVGA)

Beírja azokat a regisztereket, amelyek a kimeneti felbontást választják ki. A QVGA 320 × 240; a megnevezett méretek egészen a WQXGA2-ig (2592 × 1944, körülbelül 5 MP) terjednek az ezt támogató érzékelőkön. Egy egyéni (width, height) tuple is működik, amennyiben illeszkedik az érzékelő kimeneti képességeihez.

clock = time.clock()

Létrehoz egy clock segédet. A ciklusban minden clock.tick() hívás rögzíti az iteráció kezdési idejét; a time.clock.fps() a közelmúltbeli ciklussebességet jelenti másodpercenkénti képkockákban.

img = csi0.snapshot()

Rögzít egy képkockát az érzékelőről, és Image objektumként adja vissza. Érdemes közelebbről megnézni annak mechanikáját, ahogyan az a képkocka a memóriába kerül.

4.14.3. Hogyan tölti fel a snapshot a memóriát

Az érzékelő a érzékelőbuszok oldalon leírt képpontadat-buszon másodpercenként több száz megabájtos sebességgel szállítja a képpontokat – ez túlságosan gyors ahhoz, hogy a CPU képpontról képpontra szoftveresen másolja.

Ehelyett az MCU a közvetlen memóriahozzáférésre (DMA) hárítja az átvitelt – ez a CPU-tól független hardveres motor, amely a CPU bevonása nélkül másol bájtokat egyik helyről a másikra az MCU-n belül. A kamera bemeneti perifériája minden beérkező képpontbájtot egy kis chipen lévő FIFO-ba fog be; az MCU-oldalon futó ISP-szakaszok útközben feldolgozzák az adatot; a DMA-motor pedig a megfelelő képpont-eltolásnál a RAM-ban lévő képkocka-pufferbe írja a kész képpontokat. Ebben a láncban semmihez sincs szükség a CPU-ra, miután a DMA-csatornát beprogramozták.

Amikor a snapshot() meghívásra kerül:

  1. A CSI meghajtó beprogramozza a DMA-motort a képkocka-puffer címével, az átvitel hosszával (egy képkockányi képpont) és egy visszahívással a DMA-kész megszakításhoz.

  2. A meghajtó engedélyezi a kamera bemeneti perifériáját, és megvárja, amíg az érzékelő jelzi a következő képkocka kezdetét.

  3. Ahogy az érzékelő kiküldi a képkockát, a periféria minden képpontbájtot átad az ISP-n keresztül a DMA-motornak, amely a következő képkocka-puffer eltolásnál a RAM-ba írja az eredményt. A CPU szabadon futtathat más kódot az átvitel alatt.

  4. Amikor a képkocka utolsó képpontja megérkezik, a DMA kiváltja a kész megszakítását, a meghajtó becsomagolja a képkocka-puffert egy Image objektumba, és a snapshot() visszaadja azt a felhasználói kódnak.

A visszaadott Image nem birtokol másolatot a képpontadatokról – a kamera valamelyik RAM-ban lévő képkocka-pufferére mutat. Hogy a kamera hány képkocka-puffert tart fenn, és hogy ezeket a DMA és a felhasználói kód között hogyan adják át a snapshot() minden hívásánál, attól a pufferelési módtól függ, amelyet az alkalmazás a framebuffers() segítségével választott ki.