4.14. พื้นฐาน CSI

โมดูล csi คือวิธีที่โค้ด Python ควบคุมเซนเซอร์กล้อง สคริปต์ทุกตัวที่จับเฟรมจะมีรูปแบบสามส่วนเหมือนกัน: การนำเข้าที่ด้านบน การกำหนดค่าครั้งเดียวตรงกลาง และลูป while True ที่ด้านล่างที่ดึงเฟรมจากกล้องทีละหนึ่ง

4.14.1. ลูปทั่วไป

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. สิ่งที่แต่ละการเรียกทำ

import csi, image, time

นำเข้าสามโมดูล csi ควบคุมเซนเซอร์ image กำหนดคลาส Image ที่ snapshot() คืนค่า และ time ให้ตัวช่วย time.clock() ที่ใช้วัดเฟรมต่อวินาที

csi.CSI()

สร้างอินสแตนซ์ CSI ที่ครอบเซนเซอร์กล้องทางกายภาพหนึ่งตัว ตัวสร้างจะอ้างสิทธิ์อุปกรณ์ต่อพ่วงกล้องและบันทึกการกำหนดค่าต่อเซนเซอร์ กล้องที่มีเซนเซอร์เดียวมีอินสแตนซ์ CSI หนึ่งตัว กล้องที่มีสองเซนเซอร์ (สีบวกความร้อน สีบวกเหตุการณ์) มีสองตัว โดยแต่ละตัวเลือกด้วยอาร์กิวเมนต์ cid ให้กับตัวสร้าง

csi0.reset()

จ่ายไฟและกำหนดค่าเซนเซอร์ โดยค่าเริ่มต้นจะกระตุกพินรีเซ็ตของเซนเซอร์ จากนั้นเขียนรีจิสเตอร์ I2C ของเซนเซอร์ไปยังสถานะเริ่มต้นที่รู้จัก การเรียกกำหนดค่าที่ตามมา -- pixformat, framesize, ปุ่มควบคุมอัตโนมัติ -- จะส่งการเขียนรีจิสเตอร์เพิ่มเติมผ่านบัส I2C ควบคุมเดียวกัน

csi0.pixformat(csi.RGB565)

เขียนรีจิสเตอร์เซนเซอร์ที่เลือกรูปแบบพิกเซลเอาต์พุต ตัวเลือกที่มีคือรูปแบบที่หน้า pixel formats แนะนำไว้: RGB565, GRAYSCALE, BAYER, YUV422, และ JPEG บนเซนเซอร์ที่รองรับ

csi0.framesize(csi.QVGA)

เขียนรีจิสเตอร์ที่เลือกความละเอียดเอาต์พุต QVGA คือ 320 × 240 ขนาดที่ตั้งชื่อไว้สูงสุดถึง WQXGA2 (2592 × 1944 ประมาณ 5 MP) บนเซนเซอร์ที่รองรับ ทูเปิล (width, height) แบบกำหนดเองก็ใช้ได้เช่นกัน ตราบใดที่สอดคล้องกับความสามารถเอาต์พุตของเซนเซอร์

clock = time.clock()

สร้างตัวช่วยนาฬิกา การเรียก clock.tick() แต่ละครั้งภายในลูปจะบันทึกเวลาเริ่มต้นของการวนซ้ำ time.clock.fps() รายงานอัตราลูปล่าสุดเป็นเฟรมต่อวินาที

img = csi0.snapshot()

จับหนึ่งเฟรมจากเซนเซอร์และคืนค่าเป็น Image กลไกที่เฟรมนั้นลงเอยในหน่วยความจำนั้นคุ้มค่าที่จะพิจารณาอย่างละเอียดมากขึ้น

4.14.3. วิธีที่ snapshot เติมหน่วยความจำ

เซนเซอร์ส่งพิกเซลบนบัสข้อมูลพิกเซลที่อธิบายใน sensor buses ด้วยอัตราหลายร้อยเมกะไบต์ต่อวินาที -- เร็วเกินไปมากสำหรับ CPU ที่จะคัดลอกพิกเซลต่อพิกเซลในซอฟต์แวร์

แทนที่ MCU จะโอนภาระการถ่ายโอนไปยัง Direct Memory Access (DMA) -- เอ็นจินฮาร์ดแวร์ที่แยกจาก CPU ที่คัดลอกไบต์จากที่หนึ่งไปยังอีกที่หนึ่งภายใน MCU โดยไม่เกี่ยวข้องกับ CPU เลย อุปกรณ์ต่อพ่วงอินพุตกล้องจะรับไบต์พิกเซลขาเข้าแต่ละตัวลงใน FIFO บนชิปขนาดเล็ก ขั้นตอน ISP ที่ทำงานบน MCU จะประมวลผลข้อมูลระหว่างทาง และเอ็นจิน DMA จะเขียนพิกเซลที่เสร็จสมบูรณ์ลงในบัฟเฟอร์เฟรมใน RAM ที่ออฟเซ็ตพิกเซลที่สอดคล้องกัน ไม่มีสิ่งใดในห่วงโซ่นั้นต้องการ CPU เมื่อช่อง DMA ได้รับการตั้งโปรแกรมแล้ว

เมื่อเรียก snapshot():

  1. ไดรเวอร์ CSI ตั้งโปรแกรมเอ็นจิน DMA ด้วยที่อยู่ของบัฟเฟอร์เฟรม ความยาวการถ่ายโอน (พิกเซลหนึ่งเฟรม) และคอลแบ็กสำหรับอินเทอร์รัปต์ DMA เสร็จสิ้น

  2. ไดรเวอร์เปิดใช้งานอุปกรณ์ต่อพ่วงอินพุตกล้องและรอให้เซนเซอร์ส่งสัญญาณเริ่มต้นของเฟรมถัดไป

  3. ขณะที่เซนเซอร์สตรีมเฟรมออกมา อุปกรณ์ต่อพ่วงจะส่งไบต์พิกเซลแต่ละตัวผ่าน ISP และไปยังเอ็นจิน DMA ซึ่งเขียนผลลัพธ์ลงใน RAM ที่ออฟเซ็ตบัฟเฟอร์เฟรมถัดไป CPU สามารถทำงานโค้ดอื่นได้ระหว่างการถ่ายโอน

  4. เมื่อพิกเซลสุดท้ายของเฟรมมาถึง DMA จะยิงอินเทอร์รัปต์เสร็จสิ้น ไดรเวอร์ครอบบัฟเฟอร์เฟรมใน Image และ snapshot() คืนค่าให้กับโค้ดผู้ใช้

Image ที่คืนค่ามาไม่ได้เป็นเจ้าของสำเนาของข้อมูลพิกเซล -- มันชี้ไปที่บัฟเฟอร์เฟรมของกล้องตัวใดตัวหนึ่งใน RAM จำนวนบัฟเฟอร์เฟรมที่กล้องเก็บ และวิธีที่ส่งต่อระหว่าง DMA และโค้ดผู้ใช้ในการเรียก snapshot() แต่ละครั้ง ขึ้นอยู่กับโหมดการบัฟเฟอร์ที่แอปพลิเคชันเลือกผ่าน framebuffers()