I2C總線簡介
I2C是兩線式串行總線,用于連接微控制器及其外圍設(shè)備。
I2C總線最主要的優(yōu)點是其簡單性和有效性。由于接口直接在組件之上,因此I2C總線占用的空間非常小,減少了電路板的空間和芯片管腳的數(shù)量,降低了互聯(lián)成本??偩€的長度可高達25英尺,并且能夠以10Kbps的最大傳輸速率支持40個組件。I2C總線的另一個優(yōu)點是,它支持多主控(multimastering), 其中任何能夠進行發(fā)送和接收的設(shè)備都可以成為主總線。一個主控能夠控制信號的傳輸和時鐘頻率。當然,在任何時間點上只能有一個主控。
單片機的通訊模塊常用的有UART、SPI、I2C、CAN等等,當然還有無線模塊這里不討論。UART大多用于單片機與PC的通信,CAN總線常用于單片機與單片機通信,而SPI和I2C則用于單片機和外圍設(shè)備、外圍設(shè)備和其它外圍設(shè)備的通信,根據(jù)自己的需要來設(shè)計通訊方式。
相比較SPI而言,I2C需要更少的接線,僅由數(shù)據(jù)線SDA和時鐘SCL構(gòu)成 。而SPI則需要四根引線,它們是SDI(數(shù)據(jù)輸入),SDO(數(shù)據(jù)輸出),SCLK(時鐘),CS(片選)。但是常常因為I2C的通訊協(xié)議較為復(fù)雜,不容易在程序中實現(xiàn)而導(dǎo)致數(shù)據(jù)丟失、無應(yīng)答、“死等”等問題。
I2C通信、讀寫數(shù)據(jù)過程
在通信之初,主從機必須根據(jù)自己的要求約定好通信規(guī)則:command的定義和位置、address的位數(shù)和位置。
以讀寫從機寄存器數(shù)據(jù)為例:
假設(shè)從機寄存器地址為8位、從機寄存器也位8位(被讀取數(shù)據(jù)為8位);
約定讀command為0x01,寫command位0x02;
約定主機發(fā)起通信后,第一個slave address字節(jié)收到ack后,緊跟的一個字節(jié)為command,再下面一個字節(jié)為address。
1. 讀寄存器數(shù)據(jù)步驟:
1.1 主機先發(fā)起一次通信,將讀command(0x01)和需要讀取的寄存器地址address寫入從機;(主機發(fā)出寫操作)
1.2 從機firmware的處理:
1.2.1 將command和address分別提取出來;
1.2.2 判斷command的含義(本例中,是讀指令還是寫指令);
1.2.3 根據(jù)收到的的address,將對應(yīng)寄存器的的數(shù)據(jù)放入從機I2C輸出buffer;(這個步驟可以使用指針)
1.3 主機再次發(fā)起一次通信,讀取從機的數(shù)據(jù);(主機發(fā)出讀操作)
2. 寫操作步驟:
2.1 主機發(fā)起通信,按約定依次寫入command、要寫入的從機寄存器地址address和要寫入的數(shù)據(jù)data;
2.2 從機firmware要做的處理:
2.2.1 分別提取command、address和data;
2.2.2 根據(jù)command做出判斷(本例中則判斷是寫入還是讀取);
2.2.3 將data寫入與接收到的address對應(yīng)的寄存器。(這個步驟可以使用指針)。
4、主機發(fā)送數(shù)據(jù)流程
(1)主機在檢測到總線為“空閑狀態(tài)”(即 SDA、SCL 線均為高電平)時,發(fā)送一個啟動信號“S”,開始一次通信的開始
?。?)主機接著發(fā)送一個命令字節(jié)。該字節(jié)由 7 位的外圍器件地址和 1 位讀寫控制位 R/W組成(此時 R/W=0)
?。?)相對應(yīng)的從機收到命令字節(jié)后向主機回饋應(yīng)答信號 ACK(ACK=0)
?。?)主機收到從機的應(yīng)答信號后開始發(fā)送第一個字節(jié)的數(shù)據(jù)
?。?)從機收到數(shù)據(jù)后返回一個應(yīng)答信號 ACK
?。?)主機收到應(yīng)答信號后再發(fā)送下一個數(shù)據(jù)字節(jié)
?。?)當主機發(fā)送最后一個數(shù)據(jù)字節(jié)并收到從機的 ACK 后,通過向從機發(fā)送一個停止信號P結(jié)束本次通信并釋放總線。從機收到P信號后也退出與主機之間的通信
注意:①主機通過發(fā)送地址碼與對應(yīng)的從機建立了通信關(guān)系,而掛接在總線上的其它從機雖然同時也收到了地址碼,但因為與其自身的地址不相符合,因此提前退出與主機的通信;②主機的一次發(fā)送通信,其發(fā)送的數(shù)據(jù)數(shù)量不受限制。主機是通過 P 信號通知發(fā)送的結(jié)束,從機收到 P 信號后退出本次通信;③主機的每一次發(fā)送后都是通過從機的 ACK 信號了解從機的接收狀況,如果應(yīng)答錯誤則重發(fā)。
5、主機接收數(shù)據(jù)流程
?。?)主機發(fā)送啟動信號后,接著發(fā)送命令字節(jié)(其中 R/W=1)
(2)對應(yīng)的從機收到地址字節(jié)后,返回一個應(yīng)答信號并向主機發(fā)送數(shù)據(jù)
?。?)主機收到數(shù)據(jù)后向從機反饋一個應(yīng)答信號
?。?)從機收到應(yīng)答信號后再向主機發(fā)送下一個數(shù)據(jù)
?。?)當主機完成接收數(shù)據(jù)后,向從機發(fā)送一個“非應(yīng)答信號(ACK=1)”,從機收到ASK=1 的非應(yīng)答信號后便停止發(fā)送
?。?)主機發(fā)送非應(yīng)答信號后,再發(fā)送一個停止信號,釋放總線結(jié)束通信
注意:主機所接收數(shù)據(jù)的數(shù)量是由主機自身決定,當發(fā)送“非應(yīng)答信號/A”時從機便結(jié)束傳送并釋放總線(非應(yīng)答信號的兩個作用:前一個數(shù)據(jù)接收成功,停止從機的再次發(fā)送)。
6、總線死鎖原因分析
I2C總線寫操作過程中,主機在產(chǎn)生啟動信號后控制SCL產(chǎn)生8個時鐘脈沖,然后拉低SCL信號為低電平,在這個時候,從機輸出應(yīng)答信號,將SDA信號拉為低電平。如果這個時候主機異常復(fù)位,SCL就會被釋放為高電平。此時,如果從機沒有復(fù)位,就會繼續(xù)I2C的應(yīng)答,將SDA一直拉為低電平,直到SCL變?yōu)榈碗娖?,才會結(jié)束應(yīng)答信號。而對于主機來說,復(fù)位后檢測SCL和SDA信號,如果發(fā)現(xiàn)SDA信號為低電平,則會認為I2C總線被占用,會一直等待SCL和SDA信號變?yōu)楦唠娖?。這樣,主機等待從機釋放SDA信號,而同時從機又在等待主機將SCL信號拉低以釋放應(yīng)答信號,兩者相互等待,I2C總線進人一種死鎖狀態(tài)。同樣,當I2C進行讀操作時,從機應(yīng)答后輸出數(shù)據(jù),如果在這個時刻主機異常復(fù)位而此時從機輸出的數(shù)據(jù)位正好為0,也會導(dǎo)致I2C總線進入死鎖狀態(tài)。
解決方案通常有如下幾種:
?。?)將從機的電源設(shè)計為可控,當發(fā)生總線死鎖的時將從機復(fù)位
?。?)可以在從機的程序中加入監(jiān)測功能,如果總線長時間被拉低則釋放對總線的控制
?。?)在主機中增加I2C總線恢復(fù)程序。每次主機復(fù)位后,如果檢測到SDA被拉低,則控制SCL產(chǎn)生《=9個時鐘脈沖(針對8位數(shù)據(jù)的情況),每發(fā)送一個時鐘脈沖就檢測SDA是否被釋放,如果SDA已經(jīng)被釋放就再模擬產(chǎn)生一個停止信號,這樣從機就可以完成被掛起的讀寫操作,從死鎖狀態(tài)中恢復(fù)過來。這種方法有一定的局限性,因為大部分主機的I2C模塊由內(nèi)置的硬件電路來實現(xiàn),軟件并不能夠直接控制SCL信號模擬產(chǎn)生需要時鐘脈沖。
掛在I2C總線上的EEPROM設(shè)備
EEPROM稱為電擦除式只讀存儲器,一般容量很小、用于保存產(chǎn)品的固化參數(shù),此次跟我狹路相逢的是一款來自ATMEL公司的AT24C512B,總?cè)萘繛?4K,支持以頁的方式寫入數(shù)據(jù),頁大小128字節(jié),以下是這款設(shè)備的相關(guān)信息和操作方法(其他型號類同):
硬件連接。在AT24C512B硬件連接中,跟軟件編程相關(guān)的引腳有三個,除了連接在I2C總線上的時鐘線(SCL)、數(shù)據(jù)線(SDA)引腳之外,還有一個寫保護引腳(WP)連接在GPIO上。
尋址方式。EEPROM可以讓你精確地訪問到每一字節(jié),AT24C512B采用16位的尋址方式共計可以訪問65536字節(jié)的地址空間。
讀寫時序。AT24C512B支持的寫操作有單字節(jié)寫入、按頁寫入,支持的讀操作有隨機單字節(jié)或連接讀取、當前位置單字節(jié)或連續(xù)讀取,EEPROM一般在電路中做從設(shè)備,我此次面對的也是,以下是主設(shè)備對EEPROM進行各種操作的操作方法:
單字節(jié)寫入:START -》 發(fā)送從設(shè)備地址(寫控制碼) -》 處理Ack -》 發(fā)送字節(jié)地址 -》 處理Ack [-》 發(fā)送1字節(jié)數(shù)據(jù) -》 處理Ack] -》 STOP。
按頁寫入:將單字節(jié)寫入的[ ]中的操作重復(fù)進行128次即可實現(xiàn)。
隨機單字節(jié)讀?。篠TART -》 發(fā)送從設(shè)備地址(寫控制碼) -》 處理Ack -》 發(fā)送字節(jié)地址 -》 處理Ack -》 START -》 發(fā)送器件地址(讀控制碼) -》 處理Ack -》 接收1字節(jié)數(shù)據(jù) -》 STOP。
隨機連續(xù)讀?。涸陔S機單字節(jié)讀取操作的STOP信號發(fā)送之前,加入若干個 [-》 發(fā)送Ack -》 接收1字節(jié)數(shù)據(jù)] 即可實現(xiàn)。
當前位置單字節(jié)讀?。篠TART -》 發(fā)送從設(shè)備地址(讀控制碼) -》 處理Ack -》 發(fā)送字節(jié)地址 -》 處理Ack -》 接收1字節(jié)數(shù)據(jù) -》 STOP。當前指的是之前進行過讀取操作但是沒有發(fā)送STOP信號,EEPROM芯片內(nèi)部指針所在的位置即為當前位置。
當前位置連續(xù)讀?。涸诋斍拔恢脝喂?jié)讀取操作的STOP信號發(fā)送之前,加入若干個 [-》 發(fā)送Ack -》 接收1字節(jié)數(shù)據(jù)] 即可實現(xiàn)。
關(guān)于EEPROM的按頁寫入。為提高數(shù)據(jù)寫入效率,有的EEPROM設(shè)備用一個內(nèi)部的RAM來提供按頁寫入的功能,進行寫操作的時候,先記錄下要寫入的首地址,然后將接收到的數(shù)據(jù)都緩存在RAM中,在接收到STOP信號時再把緩存數(shù)據(jù)一次性保存到先前記錄的地址處。
有兩個需要注意的問題:(a)、如果寫入的數(shù)據(jù)超過一頁的長度,將發(fā)生回卷,即從RAM的0地址處進行數(shù)據(jù)覆蓋。(b)、如果頁大小為128字節(jié),即0-127字節(jié)為第一頁、128-255為第二頁,即頁的邊界位置是絕對的,而不是從寫入數(shù)據(jù)的起始位置開始計算。
在進行數(shù)據(jù)讀取操作沒有頁的問題,可以從任意位置開始讀取任意大小的數(shù)據(jù),超過EEPROM總?cè)萘繒r發(fā)生回卷。
評論