4.14. CSI-Grundlagen¶
Das Modul csi ist die Art und Weise, wie Python-Code den Kamerasensor ansteuert. Jedes Skript, das ein Einzelbild aufnimmt, folgt derselben dreiteiligen Form: Importe oben, einmalige Konfiguration in der Mitte und eine while True-Schleife unten, die Einzelbilder eines nach dem anderen aus der Kamera abruft.
4.14.1. Die typische Schleife¶
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. Was jeder Aufruf bewirkt¶
import csi, image, timeBindet drei Module ein.
csisteuert den Sensor,imagedefiniert die KlasseImage, diesnapshot()zurückgibt, undtimestellt den Helfertime.clock()bereit, der zum Messen der Bilder pro Sekunde verwendet wird.csi.CSI()Konstruiert eine
CSI-Instanz, die einen physischen Kamerasensor umhüllt. Der Konstruktor beansprucht das Kamera-Peripheriegerät und erfasst die sensorspezifische Konfiguration. Kameras mit einem einzigen Sensor haben eineCSI-Instanz; Kameras mit zwei Sensoren (Farbe plus Wärme, Farbe plus Event) haben zwei, die jeweils über eincid-Argument des Konstruktors ausgewählt werden.csi0.reset()Versorgt und konfiguriert den Sensor. Standardmäßig pulst es den Reset-Pin des Sensors und schreibt dann die I2C-Register des Sensors in einen bekannten Ausgangszustand. Nachfolgende Konfigurationsaufrufe –
pixformat,framesize, die Auto-Steuerungsknöpfe – senden weitere Register-Schreibvorgänge über denselben I2C-Steuerbus.csi0.pixformat(csi.RGB565)Schreibt die Sensorregister, die das Ausgabe-Pixelformat auswählen. Die verfügbaren Optionen sind die Formate, die die Seite Pixelformate vorgestellt hat:
RGB565,GRAYSCALE,BAYER,YUV422undJPEGauf den Sensoren, die es unterstützen.csi0.framesize(csi.QVGA)Schreibt die Register, die die Ausgabeauflösung auswählen.
QVGAist 320 × 240; die benannten Größen reichen bisWQXGA2(2592 × 1944, etwa 5 MP) auf Sensoren, die sie unterstützen. Ein benutzerdefiniertes(width, height)-Tupel funktioniert ebenfalls, solange es zu den Ausgabefähigkeiten des Sensors passt.clock = time.clock()Erzeugt einen Uhr-Helfer. Jeder Aufruf von
clock.tick()innerhalb der Schleife erfasst die Startzeit der Iteration;time.clock.fps()meldet die aktuelle Schleifenrate in Bildern pro Sekunde.img = csi0.snapshot()Nimmt ein Einzelbild vom Sensor auf und gibt es als
Imagezurück. Die Mechanik, wie dieses Einzelbild im Speicher landet, ist einen genaueren Blick wert.
4.14.3. Wie snapshot den Speicher füllt¶
Der Sensor liefert Pixel über den Pixeldaten-Bus, der in Sensor-Busse beschrieben ist, mit Raten von Hunderten von Megabyte pro Sekunde – viel zu schnell, als dass die CPU sie in Software Pixel für Pixel kopieren könnte.
Stattdessen lagert die MCU die Übertragung an Direct Memory Access (DMA) aus – eine von der CPU getrennte Hardware-Engine, die Bytes innerhalb der MCU von einem Ort zum anderen kopiert, ganz ohne Beteiligung der CPU. Das Kameraeingangs-Peripheriegerät fängt jedes eingehende Pixel-Byte in einem kleinen On-Chip-FIFO auf; welche ISP-Stufen auch immer auf MCU-Seite laufen, verarbeiten die Daten unterwegs; und die DMA-Engine schreibt die fertigen Pixel an den entsprechenden Pixel-Offset in einen Framebuffer im RAM. Nichts in dieser Kette benötigt die CPU, sobald der DMA-Kanal programmiert wurde.
Wenn snapshot() aufgerufen wird:
Der CSI-Treiber programmiert die DMA-Engine mit der Adresse des Framebuffers, der Übertragungslänge (Pixel im Wert eines Einzelbilds) und einem Callback für den DMA-Fertig-Interrupt.
Der Treiber aktiviert das Kameraeingangs-Peripheriegerät und wartet darauf, dass der Sensor den Beginn des nächsten Einzelbilds signalisiert.
Während der Sensor das Einzelbild ausgibt, reicht das Peripheriegerät jedes Pixel-Byte durch den ISP und weiter an die DMA-Engine, die das Ergebnis an den nächsten Framebuffer-Offset in den RAM schreibt. Die CPU kann während der Übertragung anderen Code ausführen.
Wenn das letzte Pixel des Einzelbilds eintrifft, löst die DMA ihren Fertig-Interrupt aus, der Treiber hüllt den Framebuffer in ein
Image, undsnapshot()gibt es an den Benutzercode zurück.
Das zurückgegebene Image besitzt keine Kopie der Pixeldaten – es zeigt auf einen der Framebuffer der Kamera im RAM. Wie viele Framebuffer die Kamera vorhält und wie sie bei jedem Aufruf von snapshot() zwischen DMA und Benutzercode übergeben werden, hängt vom Pufferungsmodus ab, den die Anwendung über framebuffers() ausgewählt hat.