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 נשאר עסוק למשך כל הטרנזקציה.