4.14. Dasar-dasar CSI

Modul csi adalah cara kode Python mengendalikan sensor kamera. Setiap skrip yang menangkap bingkai mengikuti bentuk tiga bagian yang sama: impor di bagian atas, konfigurasi satu kali di tengah, dan loop while True di bagian bawah yang mengambil bingkai dari kamera satu per satu.

4.14.1. Loop yang umum

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. Apa yang dilakukan setiap panggilan

import csi, image, time

Membawa tiga modul. csi mengontrol sensor, image mendefinisikan kelas Image yang dikembalikan oleh snapshot(), dan time menyediakan helper time.clock() yang digunakan untuk mengukur bingkai per detik.

csi.CSI()

Membangun instansi CSI yang membungkus satu sensor kamera fisik. Konstruktor mengklaim periferal kamera dan merekam konfigurasi per-sensor. Kamera dengan satu sensor memiliki satu instansi CSI; kamera dengan dua sensor (warna plus termal, warna plus peristiwa) memiliki dua, masing-masing dipilih oleh argumen cid pada konstruktor.

csi0.reset()

Memberi daya dan mengonfigurasi sensor. Secara default, ia menyentakkan pin reset sensor, lalu menulis register I2C sensor ke kondisi awal yang diketahui. Panggilan konfigurasi berikutnya -- pixformat, framesize, knob kontrol otomatis -- mendorong penulisan register lebih lanjut melalui bus kontrol I2C yang sama.

csi0.pixformat(csi.RGB565)

Menulis register sensor yang memilih format piksel keluaran. Pilihan yang tersedia adalah format yang diperkenalkan halaman format piksel: RGB565, GRAYSCALE, BAYER, YUV422, dan JPEG pada sensor yang mendukungnya.

csi0.framesize(csi.QVGA)

Menulis register yang memilih resolusi keluaran. QVGA adalah 320 × 240; ukuran bernama berjalan hingga WQXGA2 (2592 × 1944, sekitar 5 MP) pada sensor yang mendukungnya. Tuple (width, height) kustom juga dapat digunakan, selama sesuai dengan kemampuan keluaran sensor.

clock = time.clock()

Membuat helper clock. Setiap panggilan ke clock.tick() di dalam loop merekam waktu mulai iterasi; time.clock.fps() melaporkan laju loop terkini dalam bingkai per detik.

img = csi0.snapshot()

Menangkap satu bingkai dari sensor dan mengembalikannya sebagai Image. Mekanika bagaimana bingkai tersebut berakhir di memori layak dilihat lebih dekat.

4.14.3. Bagaimana snapshot mengisi memori

Sensor mengirimkan piksel pada bus data piksel yang dijelaskan di bus sensor pada kecepatan ratusan megabyte per detik -- jauh terlalu cepat untuk disalin CPU piksel per piksel dalam perangkat lunak.

Sebagai gantinya, MCU mendelegasikan transfer ke Direct Memory Access (DMA) -- mesin perangkat keras terpisah dari CPU yang menyalin byte dari satu tempat ke tempat lain di dalam MCU tanpa melibatkan CPU sama sekali. Periferal masukan kamera menangkap setiap byte piksel masuk ke dalam FIFO kecil di dalam chip; tahap ISP mana pun yang berjalan di sisi MCU memproses data saat melewatinya; dan mesin DMA menulis piksel yang sudah selesai ke dalam framebuffer di RAM pada offset piksel yang sesuai. Tidak ada yang dalam rantai tersebut memerlukan CPU setelah saluran DMA diprogram.

Saat snapshot() dipanggil:

  1. Driver CSI memprogram mesin DMA dengan alamat framebuffer, panjang transfer (sejumlah piksel satu bingkai), dan callback untuk interupsi DMA-done.

  2. Driver mengaktifkan periferal masukan kamera dan menunggu sensor memberi sinyal awal bingkai berikutnya.

  3. Saat sensor mengalirkan bingkai keluar, periferal meneruskan setiap byte piksel melalui ISP dan ke mesin DMA, yang menulis hasilnya ke RAM pada offset framebuffer berikutnya. CPU bebas menjalankan kode lain selama transfer.

  4. Saat piksel terakhir bingkai tiba, DMA mengaktifkan interupsi done-nya, driver membungkus framebuffer dalam Image, dan snapshot() mengembalikannya ke kode pengguna.

Image yang dikembalikan tidak memiliki salinan data piksel -- ia menunjuk ke salah satu framebuffer kamera di RAM. Berapa banyak framebuffer yang disimpan kamera, dan bagaimana mereka diserahkan antara DMA dan kode pengguna pada setiap panggilan ke snapshot(), bergantung pada mode buffering yang dipilih aplikasi melalui framebuffers().