I2C總線在物理連接上非常簡單,分別由SDA(串行數(shù)據(jù)線)和SCL(串行時(shí)鐘線)及上拉電阻組成。通信原理是通過對SCL和SDA線高低電平時(shí)序的控制,來產(chǎn)生I2C總線協(xié)議所需要的信號進(jìn)行數(shù)據(jù)的傳遞。在總線空閑狀態(tài)時(shí),這兩根線一般被上面所接的上拉電阻拉高,保持著高電平。
一、 技術(shù)性能:
工作速率有100K和400K兩種;
支持多機(jī)通訊;
支持多主控模塊,但同一時(shí)刻只允許有一個(gè)主控;
由數(shù)據(jù)線SDA和時(shí)鐘SCL構(gòu)成的串行總線;
每個(gè)電路和模塊都有唯一的地址;
每個(gè)器件可以使用獨(dú)立電源
二、 基本工作原理:
以啟動信號START來掌管總線,以停止信號STOP來釋放總線;
每次通訊以START開始,以STOP結(jié)束;
啟動信號START后緊接著發(fā)送一個(gè)地址字節(jié),其中7位為被控器件的地址碼,一位為讀/寫控制位R/W,R. /W位為0表示由主控向被控器件寫數(shù)據(jù),R/W為1表示由主控向被控器件讀數(shù)據(jù);
當(dāng)被控器件檢測到收到的地址與自己的地址相同時(shí),在第9個(gè)時(shí)鐘期間反饋應(yīng)答信號;
每個(gè)數(shù)據(jù)字節(jié)在傳送時(shí)都是高位(MSB)在前;
寫通訊過程:
1. 主控在檢測到總線空閑的狀況下,首先發(fā)送一個(gè)START信號掌管總線;
2. 發(fā)送一個(gè)地址字節(jié)(包括7位地址碼和一位R/W);
3. 當(dāng)被控器件檢測到主控發(fā)送的地址與自己的地址相同時(shí)發(fā)送一個(gè)應(yīng)答信號(ACK);
4. 主控收到ACK后開始發(fā)送第一個(gè)數(shù)據(jù)字節(jié);
5. 被控器收到數(shù)據(jù)字節(jié)后發(fā)送一個(gè)ACK表示繼續(xù)傳送數(shù)據(jù),發(fā)送NACK表示傳送數(shù)據(jù)結(jié)束;
6. 主控發(fā)送完全部數(shù)據(jù)后,發(fā)送一個(gè)停止位STOP,結(jié)束整個(gè)通訊并且釋放總線;
讀通訊過程:
1. 主控在檢測到總線空閑的狀況下,首先發(fā)送一個(gè)START信號掌管總線;
2. 發(fā)送一個(gè)地址字節(jié)(包括7位地址碼和一位R/W);
3. 當(dāng)被控器件檢測到主控發(fā)送的地址與自己的地址相同時(shí)發(fā)送一個(gè)應(yīng)答信號(ACK);
4. 主控收到ACK后釋放數(shù)據(jù)總線,開始接收第一個(gè)數(shù)據(jù)字節(jié);
5. 主控收到數(shù)據(jù)后發(fā)送ACK表示繼續(xù)傳送數(shù)據(jù),發(fā)送NACK表示傳送數(shù)據(jù)結(jié)束;
6. 主控發(fā)送完全部數(shù)據(jù)后,發(fā)送一個(gè)停止位STOP,結(jié)束整個(gè)通訊并且釋放總線;
? ?I2C總線特征
I2C總線上的每一個(gè)設(shè)備都可以作為主設(shè)備或者從設(shè)備,而且每一個(gè)設(shè)備都會對應(yīng)一個(gè)唯一的地址(可以從I2C器件的數(shù)據(jù)手冊得知),主從設(shè)備之間就通過這個(gè)地址來確定與哪個(gè)器件進(jìn)行通信,在通常的應(yīng)用中,我們把CPU帶I2C總線接口的模塊作為主設(shè)備,把掛接在總線上的其他設(shè)備都作為從設(shè)備。
I2C總線上可掛接的設(shè)備數(shù)量受總線的最大電容400pF 限制,如果所掛接的是相同型號的器件,則還受器件地址位的限制。
I2C總線數(shù)據(jù)傳輸速率在標(biāo)準(zhǔn)模式下可達(dá)100kbit/s,快速模式下可達(dá)400kbit/s,高速模式下可達(dá)3.4Mbit/s。一般通過I2C總線接口可編程時(shí)鐘來實(shí)現(xiàn)傳輸速率的調(diào)整,同時(shí)也跟所接的上拉電阻的阻值有關(guān)。
I2C總線上的主設(shè)備與從設(shè)備之間以字節(jié)(8位)為單位進(jìn)行雙向的數(shù)據(jù)傳輸。
I2C總線協(xié)議
I2C協(xié)議規(guī)定,總線上數(shù)據(jù)的傳輸必須以一個(gè)起始信號作為開始條件,以一個(gè)結(jié)束信號作為傳輸?shù)耐V箺l件。起始和結(jié)束信號總是由主設(shè)備產(chǎn)生。總線在空閑狀態(tài)時(shí),SCL和SDA都保持著高電平,當(dāng)SCL為高電平而SDA由高到低的跳變,表示產(chǎn)生一個(gè)起始條件;當(dāng)SCL為高而SDA由低到高的跳變,表示產(chǎn)生一個(gè)停止條件。在起始條件產(chǎn)生后,總線處于忙狀態(tài),由本次數(shù)據(jù)傳輸?shù)闹鲝脑O(shè)備獨(dú)占,其他I2C器件無法訪問總線;而在停止條件產(chǎn)生后,本次數(shù)據(jù)傳輸?shù)闹鲝脑O(shè)備將釋放總線,總線再次處于空閑狀態(tài)。如圖所示:
在了解起始條件和停止條件后,我們再來看看在這個(gè)過程中數(shù)據(jù)的傳輸是如何進(jìn)行的。前面我們已經(jīng)提到過,數(shù)據(jù)傳輸以字節(jié)為單位。主設(shè)備在SCL線上產(chǎn)生每個(gè)時(shí)鐘脈沖的過程中將在SDA線上傳輸一個(gè)數(shù)據(jù)位,當(dāng)一個(gè)字節(jié)按數(shù)據(jù)位從高位到低位的順序傳輸完后,緊接著從設(shè)備將拉低SDA線,回傳給主設(shè)備一個(gè)應(yīng)答位,此時(shí)才認(rèn)為一個(gè)字節(jié)真正的被傳輸完成。當(dāng)然,并不是所有的字節(jié)傳輸都必須有一個(gè)應(yīng)答位,比如:當(dāng)從設(shè)備不能再接收主設(shè)備發(fā)送的數(shù)據(jù)時(shí),從設(shè)備將回傳一個(gè)否定應(yīng)答位。數(shù)據(jù)傳輸?shù)倪^程如圖所示:
在前面我們還提到過,I2C總線上的每一個(gè)設(shè)備都對應(yīng)一個(gè)唯一的地址,主從設(shè)備之間的數(shù)據(jù)傳輸是建立在地址的基礎(chǔ)上,也就是說,主設(shè)備在傳輸有效數(shù)據(jù)之前要先指定從設(shè)備的地址,地址指定的過程和上面數(shù)據(jù)傳輸?shù)倪^程一樣,只不過大多數(shù)從設(shè)備的地址是7位的,然后協(xié)議規(guī)定再給地址添加一個(gè)最低位用來表示接下來數(shù)據(jù)傳輸?shù)姆较颍?表示主設(shè)備向從設(shè)備寫數(shù)據(jù),1表示主設(shè)備向從設(shè)備讀數(shù)據(jù)。如圖所示:
I2C總線操作
對I2C總線的操作實(shí)際就是主從設(shè)備之間的讀寫操作。大致可分為以下三種操作情況:
第一,主設(shè)備往從設(shè)備中寫數(shù)據(jù)。數(shù)據(jù)傳輸格式如下:
第二,主設(shè)備從從設(shè)備中讀數(shù)據(jù)。數(shù)據(jù)傳輸格式如下:
第三,主設(shè)備往從設(shè)備中寫數(shù)據(jù),然后重啟起始條件,緊接著從從設(shè)備中讀取數(shù)據(jù);或者是主設(shè)備從從設(shè)備中讀數(shù)據(jù),然后重啟起始條件,緊接著主設(shè)備往從設(shè)備中寫數(shù)據(jù)。數(shù)據(jù)傳輸格式如下:
第三種操作在單個(gè)主設(shè)備系統(tǒng)中,重復(fù)的開啟起始條件機(jī)制要比用STOP終止傳輸后又再次開啟總線更有效率。
1.2.1 I2C總線硬件接口電路示例一
這個(gè)電路是基于LPC2368 ARM7芯片進(jìn)行設(shè)計(jì)的,使用其內(nèi)部的I2C接口作為主設(shè)備,使用ADT75和SC16IS740作為兩個(gè)從設(shè)備的I2C總線應(yīng)用。
ADT75是一個(gè)帶I2C接口的溫度傳感器器件,數(shù)據(jù)手冊上對其地址的描述如下:
由此,其地址跟A0、A1、A2引腳的接法有關(guān),我們這里的實(shí)例是將A0、A1、A2全部接到高電平上,因此其地址是:1001111(即0x4F),又因根據(jù)協(xié)議再給地址添加一個(gè)最低位(方向位,默認(rèn)給寫方向),因此最后這個(gè)溫度傳感器作為從設(shè)備的地址是:10011110(即0x9E)。
SC16IS740是一個(gè)具有I2C或者SPI接口的擴(kuò)展UART的器件(通過第8腳來決定使用I2C還是SPI接口,我們這里要求使用I2C接口,因此將第8腳接到高電平)。根據(jù)數(shù)據(jù)手冊,我們同樣的可以知道地址跟A0、A1的接法有關(guān),我們這里的A0接高電平,A1接低電平。因此這個(gè)器件作為從設(shè)備的地址是:10010010(即0x92)。
1.2.2 I2C總線硬件接口電路示例二
這個(gè)電路是Mini2440開發(fā)板上I2C總線接口的應(yīng)用。我們可以看到,SDA和SCL線上接了一個(gè)10K的上拉排阻。AT24C08是一個(gè)容量為8Kbit的EEPROM存儲器件(注意是8Kbit,也就是1KB) ,根據(jù)數(shù)據(jù)手冊中器件地址部分的描述,AT24C08的地址是:1010+A2A1A0+方向位,其中1010是EEPROM的類型識別符;僅僅使用A2來確定總線訪問本器件的從設(shè)備地址,這里接的低電平,所以為0;A1和A0是器件內(nèi)部頁地址,在對器件擦除或者編程時(shí)使用,雖然這里也接的低電平,但器件內(nèi)部并不使用引腳的輸入值,也就是說A1和A0的值是由軟件進(jìn)行設(shè)定的。
1.3 脫離操作系統(tǒng)的I2C總線驅(qū)動示例(以電路示例一為例)
1.3.1 LPC2368中I2C接口寄存器描述
LPC2368中有三個(gè)I2C總線接口,分別表示為I2C0、I2C1和I2C2,每個(gè)I2C接口都包含7個(gè)寄存器。它們分別是:
I2C控制置位寄存器(I2CONSET): 8位寄存器,各位不同的設(shè)置是對I2C總線不同的控制。
在前面的I2C總線特征中我們提到過,I2C總線的速率通過可編程時(shí)鐘來調(diào)整,即必須通過軟件對I2SCLH和I2SCLL寄存器進(jìn)行設(shè)置來選擇合適的數(shù)據(jù)頻率和占空比。 頻率由下面的公式得出(fPCLK是PCLK的頻率)。
LPC2368中I2C總線操作
在1.1.4中我們已經(jīng)講過了對I2C總線的操作,但那只是從協(xié)議和時(shí)序上的描述,那我們?nèi)绾螐能浖先ンw現(xiàn)出來呢?接下來我們就討論這個(gè)問題。
對I2C總線上主從設(shè)備的讀寫可使用兩種方法,一是使用輪詢的方式,二是使用中斷的方式。輪詢方式即是在一個(gè)循環(huán)中判斷I2C狀態(tài)寄存器當(dāng)前的狀態(tài)值來確定總線當(dāng)前所處的狀態(tài),然后根據(jù)這個(gè)狀態(tài)來進(jìn)行下一步的操作。中斷方式即是使能I2C中斷,注冊I2C中斷服務(wù)程序,在服務(wù)程序中讀取I2C狀態(tài)寄存器的當(dāng)前狀態(tài)值,再根據(jù)狀態(tài)值來確定下一步的操作。
不管使用哪種方法,看來I2C狀態(tài)寄存器的值是至關(guān)重要的。這些狀態(tài)值代表什么意思呢?下面我們描述一些常用的狀態(tài)值(詳細(xì)的狀態(tài)值含義請參考數(shù)據(jù)手冊)。
0x08: 表明主設(shè)備向總線已發(fā)出了一個(gè)起始條件;
0x10: 表明主設(shè)備向總線已發(fā)出了一個(gè)重復(fù)的起始條件;
0x18: 表明主設(shè)備向總線已發(fā)送了一個(gè)從設(shè)備地址(寫方向)并且接收到從設(shè)備的應(yīng)答;
0x20: 表明主設(shè)備向總線已發(fā)送了一個(gè)從設(shè)備地址(寫方向)并且接收到從設(shè)備的非應(yīng)答;
0x28: 表明主設(shè)備向總線已發(fā)送了一個(gè)數(shù)據(jù)字節(jié)并且接收到從設(shè)備的應(yīng)答;
0x30: 表明主設(shè)備向總線已發(fā)送了一個(gè)數(shù)據(jù)字節(jié)并且接收到從設(shè)備的非應(yīng)答;
0x40: 表明主設(shè)備向總線已發(fā)送了一個(gè)從設(shè)備地址(讀方向)并且接收到從設(shè)備的應(yīng)答;
0x48: 表明主設(shè)備向總線已發(fā)送了一個(gè)從設(shè)備地址(讀方向)并且接收到從設(shè)備的非應(yīng)答;
0x50: 表明主設(shè)備從總線上已接收一個(gè)數(shù)據(jù)字節(jié)并且返回了應(yīng)答;
0x58: 表明主設(shè)備從總線上已接收一個(gè)數(shù)據(jù)字節(jié)并且返回了非應(yīng)答;
四、 總線信號時(shí)序分析
1. 總線空閑狀態(tài)
SDA和SCL兩條信號線都處于高電平,即總線上所有的器件都釋放總線,兩條信號線各自的上拉電阻把電平拉高;
2. 啟動信號START
時(shí)鐘信號SCL保持高電平,數(shù)據(jù)信號SDA的電平被拉低(即負(fù)跳變)。啟動信號必須是跳變信號,而且在建立該信號前必修保證總線處于空閑狀態(tài);
3. 停止信號STOP
時(shí)鐘信號SCL保持高電平,數(shù)據(jù)線被釋放,使得SDA返回高電平(即正跳變),停止信號也必須是跳變信號。
4. 數(shù)據(jù)傳送
SCL線呈現(xiàn)高電平期間,SDA線上的電平必須保持穩(wěn)定,低電平表示0(此時(shí)的線電壓為地電壓),高電平表示1(此時(shí)的電壓由元器件的VDD決定)。只有在SCL線為低電平期間,SDA上的電平允許變化。
5. 應(yīng)答信號ACK
I2C總線的數(shù)據(jù)都是以字節(jié)(8位)的方式傳送的,發(fā)送器件每發(fā)送一個(gè)字節(jié)之后,在時(shí)鐘的第9個(gè)脈沖期間釋放數(shù)據(jù)總線,由接收器發(fā)送一個(gè)ACK(把數(shù)據(jù)總線的電平拉低)來表示數(shù)據(jù)成功接收。
6. 無應(yīng)答信號NACK
在時(shí)鐘的第9個(gè)脈沖期間發(fā)送器釋放數(shù)據(jù)總線,接收器不拉低數(shù)據(jù)總線表示一個(gè)NACK,NACK有兩種用途:
a. 一般表示接收器未成功接收數(shù)據(jù)字節(jié);
b. 當(dāng)接收器是主控器時(shí),它收到最后一個(gè)字節(jié)后,應(yīng)發(fā)送一個(gè)NACK信號,以通知被控發(fā)送器結(jié)束數(shù)據(jù)發(fā)送,并釋放總線,以便主控接收器發(fā)送一個(gè)停止信號STOP。
五、 尋址約定
地址的分配方法有兩種:
1. 含CPU的智能器件,地址由軟件初始化時(shí)定義,但不能與其它的器件有沖突;
2. 不含CPU的非智能器件,由廠家在器件內(nèi)部固化,不可改變。
高7位為地址碼,其分為兩部分:
1. 高4位屬于固定地址不可改變,由廠家固化的統(tǒng)一地址;
2. 低三位為引腳設(shè)定地址,可以由外部引腳來設(shè)定(并非所有器件都可以設(shè)定);
主控器向被控器發(fā)送的信息種類有:啟動信號、停止信號、7位地址碼、讀/寫控制位、10位地址碼、數(shù)據(jù)字節(jié)、重啟動信號、應(yīng)答信號、時(shí)鐘脈沖。
被控器向主控器發(fā)送的信息種類有:應(yīng)答信號、數(shù)據(jù)字節(jié)、時(shí)鐘低電平。
下面對I2C總線通信過程中出現(xiàn)的幾種信號狀態(tài)和時(shí)序進(jìn)行分析。
①總線空閑狀態(tài)。
I2C總線總線的SDA和SCL兩條信號線同時(shí)處于高電平時(shí),規(guī)定為總線的空閑狀態(tài)。此時(shí)各個(gè)器件的輸出級場效應(yīng)管均處在截止?fàn)顟B(tài),即釋放總線,由兩條信號線各自的上拉電阻把電平拉高。
?、趩有盘?。
在時(shí)鐘線SCL保持高電平期間,數(shù)據(jù)線SDA上的電平被拉低(即負(fù)跳變),定義為I2C總線總線的啟動信號,它標(biāo)志著一次數(shù)據(jù)傳輸?shù)拈_始。
啟動信號是一種電平跳變時(shí)序信號,而不是一個(gè)電平信號。啟動信號是由主控器主動建立的,在建立該信號之前I2C總線必須處于空閑狀態(tài),如圖1所示。
圖1 I2C總線上的啟動信號和停止信號
?、弁V剐盘?。
在時(shí)鐘線SCL保持高電平期間,數(shù)據(jù)線SDA被釋放,使得SDA返回高電平(即正跳變),稱為I2C總線的停止信號,它標(biāo)志著一次數(shù)據(jù)傳輸?shù)慕K止。
停止信號也是一種電平跳變時(shí)序信號,而不是一個(gè)電平信號,停止信號也是由主控器主動建立的,建立該信號之后,I2C總線將返回空閑狀態(tài)。
?、軘?shù)據(jù)位傳送。
在I2C總線上傳送的每一位數(shù)據(jù)都有一個(gè)時(shí)鐘脈沖相對應(yīng)(或同步控制),即在SCL串行時(shí)鐘的配合下,在SDA上逐位地串行傳送每一位數(shù)據(jù)。
進(jìn)行數(shù)據(jù)傳送時(shí),在SCL呈現(xiàn)高電平期間,SDA上的電平必須保持穩(wěn)定,低電平為數(shù)據(jù)0,高電平為數(shù)據(jù)1。
只有在SCL為低電平期間,才允許SDA上的電平改變狀態(tài)。邏輯0的電平為低電壓,而邏輯1的電平取決于器件本身的正電源電壓VDD(當(dāng)使用獨(dú)立電源時(shí)),如圖2所示。
圖2 I2C總線上的數(shù)據(jù)位傳送
?、輵?yīng)答信號。
I2C總線上的所有數(shù)據(jù)都是以8位字節(jié)傳送的,發(fā)送器每發(fā)送一個(gè)字節(jié),就在時(shí)鐘脈沖9期間釋放數(shù)據(jù)線,由接收器反饋一個(gè)應(yīng)答信號。
應(yīng)答信號為低電平時(shí),規(guī)定為有效應(yīng)答位(ACK簡稱應(yīng)答位),表示接收器已經(jīng)成功地接收了該字節(jié);應(yīng)答信號為高電平時(shí),規(guī)定為非應(yīng)答位(NACK),一般表示接收器接收該字節(jié)沒有成功。
對于反饋有效應(yīng)答位ACK的要求是,接收器在第9個(gè)時(shí)鐘脈沖之前的低電平期間將SDA線拉低,并且確保在該時(shí)鐘的高電平期間為穩(wěn)定的低電平。
如果接收器是主控器,則在它收到最后一個(gè)字節(jié)后,發(fā)送一個(gè)NACK信號,以通知被控發(fā)送器結(jié)束數(shù)據(jù)發(fā)送,并釋放SDA線,以便主控接收器發(fā)送一個(gè)停止信號P,如圖3所示。
圖3 I2C總線上的應(yīng)答時(shí)序
?、薏迦氲却龝r(shí)間。
如果被控器需要延遲下一個(gè)數(shù)據(jù)字節(jié)開始傳送的時(shí)間,則可以通過把時(shí)鐘線SCL電平拉低并且保持,使主控器進(jìn)入等待狀態(tài)。
一旦被控器釋放時(shí)鐘線,數(shù)據(jù)傳輸就得以繼續(xù)下去,這樣就使得被控器得到足夠時(shí)間轉(zhuǎn)移已經(jīng)收到的數(shù)據(jù)字節(jié),或者準(zhǔn)備好即將發(fā)送的數(shù)據(jù)字節(jié)。
帶有CPU的被控器在對收到的地址字節(jié)做出應(yīng)答之后,需要一定的時(shí)間去執(zhí)行中斷服務(wù)子程序,來分析或比較地址碼,其間就把SCL線鉗位在低電平上,直到處理妥當(dāng)后才釋放SCL線,進(jìn)而使主控器繼續(xù)后續(xù)數(shù)據(jù)字節(jié)的發(fā)送,如圖4所示。
圖4 I2C總線上的插入等待時(shí)間
?、咧貑有盘?。
在主控器控制總線期間完成了一次數(shù)據(jù)通信(發(fā)送或接收)之后,如果想繼續(xù)占用總線再進(jìn)行一次數(shù)據(jù)通信(發(fā)送或接收),而又不釋放總線,就需要利用重啟動Sr信號時(shí)序。
重啟動信號Sr既作為前一次數(shù)據(jù)傳輸?shù)慕Y(jié)束,又作為后一次數(shù)據(jù)傳輸?shù)拈_始。利用重啟動信號的優(yōu)點(diǎn)是,在前后兩次通信之間主控器不需要釋放總線,這樣就不會丟失總線的控制權(quán),即不讓其他主器件節(jié)點(diǎn)搶占總線。
?、鄷r(shí)鐘同步。
如果在某一I2C總線系統(tǒng)中存在兩個(gè)主器件節(jié)點(diǎn),分別記為主器件1和主器件2,其時(shí)鐘輸出端分別為CLK1和CL【0,它們都有控制總線的能力。
假設(shè)在某一期間兩者相繼向SCL線發(fā)出了波形不同的時(shí)鐘脈沖序列CLK1和CLK2(時(shí)鐘脈沖的高、低電平寬度都是依靠各自內(nèi)部專用計(jì)數(shù)器定時(shí)產(chǎn)生的),在總線控制權(quán)還沒有裁定之前這種現(xiàn)象是可能出現(xiàn)的。
鑒于I2C總線的“線與”特性,使得時(shí)鐘線SCL上得到的時(shí)鐘信號波形,既不像主器件1所期望的CLK1,也不像主器件2所期望的CLK2,而是兩者進(jìn)行邏輯與的結(jié)果。
CLKI和CLK2的合成波形作為共同的同步時(shí)鐘信號,一旦總線控制權(quán)裁定給某一主器件,則總線時(shí)鐘信號將會只由該主器件產(chǎn)生,如圖5所示。
圖5 I2C總線上的時(shí)鐘同步
?、峥偩€沖突和總線仲裁。
假如在某I2C總線系統(tǒng)中存在兩個(gè)主器件節(jié)點(diǎn),分別記為主器件1和主器件2,其數(shù)據(jù)輸出端分別為DATA1和DATA2,它們都有控制總線的能力,這就存在著發(fā)生總線沖突(即寫沖突)的可能性。
假設(shè)在某一瞬間兩者相繼向總線發(fā)出了啟動信號,鑒于:I2C總線的“線與”特性,使得在數(shù)據(jù)線SDA上得到的信號波形是DATA1和DATA2兩者相與的結(jié)果,該結(jié)果略微超前送出低電平的主器件1,其DATA1的下降沿被當(dāng)做SDA的下降沿。
在總線被啟動后,主器件1企圖發(fā)送數(shù)據(jù)“101……”,主器件2企圖發(fā)送數(shù)據(jù)“100101……”。
兩個(gè)主器件在每次發(fā)出一個(gè)數(shù)據(jù)位的同時(shí)都要對自己輸出端的信號電平進(jìn)行抽檢,只要抽檢的結(jié)果與它們自己預(yù)期的電平相符,就會繼續(xù)占用總線,總線控制權(quán)也就得不到裁定結(jié)果。
主器件1的第3位期望發(fā)送“1”,也就是在第3個(gè)時(shí)鐘周期內(nèi)送出高電平。
在該時(shí)鐘周期的高電平期間,主器件1進(jìn)行例行抽檢時(shí),結(jié)果檢測到一個(gè)不相匹配的電平“0”,這時(shí)主器件1只好決定放棄總線控制杈;因此,主器件2就成了總線的惟一主宰者,總線控制權(quán)也就最終得出了裁定結(jié)果,從而實(shí)現(xiàn)了總線仲裁的功能。
從以上總線仲裁的完成過程可以得出:仲裁過程主器件1和主器件2都不會丟失數(shù)據(jù);各個(gè)主器件沒有優(yōu)先級別之分,總線控制權(quán)是隨機(jī)裁定的,即使是搶先發(fā)送啟動信號的主器件1最終也并沒有得到控制杈。
系統(tǒng)實(shí)際上遵循的是“低電平優(yōu)先”的仲裁原則,將總線判給在數(shù)據(jù)線上先發(fā)送低電平的主器件,而其他發(fā)送高電平的主器件將失去總線控制權(quán),如圖6所示。
圖6 I2C總線上的總線仲裁
?、饪偩€封鎖狀態(tài)。
在特殊情況下,如果需要禁止所有發(fā)生在I2C總線上的通信活動,封鎖或關(guān)閉總線是一種可行途徑,只要掛接于該總線上的任意一個(gè)器件將時(shí)鐘線SCL鎖定在低電平上即可。
I2C總線是由Philips公司開發(fā)的兩線式串行總線,用于連接微控制器和外圍設(shè)備。
I2C總線支持多主控模式,任何能夠進(jìn)行發(fā)送和接收的設(shè)備都可以成為主設(shè)備。主控能夠控制數(shù)據(jù)的傳輸和時(shí)鐘頻率,在任意的時(shí)刻只能有一個(gè)主控。
組成I2C總線的兩個(gè)信號為數(shù)據(jù)線SDA和時(shí)鐘線SCL。為避免總信號線的混亂,要求各設(shè)備連接到總線的輸出端必須是開漏輸出或集電極開路輸出的結(jié)構(gòu)。根據(jù)這種結(jié)構(gòu)的“線與”邏輯,I2C總線上任意器件輸出低電平都會使相應(yīng)總線上的信號線變低。
總線空閑時(shí),上拉電阻使SDA和SCL線都保持高電平。
數(shù)據(jù)線 SDA 的電平狀態(tài)必須在時(shí)鐘線 SCL 處于高電平期間保持穩(wěn)定不變。SDA 的電平狀態(tài)只有在 SCL 處于低電平期間才允許改變。但是在 I2C總線的起始和結(jié)束時(shí)例外。
當(dāng)SCL穩(wěn)定在高電平時(shí),SDA由高到低的變化將產(chǎn)生一個(gè)開始位,而由低到高的變化則產(chǎn)生一個(gè)停止位。開始位和停止位都是由I2C主設(shè)備產(chǎn)生的。如果從設(shè)備采用7位地址,則主設(shè)備在發(fā)起傳輸前,需先發(fā)送一字節(jié)的地址信息,前7位為設(shè)備地址,最后1位為讀寫標(biāo)志。之后每次傳輸?shù)臄?shù)據(jù)也是一個(gè)字節(jié),從MSB位(Most Significant Bit 最高有效位,對應(yīng)有LSB: Least Significant Bit 最低有效位)開始傳輸。每個(gè)字節(jié)傳完后,在SCL的第9個(gè)上升沿到來之前,接受方應(yīng)該發(fā)出一個(gè)ACK位。
應(yīng)答位的時(shí)鐘脈沖仍由主機(jī)產(chǎn)生,而應(yīng)答位的數(shù)據(jù)狀態(tài)則遵循“誰接收誰產(chǎn)生”的原則,即總是由接收器產(chǎn)生應(yīng)答位。主機(jī)向從機(jī)發(fā)送數(shù)據(jù)時(shí),應(yīng)答位由從機(jī)產(chǎn)生;主機(jī)從從機(jī)接收數(shù)據(jù)時(shí),應(yīng)答位由主機(jī)產(chǎn)生。I2C總線標(biāo)準(zhǔn)規(guī)定:應(yīng)答位為 0 表示接收器應(yīng)答(ACK) ,常常簡記為 A;為 1 則表示非應(yīng)答(NACK) ,常常簡記為NA。發(fā)送器發(fā)送完 LSB 之后,應(yīng)當(dāng)釋放 SDA 線(拉高 SDA,輸出晶體管截止) ,以等待接收器產(chǎn)生應(yīng)答位。
在切換數(shù)據(jù)的傳輸方向時(shí),可以不必先產(chǎn)生停止條件再開始下次傳輸,而是直接再一次產(chǎn)生開始條件。I2C 總線在已經(jīng)處于忙的狀態(tài)下,再一次直接產(chǎn)生起始條件的情況被稱為重復(fù)起始條件。例如:訪問某一具有 I2C總線接口的 E2PROM 存儲器時(shí),主機(jī)先向存儲器輸入存儲單元的地址信息(發(fā)送數(shù)據(jù)) ,然后再讀取其中的存儲內(nèi)容(接收數(shù)據(jù))。
帶有 I2C 總線的器件除了有從機(jī)地址(Slave Address)外,還可能有子地址。從機(jī)地址是指該器件在 I2C 總線上被主機(jī)尋址的地址, 而子地址是指該器件內(nèi)部不同部件或存儲單元的編址。 與從機(jī)地址一樣,子地址實(shí)際上也是像普通數(shù)據(jù)那樣進(jìn)行傳輸?shù)?,傳輸格式仍然是與數(shù)據(jù)相統(tǒng)一的,區(qū)分傳輸?shù)牡降资堑刂愤€是數(shù)據(jù)要靠收發(fā)雙方具體的邏輯約定。子地址的長度必須由整數(shù)個(gè)字節(jié)組成,可能是單字節(jié)(8 位子地址) ,也可能是雙字節(jié)(16 位子地址) ,還可能是 3 字節(jié)以上,這要看具體器件的規(guī)定。
I2C體系結(jié)構(gòu)分3個(gè)部分:I2C核心、I2C總線驅(qū)動、I2C設(shè)備驅(qū)動。
I2C核心提供了I2C總線驅(qū)動和設(shè)備驅(qū)動的注冊、注銷方法,I2C通信方法上層的、與具體適配器無關(guān)的代碼以及探測設(shè)備、檢測設(shè)備地址的上層代碼等。
I2C總線驅(qū)動是對I2C硬件體系結(jié)構(gòu)中適配器端的實(shí)現(xiàn),適配器可由CPU控制,甚至可以直接集成在CPU內(nèi)部。包含了i2c_adapter、i2c_algorithm和控制I2C適配器產(chǎn)生通信信號的函數(shù)。通過I2C總線驅(qū)動的代碼,我們可以控制I2C適配器以主控方式產(chǎn)生開始位、停止位讀寫周期,以及以從設(shè)備方式被讀寫,產(chǎn)生ACK等。
I2C設(shè)備驅(qū)動是對I2C硬件體系結(jié)構(gòu)中設(shè)備端的實(shí)現(xiàn),設(shè)備一般掛接在受CPU控制的I2C適配器上,通過I2C適配器與CPU交換數(shù)據(jù)。包含了i2c_driver和i2c_client。
所有的I2C設(shè)備都在sysfs文件系統(tǒng)中顯示,存于/sys/bus/i2c/目錄下,以適配器地址和芯片地址的形式列出。
i2c_adapter對應(yīng)于物理上的一個(gè)適配器,而i2c_algorithm對應(yīng)一套通信方法。一個(gè)I2C適配器需要i2c_algorithm提供的通信函數(shù)來控制適配器上產(chǎn)生特定的訪問周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的i2c_algorithm的指針。
i2c_driver對應(yīng)一套驅(qū)動方法,是純粹的用于輔助作用的數(shù)據(jù)結(jié)構(gòu),它不對應(yīng)于任何的物理實(shí)體。i2c_client對應(yīng)于真實(shí)的物理設(shè)備,每個(gè)I2C設(shè)備都需要一個(gè)i2c_client來描述。i2c_client一般被包含在I2C字符設(shè)備的私有信息結(jié)構(gòu)體中。
i2c_driver于i2c_client發(fā)生關(guān)聯(lián)的時(shí)刻在i2c_driver的attach_adapter()函數(shù)被運(yùn)行時(shí)。attach_adapter()會探測物理設(shè)備,當(dāng)確定一個(gè)client存在時(shí),把該client使用的i2c_client數(shù)據(jù)結(jié)構(gòu)的adapter指針指向?qū)?yīng)的i2c_adapter,driver指針指向該i2c_driver,并會調(diào)用i2c_adapter的client_register()函數(shù)。相反的過程會發(fā)生在i2c_driver的detach_client()函數(shù)被調(diào)用的時(shí)候。
i2c_transfer()函數(shù)用于進(jìn)行I2C適配器和I2C設(shè)備之間的一組消息交互,i2c_master_send()函數(shù)和i2c_master_recv()函數(shù)內(nèi)部會調(diào)用i2c_transfer()函數(shù)分別完成一條寫消息和一條讀消息。i2c_transfer()函數(shù)本身并不具備驅(qū)動適配器物理硬件完成消息交互的能力,它只是尋找到i2c_adapter對應(yīng)的i2c_algorithm,并使用i2c_algorithm的master_xfer函數(shù)真正驅(qū)動硬件流程。
I2C總線驅(qū)動模塊加載函數(shù)需完成兩個(gè)工作:
1.初始化I2C適配器所使用的硬件資源,如申請I/O地址、中斷號等。
2.通過i2c_add_adapter()添加i2c_adapter的數(shù)據(jù)結(jié)構(gòu),當(dāng)然這個(gè)i2c_adapter數(shù)據(jù)結(jié)構(gòu)的成員已經(jīng)被xxx適配器的相應(yīng)函數(shù)指針?biāo)跏蓟?/p>
I2C總線驅(qū)動模塊的卸載函數(shù)要完成的工作與加載函數(shù)相反。
I2C總線通信方法:我們要為特定的I2C適配器實(shí)現(xiàn)其通信方法,主要實(shí)現(xiàn)i2c_algorithm的master_xfer()函數(shù)和functionality()函數(shù)。functionality()函數(shù)用于返回algorithm所支持的通信協(xié)議,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。master_xfer()函數(shù)在I2C適配器上完成傳遞給它的i2c_msg數(shù)組中的每個(gè)I2C消息。
master_xfer()函數(shù)處理I2C消息數(shù)組,對于數(shù)組中的每個(gè)消息,判斷消息類型,若為讀消息,則賦從設(shè)備地址為(msg-》addr《《1)| 1 ,否則為msg-》addr《《1(賦地址為setaddr)。對每個(gè)消息產(chǎn)生一個(gè)開始位,緊接著傳送從設(shè)備地址,然后開始數(shù)據(jù)的發(fā)送或接收,對最后的消息還需產(chǎn)生一個(gè)停止位。
I2C設(shè)備驅(qū)動(i2c_driver和i2c_client)。
I2C設(shè)備驅(qū)動要使用i2c_driver和i2c_client數(shù)據(jù)結(jié)構(gòu)并填充其中的成員函數(shù)。i2c_client一般被包含在設(shè)備的私有信息結(jié)構(gòu)體yyy_data中,而i2c_driver則適合被定義成全局變量并初始化。
I2C設(shè)備驅(qū)動的模塊加載函數(shù)中會做:
?。?)通過register_chrdev()函數(shù)將I2C設(shè)備注冊為一個(gè)字符設(shè)備。
(2)通過I2C核心的i2c_add_driver()函數(shù)添加i2c_driver。
模板卸載函數(shù)中則相反。
yyy_init()-》i2c_add_driver()-》yyy_attach_adapter()-》i2c_probe()-》yyy_detect()-》i2c_client初始化-》i2c_attach_client()-》yyy_init_client
yyy_exit()-》i2c_del_driver()-》yyy_detach_client()-》i2c_detach_client()
作為一種字符類設(shè)備,Linux I2C設(shè)備驅(qū)動的文件操作接口與普通的設(shè)備驅(qū)動是完全一致的。I2C設(shè)備的寫操作經(jīng)歷了如下幾個(gè)步驟:
?。?)從用戶空間到字符設(shè)備驅(qū)動寫函數(shù)接口,寫函數(shù)構(gòu)造I2C消息數(shù)組。
?。?)寫函數(shù)把構(gòu)造的I2C消息數(shù)組傳遞給I2C核心的傳輸函數(shù)i2c_transfer()。
?。?)I2C核心的傳輸函數(shù)i2c_transfer()找到對應(yīng)適配器algorithm的通信方法函數(shù)master_xfer()去最終完成I2C消息的處理。
read()/write()(用戶空間)-》yyy_read()/yyy_write()(I2C設(shè)備驅(qū)動文件操作接口)-》i2c_transfer()(I2C核心)-》master_xfer()(I2C總線驅(qū)動)
i2c-dev.c文件完全可以被看做一個(gè)I2C設(shè)備驅(qū)動,不過,它實(shí)現(xiàn)的一個(gè)i2c_client是虛擬、臨時(shí)的,隨著設(shè)備文件的打開而產(chǎn)生,并隨設(shè)備文件的關(guān)閉而撤銷,并沒有被添加到i2c_adapter的clients鏈表中。i2c-dev.c針對每個(gè)I2C適配器生成一個(gè)主設(shè)備號為89的設(shè)備文件,實(shí)現(xiàn)了i2c_driver的成員函數(shù)以及文件操作接口,所以,i2c-dev.c的主體式“i2c_driver成員函數(shù)+字符設(shè)備驅(qū)動”。
i2c-dev.c中提供i2cdev_read()、i2cdev_write()函數(shù)來對應(yīng)用戶空間要使用的read()和write()文件操作接口,這兩個(gè)函數(shù)分別調(diào)用I2C核心的i2c_master_recv()和i2c_master_send()函數(shù)來構(gòu)造一條I2C消息并引發(fā)適配器algorithm通信函數(shù)的調(diào)用,完成消息的傳輸。i2cdev_read()和i2cdev_write()函數(shù)不具備太強(qiáng)的通用性,滅有太大的使用價(jià)值,只能適用于非RepStart模式的情況。對于兩條以上消息組成的讀寫,在用戶空間需要組織i2c_msg消息數(shù)組并調(diào)用I2C_RDWR IOCTL命令。
評論