概要¶
本節教程爲大家簡要介紹I²C總線協議,並以一個簡單的例程教會大家在MicroPython下使用I²C
快速開始
from machine import Pin, I2C
# construct a software I2C bus
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
# construct a hardware I2C bus
i2c = I2C(0)
i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000)
i2c.scan() # scan for slave devices
i2c.readfrom(0x3a, 4) # read 4 bytes from slave device with address 0x3a
i2c.writeto(0x3a, '12') # write '12' to slave device with address 0x3a
什麼是I²C
I²C(Inter-integrated Circuit)最早是飛利浦在1982年開發設計的一種總線協議。I²C總線支持設備之間的短距離通信,用於處理器和一些外圍設備之間的接口,它只需要兩根信號線來完成信息交換。
通信原理¶
物理接線¶
I²C最少只需要兩根線,和異步串口類似,但可以支持多個從(slave)設備,和SPI不同的是,I²C可以支持多主機(mul-master)系統,允許有多個master並且每個master都可以與所有的slaves通信(master之間不可通過I²C通信,並且每個master只能輪流使用I²C總線)。master是指啓動數據傳輸的設備並在總線上生成時鐘信號以驅動該傳輸,而被尋址的通信設備都作爲slaves。
I²C通訊只需要2條雙向總線:
功能編號 | 含義 |
---|---|
SDA(serial data:串行數據線) | 傳輸數據,SDA線傳輸數據是大端傳輸,每次傳輸一個字節 |
SCL(serial clock:串行時鐘線) | 同步數據收發 |
我們以最爲簡單的一個主機對應多個從機爲例,進行接下來的原理科普。
數據有效性¶
SDA 線上的數據必須在時鐘的高電平週期保持穩定,數據線的高或低電平狀態只有在 SCL 線的時鐘信號是低電平時才能改變。
換言之,SCL爲高電平時表示有效數據,SDA爲高電平表示“1”,低電平表示“0”;SCL爲低電平時表示無效數據,此時SDA會進行電平切換,爲下次數據表示做準備。如圖13-1爲數據有效性的時序圖。
起始條件S和停止條件P¶
-
起始條件S:當SCL高電平時,SDA由高電平向低電平轉換;
-
停止條件P:當SCL高電平時,SDA由低電平向高電平轉換。
起始和停止條件一般由主機產生。總線在起始條件後處於busy的狀態,在停止條件的某段時間後,總線纔再次處於空閒狀態。如下圖爲起始和停止條件的信號產生時序圖。
數據格式¶
I2C傳輸的數據以字節爲單位,每個字節必須爲8位,可以傳輸任意多個字節,上圖中以一個字節的數據爲例進行分析,I2C的數據格式具有以下特點:
-
每個字節後必須跟一個響應位 ACK(如上圖中的SCL上ACK),因此實際上傳輸一個字節(8位)的數據需要花費9位的時間。
-
SDA上首先傳輸字節的最高位,從上圖中我們可以看出,位數編號的發送順序從左至右 是
Bit7-Bit0
響應ACK¶
數據接收方收到傳輸的一個字節數據後,需要給出響應,此時處在第九個時鐘,發送端釋放SDA線控制權 ,將SDA電平拉高,由接收方控制。
接收方表示成功的接收到了8位一個字節的數據,便將SDA拉低爲低電平,即ACK信號,表示應答
PDU¶
當你理解了時序圖之後,接下來我們爲大家貼出I2C的PDU(Protocol Data Unit:協議數據單元,即數據格式):
以下圖片均來自網絡
硬件資源¶
ESP32 本身擁有 2 個 I²C 總線接口,根據用戶的配置,總線接口可以用作 I²C 主機或從機模式。
然而遺憾的是,MicroPython ESP32上的I²C是軟件模擬的,並沒有充分利用到ESP32的硬件資源。
理論上來講,大部分同時支持輸入與輸出的GPIO都能夠被配置爲I²C的管腳資源。
I2C API文檔
類
class machine.I2C(scl, sda, freq)
scl
: I²C設備時鐘引腳對象
sda
: I²C設備數據線引腳對象
freq
: SCL時鐘頻率 0 <freq
≤ 500000(Hz)
定義I2C
示例:
from machine import I2C, Pin I2C = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
常用類函數¶
I2C.init(scl, sda, freq)¶
函數說明:初始化構造I²C總線。
scl
:SCL信號線的I/O口
sda
:SDA信號線的I/O口
freq
:SCL時鐘頻率
示例:
I2C.init(scl=Pin(5), sda=Pin(4), freq=100000)
I2C.scan()¶
函數說明:掃描0x08到0x77之間的I²C地址,並返回設備列表。
示例:
I2C.scan()
I2C.start()¶
函數說明:在總線上觸發START狀態(SCL爲高電平時,SDA轉爲低電平)。
示例:
I2C.start()
I2C.stop()¶
函數說明:在總線上觸發STOP狀態 (SCL爲高電平時,SDA轉爲高電平)。
示例:
I2C.stop()
I2C.write(buf)¶
函數說明:buf中的數據寫入到總線,並返回寫入的字節數。
buf:存儲數據的緩衝區
注意:
使用write()函數時要與start函數一起使用,否則無法返回寫入的字節數。
示例:
buf = b'123' I2C.start() I2C.write(buf)
I2C.readinto(buf, nack=True)¶
函數說明:從總線上讀取數據並存放到buf,無返回值。
buf:存儲數據的緩衝區
注意:
讀取的字節數是buf的長度。在接收到最後一個字節之前,總線將發送ACK信號。在接收到最後一個字節後,如果nack爲True,那麼將發送一個NACK信號,否則將發送一個ACK信號。 示例:
buf=bytearray(3) I2C.readinto(buf)
標準總線操作¶
下面介紹的函數是標準的I²C主模式讀寫操作。
I2C.readfrom(addr, nbytes)¶
函數說明:從指定地址設備讀取數據,返回讀取對象,這個對象與I²C設備有關。
addr
:I²C設備地址(可由scan函數讀取出來)
nbytes
:要讀取數據的大小
示例:
>>> print(I2C.scan()) [24] >>> data = I2C.readfrom(24, 8) >>> print(data) b'\x00\x02\x00\x00\xe8\x03\xe8\x03'
I2C.readfrom_into(addr, buf)¶
函數說明:從指定地址設備讀取buf.len()個數據到buf。
addr
:I²C設備地址(可由scan函數讀取出來)
buf
:存儲數據的緩衝區
示例:
>>> buf = bytearray(8) >>> I2C.readfrom_into(24, buf) >>> print(buf) bytearray(b'\x00\x02\x00\x00\xe8\x03\xe8\x03')
I2C.writeto(addr, buf)¶
函數說明:將buf中的數據寫入設備,並返回寫入數據的大小。
addr
:I²C設備地址(可由scan函數讀取出來)
buf
:存儲數據的緩衝區
示例:
>>> b = bytearray(3) >>> b[0] = 24 >>> b[1] = 111 >>> b[2] = 107 >>> i = I2C.writeto(24,b) 3
內存操作¶
某些 I²C 設備作爲存儲設備 (或一組寄存器) ,可以讀取或者寫入。這種情況下,有兩個地址和 I²C 事務相關: 從設備地址和內存地址。下面方法用於和這些設備進行通信。
I2C.readfrom_mem(addr, memaddr, nbytes, addrsize=8)¶
函數說明:從I²C設備的寄存器中讀取並返回數據。
addr
:I²C設備地址(可由scan函數讀取出來)
memaddr
:寄存器地址
nbytes
:要讀取的字節大小
addrsize
:指定地址大小,默認爲8位(在ESP8266上這個參數無效,地址大小總是8位)
示例:
b = I2C. readfrom_mem(24, 0x58, 3) print(b)
運行結果:
b'\x00\x02\x01'
I2C.readfrom_mem_into(addr, memaddr, buf, addrsize=8)¶
函數說明:從I²C設備的寄存器中讀取buf.len()個數據到buf,無返回值。
addr
:I²C設備地址(可由scan函數讀取出來)
memaddr
:寄存器地址
buf
:存儲數據的緩衝區
addrsize
:指定地址大小,默認爲8位(在ESP8266上這個參數無效,地址大小總是8位),讀取數據數量是buf的長度。
示例:
buf=bytearray(8) I2C.readfrom_mem_into(24, 0x58, buf)
I2C.writeto_mem(addr, memaddr, buf, addrsize=8)¶
函數說明: 將buf 中的數據全部寫入到從設備 addr 的內存 memaddr。
addr
:I²C設備地址(可由scan函數讀取出來)
memaddr
:寄存器地址
buf
:存儲數據的緩衝區
addrsize
:指定地址大小,默認爲8位(在ESP8266上這個參數無效,地址大小總是8位),讀取數據數量是buf的長度。
示例:
buf = b'123' I2C.writeto_mem(24, 0x58, buf)