class I2C – פרוטוקול תקשורת טורי בשני חוטים

I2C הוא פרוטוקול בשני חוטים לתקשורת בין התקנים. ברמה הפיזית הוא מורכב מ-2 חוטים: SCL ו-SDA, קווי השעון והנתונים בהתאמה.

אובייקטי I2C נוצרים כשהם מחוברים לאפיק (bus) מסוים. ניתן לאתחל אותם בעת היצירה, או לאתחל אותם מאוחר יותר.

הדפסת אובייקט ה-I2C נותנת לך מידע על תצורתו.

קיימות מימושים של I2C הן בחומרה והן בתוכנה, באמצעות המחלקות I2C ו-SoftI2C. I2C חומרתי משתמש בתמיכת החומרה הבסיסית של המערכת לביצוע הקריאות/הכתיבות והוא בדרך כלל יעיל ומהיר, אך עשוי להיות מוגבל ביחס לפינים שניתן להשתמש בהם. I2C תוכנתי ממומש באמצעות bit-banging וניתן להשתמש בו על כל פין, אך אינו יעיל באותה מידה. למחלקות אלה יש אותן מתודות זמינות והן נבדלות בעיקר באופן שבו הן נבנות.

הערה

אפיק ה-I2C דורש מעגל pull-up הן על SDA והן על SCL לפעולתו. בדרך כלל מדובר בנגדים בטווח של 1 - 10 קילו-אוהם, המחוברים מכל אחד מ-SDA/SCL אל Vcc. בלעדיהם, ההתנהגות אינה מוגדרת ועשויה לנוע מחסימה, איפוס watchdog בלתי צפוי ועד פשוט ערכים שגויים. לעיתים קרובות מעגל pull-up זה כבר מובנה בלוח ה-MCU או בלוחות הפריצה של החיישנים, אך אין כלל מחייב לכך. לכן אנא בדוק זאת במקרה של תקלה. ראה גם את מדריך הלמידה המצוין של Adafruit על חיווט I2C.

דוגמת שימוש:

from machine import I2C

i2c = I2C(freq=400000)          # create I2C peripheral at frequency of 400kHz
                                # depending on the port, extra parameters may be required
                                # to select the peripheral and/or pins to use

i2c.scan()                      # scan for peripherals, returning a list of 7-bit addresses

i2c.writeto(42, b'123')         # write 3 bytes to peripheral with 7-bit address 42
i2c.readfrom(42, 4)             # read 4 bytes from peripheral with 7-bit address 42

i2c.readfrom_mem(42, 8, 3)      # read 3 bytes from memory of peripheral 42,
                                #   starting at memory-address 8 in the peripheral
i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of peripheral 42
                                #   starting at address 2 in the peripheral

בנאים (Constructors)

class machine.I2C(id: int, *, scl: Pin | None = None, sda: Pin | None = None, freq: int = 400000, timeout: int = 50000)

בונה ומחזיר אובייקט I2C חדש באמצעות הפרמטרים הבאים:

  • id מזהה התקן I2C היקפי מסוים. הערכים המותרים תלויים בפורט/לוח המסוים

  • scl צריך להיות אובייקט פין המציין את הפין שישמש עבור SCL.

  • sda צריך להיות אובייקט פין המציין את הפין שישמש עבור SDA.

  • freq צריך להיות מספר שלם הקובע את התדר המרבי עבור SCL.

  • timeout הוא הזמן המרבי במיקרו-שניות שמותר לטרנזקציות I2C. פרמטר זה אינו מותר בחלק מהפורטים.

שים לב שחלק מהפורטים/לוחות יכללו ערכי ברירת מחדל של scl ו-sda שניתן לשנותם בבנאי זה. לאחרים יהיו ערכים קבועים של scl ו-sda שלא ניתן לשנותם.

מתודות כלליות

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

אתחל את אפיק ה-I2C עם הארגומנטים הנתונים:

  • scl הוא אובייקט פין עבור קו ה-SCL

  • sda הוא אובייקט פין עבור קו ה-SDA

  • freq הוא קצב שעון ה-SCL

במקרה של I2C חומרתי, תדר השעון בפועל עשוי להיות נמוך מהתדר המבוקש. הדבר תלוי בחומרת הפלטפורמה. ניתן לקבוע את הקצב בפועל על ידי הדפסת אובייקט ה-I2C.

scan() List[int]

סרוק את כל כתובות ה-I2C בין 0x08 ל-0x77 כולל והחזר רשימה של אלה שמגיבות. התקן מגיב אם הוא מושך את קו ה-SDA למצב נמוך לאחר שכתובתו (כולל ביט כתיבה) נשלחת על האפיק.

פעולות I2C ראשוניות

המתודות הבאות מממשות את פעולות אפיק ה-I2C הראשוניות של הבקר וניתן לשלב אותן ליצירת כל טרנזקציית I2C. הן מסופקות אם אתה זקוק לשליטה רבה יותר על האפיק, אחרת ניתן להשתמש במתודות הסטנדרטיות (ראה להלן).

מתודות אלה זמינות רק במחלקת SoftI2C.

start() None

צור תנאי START על האפיק (SDA עובר למצב נמוך בעוד SCL גבוה).

stop() None

צור תנאי STOP על האפיק (SDA עובר למצב גבוה בעוד SCL גבוה).

readinto(buf: bytearray, nack: bool = True, /) None

קורא בתים מהאפיק ושומר אותם אל buf. מספר הבתים שנקראים הוא אורכו של buf. ACK יישלח על האפיק לאחר קבלת כל הבתים מלבד האחרון. לאחר קבלת הבית האחרון, אם nack הוא true אז יישלח NACK, אחרת יישלח ACK (ובמקרה זה ההתקן ההיקפי מניח שייקראו בתים נוספים בקריאה מאוחרת יותר).

write(buf: bytes) int

כתוב את הבתים מתוך buf אל האפיק. בודק שמתקבל ACK לאחר כל בית ומפסיק לשדר את הבתים הנותרים אם מתקבל NACK. הפונקציה מחזירה את מספר ה-ACK שהתקבלו.

פעולות אפיק סטנדרטיות

המתודות הבאות מממשות את פעולות הקריאה והכתיבה הסטנדרטיות של בקר ה-I2C המכוונות להתקן היקפי נתון.

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

קרא nbytes מההתקן ההיקפי שצוין על ידי addr. אם stop הוא true אז תנאי STOP נוצר בסוף ההעברה. מחזיר אובייקט bytes עם הנתונים שנקראו.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

קרא אל buf מההתקן ההיקפי שצוין על ידי addr. מספר הבתים שנקראים יהיה אורכו של buf. אם stop הוא true אז תנאי STOP נוצר בסוף ההעברה.

המתודה מחזירה None.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

כתוב את הבתים מתוך buf אל ההתקן ההיקפי שצוין על ידי addr. אם מתקבל NACK לאחר כתיבת בית מתוך buf אז הבתים הנותרים אינם נשלחים. אם stop הוא true אז תנאי STOP נוצר בסוף ההעברה, גם אם מתקבל NACK. הפונקציה מחזירה את מספר ה-ACK שהתקבלו.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

כתוב את הבתים הכלולים ב-vector אל ההתקן ההיקפי שצוין על ידי addr. vector צריך להיות tuple או רשימה של אובייקטים בעלי פרוטוקול החוצץ. ה-addr נשלח פעם אחת ולאחר מכן הבתים מכל אובייקט ב-vector נכתבים החוצה ברצף. האובייקטים ב-vector עשויים להיות באורך אפס בתים, ובמקרה זה הם אינם תורמים לפלט.

אם מתקבל NACK לאחר כתיבת בית מאחד האובייקטים ב-vector אז הבתים הנותרים, וכל אובייקט נותר, אינם נשלחים. אם stop הוא true אז תנאי STOP נוצר בסוף ההעברה, גם אם מתקבל NACK. הפונקציה מחזירה את מספר ה-ACK שהתקבלו.

פעולות זיכרון

חלק מהתקני I2C מתפקדים כהתקן זיכרון (או כמערכת אוגרים) שניתן לקרוא ממנו ולכתוב אליו. במקרה זה ישנן שתי כתובות המשויכות לטרנזקציית I2C: כתובת ההתקן ההיקפי וכתובת הזיכרון. המתודות הבאות הן פונקציות נוחות לתקשורת עם התקנים מסוג זה.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

קרא nbytes מההתקן ההיקפי שצוין על ידי addr החל מכתובת הזיכרון שצוינה על ידי memaddr. הארגומנט addrsize מציין את גודל הכתובת בביטים. מחזיר אובייקט bytes עם הנתונים שנקראו.

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

קרא אל buf מההתקן ההיקפי שצוין על ידי addr החל מכתובת הזיכרון שצוינה על ידי memaddr. מספר הבתים שנקראים הוא אורכו של buf. הארגומנט addrsize מציין את גודל הכתובת בביטים.

המתודה מחזירה None.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

כתוב את buf אל ההתקן ההיקפי שצוין על ידי addr החל מכתובת הזיכרון שצוינה על ידי memaddr. הארגומנט addrsize מציין את גודל הכתובת בביטים.

המתודה מחזירה None.

class SoftI2C – אפיק I2C המדומה בתוכנה

המחלקה SoftI2C מממשת I2C על ידי bit-banging של פיני GPIO שרירותיים. היא חושפת את אותו מערך מתודות כמו I2C בתוספת פעולות האפיק הראשוניות ברמה הנמוכה (start(), stop(), readinto(), write()) עבור קוראים שזקוקים להרכבת טרנזקציות לא-סטנדרטיות. השתמש בה כאשר הפינים שאתה צריך אינם מחווטים לבלוק I2C חומרתי, כאשר אתה צריך יותר אפיקים ממה שהחומרה מספקת, או כדי לתקשר עם התקנים הדורשים רצפים בלתי שגרתיים (שעונים נוספים, התחלות חוזרות לאחר כתיבות, וכדומה).

בנאים (Constructors)

class machine.SoftI2C(scl: Pin, sda: Pin, *, freq: int = 400000, timeout: int = 50000)

בונה אפיק I2C תוכנתי המונע על ידי scl / sda.

freq הוא קצב שעון ה-SCL המבוקש ב-Hz (הקצב בפועל בדרך כלל נמוך יותר בשל תקורת לולאת ה-bit-bang).

timeout הוא הזמן המרבי במיקרו-שניות להמתנה למתיחת שעון (clock stretching) (כאשר SCL מוחזק במצב נמוך על ידי התקן אחר על האפיק); עם תום הזמן מתרחשת חריגה OSError(ETIMEDOUT).

מתודות כלליות

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

אתחל מחדש את אפיק ה-I2C התוכנתי עם הפינים והתדר הנתונים. שקול לבניית SoftI2C חדש על אותו אובייקט.

scan() List[int]

סרוק את כל כתובות ה-I2C בין 0x08 ל-0x77 כולל והחזר רשימה של אלה שהגיבו.

פעולות I2C ראשוניות

המתודות הבאות מממשות את פעולות אפיק ה-I2C הראשוניות של הבקר וניתן לשלב אותן ליצירת כל טרנזקציית I2C. הן זמינות רק ב-SoftI2C – מחלקת ה-I2C החומרתית אינה חושפת אותן.

start() None

צור תנאי START על האפיק (SDA עובר למצב נמוך בעוד SCL גבוה).

stop() None

צור תנאי STOP על האפיק (SDA עובר למצב גבוה בעוד SCL גבוה).

readinto(buf: bytearray, nack: bool = True, /) None

קרא בתים מהאפיק אל buf. len(buf) בתים נקראים; ACK נשלח לאחר כל בית מלבד האחרון. לאחר הבית האחרון, nack=True (ברירת המחדל) שולח NACK כדי לסיים את ההעברה; nack=False שולח ACK כך שההתקן נשאר נבחר עבור readinto() עוקבת.

write(buf: bytes) int

כתוב את buf אל האפיק, תוך בדיקת ACK לאחר כל בית. השידור נעצר ב-NACK הראשון. מחזיר את מספר ה-ACK שהתקבלו.

פעולות אפיק סטנדרטיות

המתודות הבאות מממשות את פעולות הקריאה והכתיבה הסטנדרטיות של בקר ה-I2C המכוונות להתקן היקפי נתון.

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

קרא nbytes מההתקן בכתובת ה-7 ביט addr. אם stop הוא true תנאי STOP נוצר בסוף ההעברה.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

קרא len(buf) בתים מההתקן בכתובת addr אל buf. אם stop הוא true תנאי STOP נוצר בסוף ההעברה.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

כתוב את buf אל ההתקן בכתובת addr. השידור נעצר ב-NACK הראשון. אם stop הוא true תנאי STOP תמיד נוצר בסוף ההעברה (גם במקרה של NACK מוקדם). מחזיר את מספר ה-ACK שהתקבלו.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

כתוב את שרשור החוצצים שב-vector אל ההתקן בכתובת addr כטרנזקציה אחת. חוצצים ריקים מתעלמים מהם. מתנהג כמו writeto() ביחס לסמנטיקת stop ולערך ההחזרה.

פעולות זיכרון

חלק מהתקני I2C מתפקדים כהתקן זיכרון (או כמערכת אוגרים) שניתן לקרוא ממנו ולכתוב אליו. במקרה זה ישנן שתי כתובות המשויכות לטרנזקציית I2C: כתובת ההתקן ההיקפי וכתובת הזיכרון. המתודות הבאות הן עזרים נוחים לתקשורת עם התקנים מסוג זה.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

קרא nbytes מההתקן בכתובת addr החל מהאוגר memaddr. addrsize הוא רוחב כתובת-האוגר בביטים (בדרך כלל 8 או 16).

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

קרא אל buf מההתקן בכתובת addr החל מהאוגר memaddr.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

כתוב את buf אל ההתקן בכתובת addr החל מהאוגר memaddr.