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)כותב לאוגרי החיישן הבוחרים את פורמט פיקסל הפלט. האפשרויות הזמינות הן הפורמטים שהציג עמוד פורמטי הפיקסל:
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 ממלא את הזיכרון¶
החיישן מספק פיקסלים על אפיק נתוני הפיקסלים המתואר ב-אפיקי חיישן בקצבים של מאות מגה-בייט לשנייה – מהיר בהרבה מכדי שה-CPU יעתיק פיקסל-פיקסל בתוכנה.
במקום זאת, ה-MCU מעביר את ההעברה ל-Direct Memory Access (DMA) – מנוע חומרה נפרד מה-CPU המעתיק בייטים ממקום אחד לאחר בתוך ה-MCU מבלי לערב את ה-CPU כלל. ההתקן ההיקפי לקלט מצלמה תופס כל בייט פיקסל נכנס ל-FIFO קטן על השבב; כל שלבי ה-ISP הרצים בצד ה-MCU מעבדים את הנתונים בדרך; ומנוע ה-DMA כותב את הפיקסלים המוגמרים אל חוצץ פריימים (frame buffer) ב-RAM באופסט הפיקסל המתאים. שום דבר בשרשרת זו אינו זקוק ל-CPU לאחר שערוץ ה-DMA תוכנת.
כאשר snapshot() נקרא:
דרייבר ה-CSI מתכנת את מנוע ה-DMA עם כתובת חוצץ הפריימים (frame buffer), אורך ההעברה (פיקסלים בשווי פריים אחד), ופונקציית callback עבור פסיקת סיום ה-DMA.
הדרייבר מאפשר את ההתקן ההיקפי לקלט מצלמה וממתין שהחיישן יסמן את תחילת הפריים הבא.
כשהחיישן מזרים את הפריים החוצה, ההתקן ההיקפי מעביר כל בייט פיקסל דרך ה-ISP והלאה אל מנוע ה-DMA, הכותב את התוצאה אל ה-RAM באופסט חוצץ הפריימים (frame buffer) הבא. ה-CPU פנוי להריץ קוד אחר במהלך ההעברה.
כשהפיקסל האחרון של הפריים מגיע, ה-DMA יורה את פסיקת הסיום שלו, הדרייבר עוטף את חוצץ הפריימים (frame buffer) ב-
Image, ו-snapshot()מחזיר אותו לקוד המשתמש.
ה-Image המוחזר אינו מחזיק עותק של נתוני הפיקסלים – הוא מצביע על אחד מחוצצי הפריימים (frame buffer) של המצלמה ב-RAM. כמה חוצצי פריימים (frame buffer) שומרת המצלמה, וכיצד הם מועברים בין ה-DMA לקוד המשתמש בכל קריאה ל-snapshot(), תלוי במצב החציצה שהיישום בחר באמצעות framebuffers().