3.22. SPI בקוד#
machine.SPI עוטף בקר SPI חומרתי; קווי CS הם פלטי Pin רגילים המנוהלים על ידי הסקריפט. צור מופע SPI עם מזהה האפיק, קצב השעון הרצוי, ו(אם נדרש) המצב:
from machine import SPI, Pin
spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0)
cs = Pin("P3", Pin.OUT, value=1) # CS idle high
ה-id בוחר באיזה בלוק SPI חומרתי להשתמש; המספרים הזמינים ופיני ה-SCK/MOSI/MISO שאליהם הם ממופים תלויים בלוח (ראו לוחות OpenMV). baudrate הוא תדר ה-SCK בהרץ – הקצב בפועל שהחומרה משיגה עשוי להיות מעט נמוך יותר עקב חלוקת שעון, מה שהערך המודפס של אובייקט ה-SPI יציג.
פין ה-CS נבנה עם value=1 כך שהוא במנוחה במצב לא פעיל. כל טרנזקציה מפעילה את CS (מניעה אותו לנמוך), מזיזה את הביטים, ומשביתה את CS (מניעה אותו לגבוה) שוב.
3.22.1. קריאה, כתיבה, החלפה#
שלוש מתודות מכסות את המקרים הנפוצים:
cs.value(0)
spi.write(b"\x10\x20\x30") # send 3 bytes, ignore what comes back
cs.value(1)
cs.value(0)
data = spi.read(4) # read 4 bytes; sends 0x00 while reading
cs.value(1)
rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x9F\x00", rx) # send 0x9F, 0x00; receive 2 bytes
cs.value(1)
write() היא הנתיב המהיר לכתיבה בלבד; הבקר דוחף את הביטים ומשליך את כל מה שההתקן ההיקפי החזיר על MISO. read() היא תמונת המראה – היא פולטת N פעימות SCK תוך שליחת בית דמה קבוע (0 כברירת מחדל) על MOSI ומאחסנת את ביטי ה-MISO. write_readinto() היא הצורה הדו-כיוונית המלאה: היא שולחת את הביטים מחוצץ (buffer) אחד ומאחסנת את ביטי ה-MISO הבו-זמניים לתוך אחר. התקנים היקפיים רבים משתמשים בדפוס הזה – ”שלח בית פקודה, ואז קרא את התגובה בהעברה הבאה“ – כך ששתי הפעולות משתלבות באופן טבעי לקריאת write_readinto אחת.
רוב ההתקנים ההיקפיים מצפים שקו ה-CS יישאר פעיל למשך כל הטרנזקציה (מבתי הפקודה ועד בתי התגובה), אז שמרו את הסוגריים cs.value(0) / cs.value(1) סביב כל הרצף, ולא סביב כל קריאת מתודה.
3.22.2. קריאת חיישן טיפוסית#
חיישני SPI רבים מארגנים את מצבם כקבוצה של אוגרים (registers) פנימיים ועוקבים אחר אותה צורת החלפה: שלח את כתובת האוגר (עם דגל קריאה/כתיבה בביט העליון), ואז קרא או כתוב את בתי האוגר. קריאה של האוגר 0x0F בהתקן כזה:
rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x8F\x00", rx) # 0x80 = "read" flag, then reg 0x0F
cs.value(1)
register_value = rx[1]
בית ה-MISO הראשון הוא זבל (ההתקן עדיין קיבל את הפקודה באותה נקודה); בית ה-MISO השני מחזיק את תוכן האוגר. פורמט בית הפקודה המדויק – איזה ביט הוא דגל הקריאה/כתיבה, האם הכתובת מקודמת אוטומטית בקריאות מרובות-בתים – מופיע בגיליון הנתונים של ההתקן.
3.22.3. Bit-banging#
מופע ה-SPI שלמעלה משתמש בבלוק SPI חומרתי: התקן היקפי ייעודי בתוך ה-MCU עם אוגר הסטה (shift register) ומחולל שעון משלו המייצרים את צורות הגל SCK / MOSI / MISO בסיליקון. התוכנה פשוט מוסרת לו בית; הביטים זזים על החוט ללא עזרת CPU נוספת, ומשאירים את ה-CPU פנוי לעשות עבודה אחרת במקביל.
החלופה היא bit-banging: התוכנה עוברת בלולאה על כל ביט ומחליפה מצבי פיני GPIO ישירות כדי לייצר את אותה צורת גל. אין התקן היקפי חומרתי מעורב – ה-CPU מניע את SCK לנמוך, מגדיר את MOSI, מניע את SCK לגבוה, דוגם את MISO, וכן הלאה עבור כל ביט של כל בית. זה כובל את ה-CPU למשך כל הטרנזקציה ורץ לאט יותר ממה שהבלוק החומרתי יכול, אך זה עובד על כל פין ואינו זקוק לבלוק חומרתי פנוי.
machine.SoftSPI היא מימוש ה-bit-bang של אותו ממשק SPI API:
from machine import SoftSPI, Pin
spi = SoftSPI(baudrate=500_000, polarity=0, phase=0,
sck=Pin("P2"), mosi=Pin("P0"), miso=Pin("P1"))
השתמשו בו כאשר ההתקן צריך להיות על פינים שאינם מחווטים לבלוק SPI חומרתי, או כאשר כל הבלוקים החומרתיים בשימוש. 500 kHz הוא תקרה נוחה ברוב המצלמות; ה-CPU נשאר עסוק למשך כל הטרנזקציה.