前言:
I2C(Inter-Integrated?Circuit?BUS)是集成電路總線,是目前應用最廣泛的總線之一,最初由PHILIPS(現為NXP)設計。它使用多主從架構,主要用于連接低速周邊設備。I2C總線在硬件物理層包括兩條線:一條數據線(SDA)和一條時鐘線(SCL)。所有設備都連接在這兩條線上。I2C協議在物理層之上添加了協議層,定義了通信的規則和過程。I2C通信過程可以類比為體育老師和學生之間的球類傳遞。老師(主設備)可以將球(數據)發送給學生(從設備),也可以從學生那里接收球。在接收到球后,學生應該向老師作出回應。這個過程可以對應到I2C通信中的數據傳輸和應答過程。I2C總線在傳輸數據時,需要主設備發送時鐘信號,從設備在每個時鐘脈沖的上升沿或下降沿發送或接收一位數據。
一、I2C 硬件框架
在一個芯片(SoC)內部,有一個或多個 I2C 控制器
在一個 I2C 控制器上,可以連接一個或多個 I2C 設備
I2C 總線只需要 2 條線:時鐘線 SCL、數據線 SDA
在 I2C 總線的 SCL、SDA 線上,都有上拉電阻
二、I2C 軟件框架
?以 I2C 接口的存儲設備 AT24C02 為例:
APP:
???????? 提出要求:把字符串"www.100ask.net"寫入 AT24C02 地址 16 開始的 地方
? ? ? ? ?它是大爺,不關心底層實現的細節
? ? ? ? ?它只需要調用設備驅動程序提供的接口
AT24C02 驅動:
??????????它知道 AT24C02 要求的地址、數據格式
? ? ? ? ? 它知道發出什么信號才能讓 AT24C02 執行擦除、燒寫工作
? ? ? ? ? 它知道怎么判斷數據是否燒寫成功
??????????它構造好一系列的數據,發給 I2C 控制器
I2C 控制器驅動:
???????? 它根據 I2C 協議發出各類信號:I2C 設備地址、I2C 存儲地址、數據
? ? ? ? ?它根據 I2C 協議判斷
三、I2C協議:
1.硬件連接:
????????I2C 在硬件上的接法如下所示,主控芯片引出兩條線 SCL,SDA 線,在一條 I2C 總線上可以接很多 I2C 設備,我們還會放一個上拉電阻。
?2.傳輸數據類比
????????怎么通過 I2C 傳輸數據,我們需要把數據從主設備發送到從設備上去,也需要把數據從設備傳送到主設備上去,數據涉及到雙向傳輸。
體育老師:可以把球發給學生,也可以把球從學生中接過來。
發球:
????????老師:開始了(start)
? ? ? ? 老師:A!我要發球給你!(地址/方向)
????????學生 A:到!(回應)
????????老師把球發出去(傳輸)
????????A 收到球之后,應該告訴老師一聲(回應)
????????老師:結束(停止)
接球:
????????老師:開始了(start)
????????老師:B!把球發給我!(地址/方向)
????????學生 B:到!
????????B 把球發給老師(傳輸)
????????老師收到球之后,給 B 說一聲,表示收到球了(回應)
????????老師:結束(停止)
我們就使用這個簡單的例子,來解釋一下 IIC 的傳輸協議:
老師說開始了,表示開始信號(start)
老師提醒某個學生要發球,表示發送地址和方向(address/read/write)
老師發球/接球,表示數據的傳輸
收到球要回應:回應信號(ACK)
老師說結束,表示 IIC 傳輸結束(P)
3. IIC 傳輸數據的格式
(1)寫操作
主芯片要發出一個 start 信號
然后發出一個設備地址(用來確定是往哪一個芯片寫數據),方向(讀/寫,0 表示寫,1 表示讀)
從設備回應(用來確定這個設備是否存在),然后就可以傳輸數據
主設備發送一個字節數據給從設備,并等待回應
每傳輸一字節數據,接收方要有一個回應信號(確定數據是否接受完成),然后再傳輸下一個數據。
數據發送完之后,主芯片就會發送一個停止信號。
圖:白色背景表示"主→從",灰色背景表示"從→主"
(2)讀操作
主芯片要發出一個 start 信號
然后發出一個設備地址(用來確定是往哪一個芯片寫數據),方向(讀/寫,0 表示寫,1 表示讀)
從設備回應(用來確定這個設備是否存在),然后就可以傳輸數據
從設備發送一個字節數據給主設備,并等待回應
每傳輸一字節數據,接收方要有一個回應信號(確定數據是否接受完成),然后再傳輸下一個數據。
數據發送完之后,主芯片就會發送一個停止信號。
下圖:白色背景表示"主→從",灰色背景表示"從→主"?
(3).I2C 信號
????????I2C 協議中數據傳輸的單位是字節,也就是 8 位。但是要用到 9 個時鐘:前面 8 個時鐘用來傳輸 8 數據,第 9 個時鐘用來傳輸回應信號。傳輸時,先傳輸最高位(MSB)。
開始信號(S):SCL 為高電平時,SDA 山高電平向低電平跳變,開始傳送數據。
結束信號(P):SCL 為高電平時,SDA 由低電平向高電平跳變,結束傳送數據。
響應信號(ACK):接收器在接收到 8 位數據后,在第 9 個時鐘周期,拉低 SDA
SDA 上傳輸的數據必須在 SCL 為高電平期間保持穩定,SDA 上的數據只能在 SCL 為低電平期間變化
I2C 協議信號如下:
????????在SCL低電平狀態下改變SDA,在SCL高電平狀態下保持SDA。
?(4).協議細節
1) 如何在 SDA 上實現雙向傳輸?
????????主芯片通過一根 SDA 線既可以把數據發給從設備,也可以從 SDA 上讀取數據,連接 SDA 線的引腳里面必然有兩個引腳(發送引腳/接受引腳)。
2) 主、從設備都可以通過 SDA 發送數據,肯定不能同時發送數據,怎么錯開時間?
在9 個時鐘里:
????????前 8 個時鐘由主設備發送數據的話,第 9 個時鐘就由從設備發送數據。
????????前 8 個時鐘由從設備發送數據的話,第 9 個時鐘就由主設備發送數據。
3) 雙方設備中,某個設備發送數據時,另一方怎樣才能不影響 SDA 上的數據?
????????設備的 SDA 中有一個三極管,使用開極/開漏電路(三極管是開極,CMOS 管是開漏,作用一樣)。
如果主機發送1,從機因為某些錯誤發送了0,則會出現短路的狀況,容易燒起來。
這里我們就需要運用到規范鐘:
??
????????外加一個上拉電阻有效的避免了短路燒毀電路情況的發生。
????????當某一個芯片不想影響 SDA 線時,那就不驅動這個三極管
????????想讓 SDA 輸出高電平,雙方都不驅動三極管(SDA 通過上拉電阻變為高電平)
????????想讓 SDA 輸出低電平,就驅動三極管
例子:
主設備發送(8bit)給從設備
????????前 8 個 clk
????????從設備不要影響 SDA,從設備不驅動三極管
????????主設備決定數據,主設備要發送 1 時不驅動三極管,要發送 0 時驅動三極管
????????第 9 個 clk,由從設備決定數據
????????主設備不驅動三極管
????????從設備決定數據,要發出回應信號的話,就驅動三極管讓 SDA 變為 0
為何 SCL 也要使用上拉電阻??
????????在第 9 個時鐘之后,如果有某一方需要更多的 時間來處理數據,它可以一直驅動三極管把 SCL 拉低。
????????當 SCL 為低電平時候,大家都不應該使用 IIC 總線,只有當 SCL 從低電平變為高電平的時候,IIC 總線才能被使用。
????????當它就緒后,就可以不再驅動三極管,這是上拉電阻把 SCL 變為高電平,其 他設備就可以繼續使用 I2C 總線了。
四、SMBus 協議
1. SMBus 是 I2C 協議的一個子集
SMBus: System Management Bus,系統管理總線。
SMBus 最初的目的是為智能電池、充電電池、其他微控制器之間的通信鏈路而定義的。
SMBus 也被用來連接各種設備,包括電源相關設備,系統傳感器,EEPROM 通訊 設備等等。
SMBus 為系統和電源管理這樣的任務提供了一條控制總線,使用 SMBus 的系統,設備之間發送和接收消息都是通過 SMBus,而不是使用單獨的控制線,這樣可以節省設備的管腳數。
SMBus 是基于 I2C 協議的,SMBus 要求更嚴格,SMBus 是 I2C 協議的子集。
SMBus 有哪些更嚴格的要求?跟一般的 I2C 協議有哪些差別?
VDD 的極限值不一樣
???????? I2C 協議:范圍很廣,甚至討論了高達 12V 的情況
???????? SMBus:1.8V~5V
最小時鐘頻率、最大的 Clock Stretching
???????? Clock Stretching 含義:某個設備需要更多時間進行內部的處理時, 它可以把 SCL 拉低占住 I2C 總線,也可以叫做時鐘延長。
???????? I2C 協議:時鐘頻率最小值無限制,Clock Stretching 時長也沒有 限制
??????? ?SMBus:時鐘頻率最小值是 10KHz,Clock Stretching 的最大時間值 也有限制
地址回應(Address Acknowledge):
一個 I2C 設備接收到它的設備地址后, 是否必須發出回應信號?
???????? I2C 協議:沒有強制要求必須發出回應信號
? ? ? ? ?SMBus:強制要求必須發出回應信號,這樣對方才知道該設備的狀態: busy,failed,或是被移除了
SMBus 協議明確了數據的傳輸格式
???????? I2C 協議:它只定義了怎么傳輸數據,但是并沒有定義數據的格式,這完全由設備來定義
???????? SMBus:定義了幾種數據格式(后面分析)
REPEATED START Condition(重復發出 S 信號)
????????比如讀 EEPROM 時,涉及 2 個操作: (1)把存儲地址發給設備 (2)讀數據
在寫、讀之間,可以不發出 P 信號,而是直接發出 S 信號:這個 S 信號就是 REPEATED START,如圖所示
SMBus Low Power Version:SMBus 也有低功耗的版本?
2.SMBus 協議分析?
????????對于 I2C 協議,它只定義了怎么傳輸數據,但是并沒有定義數據的格式, 這完全由設備來定義。
????????對于 SMBus 協議,它定義了幾種數據格式。
注意:下面文檔中的 Functionality flag 是 Linux 的某個 I2C 控制器驅動所支持的功能。比如 Functionality flag: I2C_FUNC_SMBUS_QUICK,表示需要 I2C 控制器支持 SMBus Quick Command。
(1)symbols(符號)
S (1 bit) : Start bit(開始位)
Sr (1 bit) : 重復的開始位
P (1 bit) : Stop bit(停止位)
R/W# (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.(讀寫位)
A, N (1 bit) : Accept and reverse accept bit.(回應位)
Address(7 bits): I2C 7 bit address. Note that this can be expanded as usual toget a 10 bit I2C address.(地址位,7 位地址)
Command Code (8 bits): Command byte, a data byte which often selects a register onthe device.(命令字節,一般用來選擇芯片內部的寄存器)
Data Byte (8 bits): A plain data byte. Sometimes, I write DataLow, DataHighfor 16 bit data.(數據字節,8 位;如果是 16 位數據的話,用 2 個字節來表示:DataLow、DataHigh)
Count (8 bits): A data byte containing the length of a block operation.(在 block 操作總,表示數據長度)
[..]: Data sent by I2C device, as opposed to data sent by the hostadapter.(中括號表示 I2C 設備發送的數據,沒有中括號表示 host adapter 發送的數據)
(2)SMBus Quick Command
????????只是用來發送一位數據:R/W#本意是用來表示讀或寫,但是在 SMBus 里可以用來表示其他含義。比如某些開關設備,可以根據這一位來決定是打開還是關閉。?
Functionality flag: I2C_FUNC_SMBUS_QUICK
(3)SMBus Receive Byte?
????????I2C-tools 中的函數:i2c_smbus_read_byte()。讀取一個字節,Host adapter 接收到一個字節后不需要發出回應信號(上圖中 N 表示不回應)。
Functionality flag: I2C_FUNC_SMBUS_READ_BYTE
?(4)SMBus Send Byte
????????I2C-tools 中的函數:i2c_smbus_write_byte()。發送一個字節。
Functionality flag: I2C_FUNC_SMBUS_WRITE_BYTE
(5)SMBus Read Byte?
????????I2C-tools 中的函數:i2c_smbus_read_byte_data()。先發出Command Code(它一般表示芯片內部的寄存器地址),再讀取一個字節的數據。上面介紹的 SMBus Receive Byte 是不發送 Comand,直接讀取數據。
Functionality flag: I2C_FUNC_SMBUS_READ_BYTE_DATA
(6)SMBus Read Word
????????I2C-tools 中的函數:i2c_smbus_read_word_data()。先發出 Command Code(它一般表示芯片內部的寄存器地址),再讀取 2 個字節的數據。?
Functionality flag: I2C_FUNC_SMBUS_READ_WORD_DATA
?(7)SMBus Write Byte
????????I2C-tools 中的函數:i2c_smbus_write_byte_data()。先發出 Command Code(它一般表示芯片內部的寄存器地址),再發出 1 個字節的數據。
Functionality flag: I2C_FUNC_SMBUS_WRITE_BYTE_DATA
(8)SMBus Write Word
????????I2C-tools 中的函數:i2c_smbus_write_word_data()。先發出 Command Code(它一般表示芯片內部的寄存器地址),再發出 1 個字節的數據。
Functionality flag: I2C_FUNC_SMBUS_WRITE_WORD_DATA
(9)SMBus Block Read
????????I2C-tools 中的函數:i2c_smbus_read_block_data()。先發出 Command Code(它一般表示芯片內部的寄存器地址),再發起度操作:
???????? 先讀到一個字節(Block Count),表示后續要讀的字節數
???????? 然后讀取全部數據?
Functionality flag: I2C_FUNC_SMBUS_READ_BLOCK_DATA
(10)SMBus Block Write
????????I2C-tools 中的函數:i2c_smbus_write_block_data()。先發出 Command Code(它一般表示芯片內部的寄存器地址),再發出 1 個字節的 Byte Conut(表 示后續要發出的數據字節數),最后發出全部數據。?
Functionality flag: I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
(11)I2C Block Read
????????在一般的 I2C 協議中,也可以連續讀出多個字節。它跟 SMBus Block Read 的差別在于設備發出的第 1 個數據不是長度 N,如下圖所示:
????????I2C-tools 中的函數:i2c_smbus_read_i2c_block_data()。先發出 Command Code(它一般表示芯片內部的寄存器地址),再發出 1 個字節的 Byte Conut(表示后續要發出的數據字節數),最后發出全部數據。
Functionality flag: I2C_FUNC_SMBUS_READ_I2C_BLOCK
(12)I2C Block Write
????????在一般的 I2C 協議中,也可以連續發出多個字節。它跟 SMBus Block Write 的差別在于發出的第 1 個數據不是長度 N,如下圖所示:
?????????I2C-tools 中的函數:i2c_smbus_write_i2c_block_data()。先發出 Command Code(它一般表示芯片內部的寄存器地址),再發出 1 個字節的 Byte Conut(表示后續要發出的數據字節數),最后發出全部數據。
Functionality flag: I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
(13)SMBus Block Write - Block Read Process Call
????????先寫一塊數據,再讀一塊數據。
Functionality flag: I2C_FUNC_SMBUS_BLOCK_PROC_CALL
(14)Packet Error Checking (PEC)
????????PEC 是一種錯誤校驗碼,如果使用 PEC,那么在 P 信號之前,數據發送方要 發送一個字節的 PEC 碼(它是 CRC-8 碼)。以 SMBus Send Byte 為例,下圖中,一個未使用 PEC,另一個使用 PEC:
3.SMBus 和 I2C 的建議?
????????因為很多設備都實現了 SMBus,而不是更寬泛的 I2C 協議,所以優先使用 SMBus。即使 I2C 控制器沒有實現 SMBus,軟件方面也是可以使用 I2C 協議來模擬 SMBus。所以:Linux 建議優先使用 SMBus。