class I2C -- 雙線串列協定

I2C 是一種用於裝置間通訊的雙線協定。在實體層上,它由 2 條線組成:SCL 與 SDA,分別為時脈線與資料線。

I2C 物件在建立時會附接至特定的匯流排。它們可以在建立時初始化,或稍後再行初始化。

列印 I2C 物件可取得其組態的相關資訊。

硬體與軟體 I2C 實作分別透過 I2CSoftI2C 類別提供。硬體 I2C 利用系統底層的硬體支援來執行讀寫,通常有效率且快速,但可能限制可使用哪些接腳。軟體 I2C 以位元觸發(bit-banging)方式實作,可用於任何接腳,但效率不如硬體。這些類別提供相同的方法,主要差異在於建構方式。

備註

I2C 匯流排運作時需要在 SDA 與 SCL 上加裝上拉電路。通常這些是阻值範圍在 1 - 10 kOhm 的電阻,從各 SDA/SCL 連接至 Vcc。若沒有這些電阻,行為將無法預期,從阻塞、非預期的看門狗重置到單純的錯誤值都有可能。這類上拉電路通常已內建於 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

建構式

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

使用下列參數建構並回傳一個新的 I2C 物件:

  • id 用於識別特定的 I2C 周邊裝置。允許的值取決於特定的 port/開發板

  • scl 應為一個接腳物件,指定要用於 SCL 的接腳。

  • sda 應為一個接腳物件,指定要用於 SDA 的接腳。

  • freq 應為一個整數,用於設定 SCL 的最大頻率。

  • timeout 是允許 I2C 交易的最長時間,以微秒為單位。某些 port 不允許使用此參數。

請注意,某些 port/開發板會有 sclsda 的預設值,可在此建構式中變更。其他則會有無法變更的 sclsda 固定值。

一般方法

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

使用給定的引數初始化 I2C 匯流排:

  • scl 是 SCL 線的接腳物件

  • sda 是 SDA 線的接腳物件

  • freq 是 SCL 的時脈速率

在硬體 I2C 的情況下,實際的時脈頻率可能低於所要求的頻率。這取決於平台硬體。實際速率可透過列印 I2C 物件來判定。

scan() List[int]

掃描介於 0x08 至 0x77(含)之間的所有 I2C 位址,並回傳有回應者的清單。當某裝置的位址(包含寫入位元)在匯流排上送出後,若該裝置將 SDA 線拉低,即表示該裝置有回應。

原始 I2C 操作

下列方法實作了原始的 I2C 控制器匯流排操作,可組合以構成任何 I2C 交易。當你需要更多對匯流排的控制時可使用它們,否則可使用標準方法(見下文)。

這些方法僅在 SoftI2C 類別上可用。

start() None

在匯流排上產生 START 條件(當 SCL 為高時 SDA 由高轉低)。

stop() None

在匯流排上產生 STOP 條件(當 SCL 為高時 SDA 由低轉高)。

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

從匯流排讀取位元組並存入 buf。讀取的位元組數為 buf 的長度。在接收除最後一個位元組以外的所有位元組後,會於匯流排上送出 ACK。在接收最後一個位元組後,若 nack 為真則會送出 NACK,否則會送出 ACK(在此情況下,周邊裝置會假設後續呼叫還會讀取更多位元組)。

write(buf: bytes) int

buf 中的位元組寫入匯流排。每個位元組之後會檢查是否收到 ACK,若收到 NACK 則停止傳送剩餘的位元組。此函式回傳所收到的 ACK 數量。

標準匯流排操作

下列方法實作了針對給定周邊裝置的標準 I2C 控制器讀寫操作。

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

addr 所指定的周邊裝置讀取 nbytes。若 stop 為真,則在傳輸結束時產生 STOP 條件。回傳一個包含所讀取資料的 bytes 物件。

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

addr 所指定的周邊裝置讀取資料至 buf。讀取的位元組數為 buf 的長度。若 stop 為真,則在傳輸結束時產生 STOP 條件。

此方法回傳 None

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

buf 中的位元組寫入 addr 所指定的周邊裝置。若在寫入 buf 中某個位元組後收到 NACK,則不再傳送剩餘的位元組。若 stop 為真,則即使收到 NACK,仍會在傳輸結束時產生 STOP 條件。此函式回傳所收到的 ACK 數量。

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

vector 中所含的位元組寫入 addr 所指定的周邊裝置。vector 應為一個由具備緩衝協定的物件所組成的元組或清單。addr 只會送出一次,接著 vector 中每個物件的位元組會依序寫出。vector 中的物件長度可以為零位元組,此情況下它們不會對輸出有所貢獻。

若在寫入 vector 中某個物件的某個位元組後收到 NACK,則不再傳送剩餘的位元組以及任何剩餘的物件。若 stop 為真,則即使收到 NACK,仍會在傳輸結束時產生 STOP 條件。此函式回傳所收到的 ACK 數量。

記憶體操作

某些 I2C 裝置可作為記憶體裝置(或一組暫存器),可從中讀取與寫入。在此情況下,一次 I2C 交易會關聯兩個位址:周邊位址與記憶體位址。下列方法是用於與此類裝置通訊的便利函式。

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

addr 所指定的周邊裝置、自 memaddr 所指定的記憶體位址開始讀取 nbytes。引數 addrsize 以位元為單位指定位址大小。回傳一個包含所讀取資料的 bytes 物件。

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

addr 所指定的周邊裝置、自 memaddr 所指定的記憶體位址開始讀取資料至 buf。讀取的位元組數為 buf 的長度。引數 addrsize 以位元為單位指定位址大小。

此方法回傳 None

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

memaddr 所指定的記憶體位址開始,將 buf 寫入 addr 所指定的周邊裝置。引數 addrsize 以位元為單位指定位址大小。

此方法回傳 None

class SoftI2C -- 軟體模擬的 I2C 匯流排

SoftI2C 類別透過對任意 GPIO 接腳進行位元觸發(bit-banging)來實作 I2C。它公開與 I2C 相同的方法介面,並額外提供低階的原始匯流排操作(start()stop()readinto()write()),供需要組裝非標準交易的呼叫端使用。當你需要的接腳未接至硬體 I2C 區塊、需要的匯流排數量多於硬體所提供、或需要與要求特殊序列(額外的時脈、寫入後重複的 START 等)的裝置通訊時,可使用它。

建構式

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

建構一個由 scl / sda 驅動的軟體 I2C 匯流排。

freq 是目標 SCL 時脈速率,以 Hz 為單位(由於位元觸發迴圈的額外開銷,實際速率通常較低)。

timeout 是等待時脈延展(clock stretching,即匯流排上其他裝置將 SCL 持續拉低)的最長時間,以微秒為單位;逾時即會引發 OSError(ETIMEDOUT)

一般方法

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

使用給定的接腳與頻率重新初始化軟體 I2C 匯流排。等同於在同一物件上建構一個新的 SoftI2C

scan() List[int]

掃描介於 0x08 至 0x77(含)之間的所有 I2C 位址,並回傳有回應者的清單。

原始 I2C 操作

下列方法實作了原始的 I2C 控制器匯流排操作,可組合以構成任何 I2C 交易。它們僅限 SoftI2C 使用——硬體 I2C 類別並不公開它們。

start() None

在匯流排上產生 START 條件(當 SCL 為高時 SDA 由高轉低)。

stop() None

在匯流排上產生 STOP 條件(當 SCL 為高時 SDA 由低轉高)。

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

從 7 位元位址 addr 的裝置讀取 nbytes。若 stop 為真,則在傳輸結束時產生 STOP 條件。

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

addr 的裝置讀取 len(buf) 個位元組至 buf。若 stop 為真,則在傳輸結束時產生 STOP 條件。

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

buf 寫入 addr 的裝置。傳輸會在首次出現 NACK 時停止。若 stop 為真,則一律在傳輸結束時產生 STOP 條件(即使提早出現 NACK 亦然)。回傳所收到的 ACK 數量。

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

vector 中各緩衝區的串接內容作為單一交易寫入 addr 的裝置。空緩衝區會被忽略。在 stop 語意與回傳值方面的行為與 writeto() 相同。

記憶體操作

某些 I2C 裝置可作為記憶體裝置(或一組暫存器),可從中讀取與寫入。在此情況下,一次 I2C 交易會關聯兩個位址:周邊位址與記憶體位址。下列方法是用於與此類裝置通訊的便利輔助函式。

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

addr 的裝置、自暫存器 memaddr 開始讀取 nbytesaddrsize 是暫存器位址的寬度,以位元為單位(通常為 816)。

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

addr 的裝置、自暫存器 memaddr 開始讀取資料至 buf

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

從暫存器 memaddr 開始,將 buf 寫入 addr 的裝置。