I2C(也常寫作IIC,I2C),全稱為Inter-Integrated Circuit(“互連集成電路”),用于在集成電路之間進行短距離數據傳輸。它由Philips(現在的NXP半導體)公司于1980年代初開發,并成為一種廣泛應用于電子設備之間通信的標準。I2C協議簡單、靈活且廣泛支持,常用于連接傳感器、存儲器、顯示屏和其他外設到微控制器、微處理器或其他集成電路上。這是一種簡單的雙向雙線總線,非常適合用于微控制器與外設之間,或者多個微控制器之間的高效互連控制。
其名稱反映了最初的設計目的:它專為“集成電路”間的通信設計。I2C的兩條線包括SDA(串行數據線),用于傳輸信息;和SCL(串行時鐘線),負責同步。I2C的一個優點是它只需要這兩根線進行通信,這可以在復雜系統中有效地利用資源。
它也是一種在許多產品中廣泛使用的標準,擴展和集成相對簡單。
I2C的主要特點包括:
- 通信方式:I2C采用主從式通信方式。一個主設備(通常是微控制器或處理器)控制總線并發起通信,而一個或多個從設備被動地響應主設備的命令或請求。
- 總線結構:I2C使用兩根線路進行通信:* SCL(Serial Clock):時鐘線由主設備提供,用于同步通信速度。* SDA(Serial Data):數據線用于傳輸數據和控制信號。
- 信號電平:I2C使用雙向開漏(open-drain)輸出,這意味著信號線可以被拉低(邏輯0)但不能被主動拉高,只能通過外部上拉電阻回到高電平(邏輯1)。
- 地址和幀格式:I2C使用7位或10位的設備地址來唯一標識每個從設備。通信幀包括設備地址、讀/寫位、數據字節和應答位(ACK)。
- 速率和模式:I2C支持不同的通信速率,通常有標準模式(100 kbit/s)和快速模式(400 kbit/s)。還有更高速的模式如高速模式(3.4 Mbit/s)和超高速模式(5 Mbit/s)。
- 啟動和停止條件:I2C通信的開始由主設備發送啟動信號(SDA從高電平切換到低電平,同時SCL保持高電平)表示。通信結束時,主設備發送停止信號(SDA從低電平切換到高電平,同時SCL保持高電平)表示。
- 多主設備支持:I2C協議允許多個主設備共享同一條總線,通過仲裁機制來解決總線競爭問題。
- 應用領域:I2C廣泛應用于各種設備和應用領域,例如傳感器、存儲器(如EEPROM)、顯示屏、溫度傳感器、實時時鐘(RTC)、擴展IO芯片等。
I2C是一種簡單、靈活且廣泛支持的串行通信協議,適用于在電子設備之間進行短距離數據傳輸。它具有較低的硬件復雜性和通信開銷,因此在許多嵌入式系統和電子設備中被廣泛采用。
I2C協議還有一些其他的特性和擴展,如高速模式、高容量模式、地址擴展等,以滿足更復雜的應用需求。雖然I2C并不適合所有情況(例如,它并不適用于長距離或高速應用),但它是一種常見的、實用的選擇,適用于簡單的內部通信。當希望許多從設備共享通信線并由一個(或多個)主設備管理時,它特別有用。
I2C總線通用連接框圖:
I2C總線互聯系統示意圖
在單主機系統中,可以抽象為:
單主機I2C總線
典型的多主機系統則為:
多主機I2C總線
電氣特性
I2C使用開漏/開集電極結構,同時在同一數據線上使用輸入緩沖器,這允許單個數據線用于雙向數據流。
雙向通信的開漏結構
開漏指的是一種輸出類型,可以將總線拉低到某個電壓(通常是地線),或者"釋放"總線,讓其被上拉電阻拉高。當總線被主設備或從設備釋放時,上拉電阻(RPU)負責將總線電壓拉高到電源電平。由于沒有設備可以強制將線路拉高,這意味著總線永遠不會遇到通信問題,其中一個設備嘗試傳輸高電平,而另一個設備傳輸低電平,導致短路(電源電平到地)。I2C要求在多主設備環境中,如果主設備發送高電平,但檢測到線路低電平(另一個設備將其拉低),則停止通信,因為另一個設備正在使用總線。推挽接口不允許此類自由,這是I2C的一個優點。
SDA/SCL內部基本結構
上圖顯示了在SDA/SCL線上的從設備或主設備的內部結構的簡化視圖,包括用于讀取輸入數據的緩沖器和用于傳輸數據的下拉場效應晶體管(FET)。設備只能將總線線路拉低(與地短接)或釋放總線線路(對地高阻抗),并允許上拉電阻將電壓拉高。這是在處理I2C設備時需要了解的重要概念,因為沒有設備可以將總線保持高電平。這個特性是實現雙向通信的關鍵。
拉低總線
如前所述,開漏結構只能將總線拉低,或者"釋放"總線并由電阻將其拉高。下圖展示了將總線拉低時的電流流動。希望傳輸低電平的邏輯會激活下拉場效應晶體管,提供對地的短路,將線路拉低。
拉低總線
釋放總線
當從設備或主設備希望傳輸邏輯高時,它只能通過關閉下拉場效應晶體管來釋放總線。這將使總線懸空,而上拉電阻將把電壓拉高到電源電平,被解釋為高電平。下圖展示了通過上拉電阻的電流流動,將總線拉高。
釋放總線
通信速率模式
速率模式
常規模式
高速模式
超快模式
接口規范
一般操作
I2C總線是一種標準的雙向接口,使用控制器(即主設備)與從設備進行通信。除非被主設備尋址,否則從設備不能傳輸數據。I2C總線上的每個設備都有一個特定的設備地址,以區分其他在同一I2C總線上的設備。許多從設備在啟動時需要進行配置,以設置設備的行為。這通常在主設備訪問從設備的內部寄存器映射時完成,這些寄存器具有唯一的寄存器地址。一個設備可以有一個或多個寄存器,用于存儲、寫入或讀取數據。
物理的I2C接口由串行時鐘(SCL)線和串行數據(SDA)線組成。SDA和SCL線都必須通過上拉電阻連接到VCC電源。上拉電阻的大小取決于I2C線路上的電容量。只有當總線處于空閑狀態時,才能啟動數據傳輸。如果在停止條件之后,SDA和SCL線都保持高電平,總線將被認為是空閑的。
主設備訪問從設備的一般過程如下:
- 如果主設備想要向從設備發送數據:
- 主設備-發送器發送起始條件并尋址從設備-接收器* 主設備-發送器向從設備-接收器發送數據* 主設備-發送器通過停止條件終止傳輸
- 如果主設備想要接收/讀取從設備的數據:
- 主設備-接收器發送起始條件并尋址從設備-發送器
- 主設備-接收器向從設備-發送器發送要讀取的寄存器
- 主設備-接收器從從設備-發送器接收數據
- 主設備-接收器通過停止條件終止傳輸
起始和停止條件
I2C通信通過主設備發送起始條件(START condition)來初始化,并通過主設備發送停止條件(STOP condition)來終止。在SCL線為高電平時,SDA線上的高至低的跳變定義了起始條件。在SCL線為高電平時,SDA線上的低至高的跳變定義了停止條件。
起始和停止條件示例
重復起始條件
重復的起始條件(Repeated START condition)類似于起始條件(START condition),用作連續的停止條件和起始條件的替代。它在外觀上與起始條件相同,但與起始條件不同,因為它發生在停止條件之前(總線不處于空閑狀態)。這在主設備希望啟動新的通信,但不希望通過停止條件使總線空閑,從而可能失去對總線的控制權(在多主設備環境中)時非常有用。
數據有效性和字節格式
在每個SCL時鐘脈沖期間傳輸一個數據位。一個字節由SDA線上的八個位組成。一個字節可以是設備地址、寄存器地址或從從設備寫入或讀取的數據。數據以最高有效位(MSB)優先的順序傳輸。在START和STOP條件之間,可以從主設備向從設備傳輸任意數量的數據字節。在時鐘周期的高電平期間,SDA線上的數據必須保持穩定,因為當SCL為高電平時,數據線的變化被解釋為控制命令(START或STOP)。
單字節傳輸示例
應答(ACK)和非應答(NACK)
接收器在每個數據字節(包括地址字節)后發送一個ACK位。ACK位允許接收器向發送器傳達字節已成功接收,并且可以發送另一個字節。
在接收器發送ACK之前,發送器必須釋放SDA線。為了發送ACK位,接收器在ACK/NACK相關的時鐘周期(周期9)的低電平期間拉低SDA線,以便在ACK/NACK相關的時鐘周期的高電平期間,SDA線保持穩定低電平。必須考慮到設置時間和保持時間。
當SDA線在ACK/NACK相關的時鐘周期中保持高電平時,這被解釋為非應答(NACK)。有幾種情況會導致產生NACK:
- 接收器由于執行某些實時功能并且尚未準備好與主設備開始通信,因此無法接收或發送。
- 在傳輸過程中,接收器接收到無法理解的數據或命令。
- 在傳輸過程中,接收器無法接收更多的數據字節。
- 主設備接收器完成數據讀取,并通過NACK向從設備表示這一點。
NACK波形示例
I2C數據
必須通過讀取或寫入從設備的寄存器來發送和接收數據。寄存器是從設備內存中包含信息的位置,無論是配置信息還是一些采樣數據用于發送回主設備。主設備必須向這些寄存器中寫入信息,以指示從設備執行任務。
雖然在I2C從設備中常見的是寄存器,但請注意,并非所有的從設備都會有寄存器。一些設備非常簡單,只包含一個寄存器,可以通過在從設備地址之后立即發送寄存器數據來直接寫入該寄存器,而無需尋址寄存器。單寄存器設備的一個例子是8位I2C開關,它通過I2C命令進行控制。由于它只有一個位來啟用或禁用通道,因此只需要一個寄存器,主設備只需在從設備地址之后寫入寄存器數據,跳過寄存器編號。
向I2C總線上的從設備寫入數據
要在I2C總線上寫入數據,主設備將在總線上發送一個帶有從設備地址的起始條件,并將最后一位(R/W位)設置為0,表示寫入操作。在從設備發送應答位后,主設備將發送要寫入的寄存器地址。從設備再次發送應答位,通知主設備它已準備好。然后,主設備將開始向從設備發送寄存器數據,直到主設備發送完所有需要的數據(有時僅為一個字節),然后通過發送停止條件終止傳輸。
下圖顯示了向從設備寄存器寫入單個字節的示例:
向從設備寄存器寫數據示例
從I2C總線上的從設備讀取數據
從從設備讀取數據與寫入數據非常相似,但有一些額外的步驟。為了從從設備讀取數據,主設備首先必須告知從設備它希望從哪個寄存器讀取數據。主設備通過類似寫入操作的方式開始傳輸,發送帶有R/W位為0(表示寫入)的地址,然后是要從中讀取數據的寄存器地址。一旦從設備確認了寄存器地址,主設備將再次發送起始條件,然后發送帶有R/W位為1(表示讀取)的從設備地址。這次,從設備將確認讀取請求,主設備釋放SDA總線,但仍向從設備提供時鐘。在此事務的這個階段,主設備將變為主設備接收器,而從設備將變為從設備發送器。
主設備將繼續發送時鐘脈沖,但會釋放SDA線,以便從設備可以傳輸數據。在每個數據字節結束時,主設備將向從設備發送一個ACK,表示主設備準備好接收更多數據。一旦主設備接收到了所期望的字節數,它將發送一個NACK,告知從設備停止通信并釋放總線。主設備隨后發送停止條件。
下圖顯示了從從設備寄存器讀取單個字節的示例:
從從設備寄存器讀數據示例
實例
這兩天調試這個模塊時捕捉了一些波形,可以結合前文一起理解:
實例1
對應代碼:
result = ch347_driver.i2c_set(device_index, 1)
if result:
print("Success to set I2C speed.")
else:
print("Failed to set I2C speed.")
result = ch347_driver.i2c_set_delay_ms(device_index, 1)
if result:
print("Success to set I2C delay.")
else:
print("Failed to set I2C delay.")
result = ch347_driver.stream_i2c(device_index, b'x13', 2)
if result:
print("Success!")
else:
print("Failed!")
實例2
對應代碼:
result = ch347_driver.i2c_set(device_index, 1)
if result:
print("Success to set I2C speed.")
else:
print("Failed to set I2C speed.")
result = ch347_driver.i2c_set_delay_ms(device_index, 1)
if result:
print("Success to set I2C delay.")
else:
print("Failed to set I2C delay.")
result = ch347_driver.stream_i2c(device_index, b'x12x13x14', 8)
if result:
print("Success!")
else:
print("Failed!")
總結
I2C總結
評論