1.1?I2C總線知識(shí)
1.1.1? I2C總線物理拓?fù)浣Y(jié)構(gòu)
?
????I2C總線在物理連接上非常簡(jiǎn)單,分別由SDA(串行數(shù)據(jù)線)和SCL(串行時(shí)鐘線)及上拉電阻組成。通信原理是通過對(duì)SCL和SDA線高低電平時(shí)序的控制,來(lái)產(chǎn)生I2C總線協(xié)議所需要的信號(hào)進(jìn)行數(shù)據(jù)的傳遞。在總線空閑狀態(tài)時(shí),這兩根線一般被上面所接的上拉電阻拉高,保持著高電平。
1.1.2? I2C總線特征
??? I2C總線上的每一個(gè)設(shè)備都可以作為主設(shè)備或者從設(shè)備,而且每一個(gè)設(shè)備都會(huì)對(duì)應(yīng)一個(gè)唯一的地址(可以從I2C器件的數(shù)據(jù)手冊(cè)得知),主從設(shè)備之間就通過這個(gè)地址來(lái)確定與哪個(gè)器件進(jìn)行通信,在通常的應(yīng)用中,我們把CPU帶I2C總線接口的模塊作為主設(shè)備,把掛接在總線上的其他設(shè)備都作為從設(shè)備。
??? I2C總線上可掛接的設(shè)備數(shù)量受總線的最大電容400pF 限制,如果所掛接的是相同型號(hào)的器件,則還受器件地址位的限制。
??? I2C總線數(shù)據(jù)傳輸速率在標(biāo)準(zhǔn)模式下可達(dá)100kbit/s,快速模式下可達(dá)400kbit/s,高速模式下可達(dá)3.4Mbit/s。一般通過I2C總線接口可編程時(shí)鐘來(lái)實(shí)現(xiàn)傳輸速率的調(diào)整,同時(shí)也跟所接的上拉電阻的阻值有關(guān)。
??? I2C總線上的主設(shè)備與從設(shè)備之間以字節(jié)(8位)為單位進(jìn)行雙向的數(shù)據(jù)傳輸。
1.1.3? I2C總線協(xié)議
??? I2C協(xié)議規(guī)定,總線上數(shù)據(jù)的傳輸必須以一個(gè)起始信號(hào)作為開始條件,以一個(gè)結(jié)束信號(hào)作為傳輸?shù)耐V箺l件。起始和結(jié)束信號(hào)總是由主設(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器件無(wú)法訪問總線;而在停止條件產(chǎn)生后,本次數(shù)據(jù)傳輸?shù)闹鲝脑O(shè)備將釋放總線,總線再次處于空閑狀態(tài)。如圖所示:
在了解起始條件和停止條件后,我們?cè)賮?lá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è)備都對(duì)應(yīng)一個(gè)唯一的地址,主從設(shè)備之間的數(shù)據(jù)傳輸是建立在地址的基礎(chǔ)上,也就是說(shuō),主設(shè)備在傳輸有效數(shù)據(jù)之前要先指定從設(shè)備的地址,地址指定的過程和上面數(shù)據(jù)傳輸?shù)倪^程一樣,只不過大多數(shù)從設(shè)備的地址是7位的,然后協(xié)議規(guī)定再給地址添加一個(gè)最低位用來(lái)表示接下來(lái)數(shù)據(jù)傳輸?shù)姆较颍?表示主設(shè)備向從設(shè)備寫數(shù)據(jù),1表示主設(shè)備向從設(shè)備讀數(shù)據(jù)。如圖所示:
?
1.1.4? I2C總線操作
??? 對(duì)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ù)手冊(cè)上對(duì)其地址的描述如下:
????
??? 由此,其地址跟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腳來(lái)決定使用I2C還是SPI接口,我們這里要求使用I2C接口,因此將第8腳接到高電平)。根據(jù)數(shù)據(jù)手冊(cè),我們同樣的可以知道地址跟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存儲(chǔ)器件(注意是8Kbit,也就是1KB) ,根據(jù)數(shù)據(jù)手冊(cè)中器件地址部分的描述,AT24C08的地址是:1010+A2A1A0+方向位,其中1010是EEPROM的類型識(shí)別符;僅僅使用A2來(lái)確定總線訪問本器件的從設(shè)備地址,這里接的低電平,所以為0;A1和A0是器件內(nèi)部頁(yè)地址,在對(duì)器件擦除或者編程時(shí)使用,雖然這里也接的低電平,但器件內(nèi)部并不使用引腳的輸入值,也就是說(shuō)A1和A0的值是由軟件進(jìn)行設(shè)定的。
1.3?脫離操作系統(tǒng)的I2C總線驅(qū)動(dòng)示例(以電路示例一為例)
1.3.1?LPC2368中I2C接口寄存器描述
??? LPC2368中有三個(gè)I2C總線接口,分別表示為I2C0、I2C1和I2C2,每個(gè)I2C接口都包含7個(gè)寄存器。它們分別是:
I2C控制置位寄存器(I2CONSET):8位寄存器,各位不同的設(shè)置是對(duì)I2C總線不同的控制。
位
符號(hào)
描述
復(fù)位值
1:0
-
保留,用戶軟件不要向其寫入1。從保留位讀出的值未被定義
NA
2
AA
聲明應(yīng)答標(biāo)志。為1時(shí)將為需要應(yīng)答的情況產(chǎn)生一個(gè)應(yīng)答
0
3
SI
I2C中斷標(biāo)志。當(dāng)I2C狀態(tài)改變時(shí)該位置位
0
4
STO
總線停止條件控制。1發(fā)出一個(gè)停止條件,當(dāng)總線檢測(cè)到停止條件時(shí),STO自動(dòng)清零
0
5
STA
總線起始條件控制。1進(jìn)入主模式并發(fā)出一個(gè)起始條件
0
6
I2EN
總線使能控制。1為使能
0
7
-
保留,用戶軟件不要向其寫入1。從保留位讀出的值未被定義
NA
I2C控制清零寄存器(I2CONCLR):8位寄存器,對(duì)I2CONSET寄存器中的相應(yīng)為清零。
位
符號(hào)
描述
復(fù)位值
1:0
-
保留,用戶軟件不要向其寫入1。從保留位讀出的值未被定義
NA
2
AAC
聲明應(yīng)答標(biāo)志清零位。向該位寫入1清零I2CONSET寄存器中的AA位
0
3
SIC
中斷標(biāo)志清零位。向該位寫入1清零I2CONSET寄存器中的SI位
0
4
-
保留,用戶軟件不要向其寫入1。從保留位讀出的值未被定義
NA
5
STAC
起始條件清零位。向該位寫入1清零I2CONSET寄存器中的STA位
0
6
I2ENC
總線禁能控制。寫入1清零I2CONSET寄存器中的I2EN位
0
7
-
保留,用戶軟件不要向其寫入1。從保留位讀出的值未被定義
NA
I2C狀態(tài)寄存器(I2STAT):8位只讀寄存器,用于監(jiān)控總線的實(shí)時(shí)狀態(tài)(可能存在26種狀態(tài))。
位
符號(hào)
描述
復(fù)位值
2:0
-
這3個(gè)位不使用且總是為0
0
7:3
Status
這些位給出I2C接口的實(shí)時(shí)狀態(tài),不同的值代表不同的狀態(tài),狀態(tài)碼請(qǐng)參考數(shù)據(jù)手冊(cè)
0x1F
I2C數(shù)據(jù)寄存器(I2DAT):8位寄存器,在SI置位期間,I2DAT中的數(shù)據(jù)保持穩(wěn)定。
位
符號(hào)
描述
復(fù)位值
7:0
Data
該寄存器保留已經(jīng)接收到或者準(zhǔn)備要發(fā)送的數(shù)據(jù)值
0
I2C從地址寄存器(I2ADR):8位寄存器,I2C總線為從模式時(shí)才使用。主模式中該寄存器無(wú)效。
位
符號(hào)
描述
復(fù)位值
0
GC
通用調(diào)用使能位
0
7:1
Address
從模式的I2C器件地址
0x00
SCH占空比寄存器(I2SCLH):16位寄存器,用于定義SCL高電平所保持的PCLK周期數(shù)。
位
符號(hào)
描述
復(fù)位值
15:0
SCLH
SCL高電平周期選擇計(jì)數(shù)
0x0004
SCL占空比寄存器(I2SCLL):16位寄存器,用于定義SCL低電平所保持的PCLK周期數(shù)。
位
符號(hào)
描述
復(fù)位值
15:0
SCLL
SCL低電平周期選擇計(jì)數(shù)
0x0004
在前面的I2C總線特征中我們提到過,I2C總線的速率通過可編程時(shí)鐘來(lái)調(diào)整,即必須通過軟件對(duì)I2SCLH和I2SCLL寄存器進(jìn)行設(shè)置來(lái)選擇合適的數(shù)據(jù)頻率和占空比。 頻率由下面的公式得出(fPCLK是PCLK的頻率)。
?
1.3.2?LPC2368中I2C總線操作
??? 在1.1.4中我們已經(jīng)講過了對(duì)I2C總線的操作,但那只是從協(xié)議和時(shí)序上的描述,那我們?nèi)绾螐能浖先ンw現(xiàn)出來(lái)呢?接下來(lái)我們就討論這個(gè)問題。
??? 對(duì)I2C總線上主從設(shè)備的讀寫可使用兩種方法,一是使用輪詢的方式,二是使用中斷的方式。輪詢方式即是在一個(gè)循環(huán)中判斷I2C狀態(tài)寄存器當(dāng)前的狀態(tài)值來(lái)確定總線當(dāng)前所處的狀態(tài),然后根據(jù)這個(gè)狀態(tài)來(lái)進(jìn)行下一步的操作。中斷方式即是使能I2C中斷,注冊(cè)I2C中斷服務(wù)程序,在服務(wù)程序中讀取I2C狀態(tài)寄存器的當(dāng)前狀態(tài)值,再根據(jù)狀態(tài)值來(lái)確定下一步的操作。
??? 不管使用哪種方法,看來(lái)I2C狀態(tài)寄存器的值是至關(guān)重要的。這些狀態(tài)值代表什么意思呢?下面我們描述一些常用的狀態(tài)值(詳細(xì)的狀態(tài)值含義請(qǐng)參考數(shù)據(jù)手冊(cè))。
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)答;
1.3.3?示例代碼
一、?輪詢方式讀寫總線:
對(duì)于代碼中從設(shè)備內(nèi)部寄存器的操作請(qǐng)參考該設(shè)備的數(shù)據(jù)手冊(cè)。例如,要讀取溫度傳感器的溫度值只需要調(diào)用:I2C0_ReadRegister(CHANNEL_TEMPERATURE, ADT75A_TEMP, &value),如果讀取成功,則value中的數(shù)據(jù)就是通過I2C總線讀取溫度傳感器中的溫度數(shù)據(jù)。
二、?中斷方式讀寫總線:
??? 這里的從設(shè)備地址定義、I2C控制寄存器宏定義和I2C初始化與上面輪詢中的類似,只是要在初始化函數(shù)中加上中斷申請(qǐng)的代碼,中斷服務(wù)程序名稱為:I2C0_Exception。這里不再貼出以上代碼了,這里只貼出關(guān)鍵性的代碼。
/*定義I2C狀態(tài)標(biāo)志*/
typedef enum
{
????I2C_IDLE = 0,
????I2C_STARTED = 1,
????I2C_RESTARTED = 2,
????I2C_REPEATED_START = 3,
????I2C_DATA_ACK = 4,
????I2C_DATA_NACK = 5
} I2C_STATUS_FLAG;
/*定義I2C數(shù)據(jù)傳輸緩沖區(qū)大小和傳輸超時(shí)大小*/
#define I2C_BUFSIZE 0x200
#define I2C_TIMEOUT 0x00FFFFFF
/*定義I2C當(dāng)前狀態(tài)標(biāo)志*/
volatile I2C_STATUS_FLAG I2C_Flag;
/*I2C當(dāng)前的模式,0為主發(fā)送器模式,1為主接收器模式*/
volatile uint32 I2CMasterMode = 0;
/*分別定義I2C接收和發(fā)送緩沖區(qū)、要發(fā)送或要接收的字節(jié)數(shù)、實(shí)際發(fā)送或接收的字節(jié)數(shù)*/
volatile uint8 I2CReadBuf[I2C_BUFSIZE], I2CWriteBuf[I2C_BUFSIZE];
volatile uint32 I2CReadLength, I2CWriteLength;
volatile uint32 I2C_RD_Index, I2C_WR_Index;
/****************************************************************************
** Function name: I2C0_Exception
** Descriptions : I2C0中斷服務(wù)程序
** Input : 無(wú)
** Output : 無(wú)
** Created Date : 2011-03-24
*****************************************************************************/
void I2C0_Exception(void)
{
????volatile uint32 stat_value;
????stat_value = I20STAT;
????switch(stat_value)
????{
????????case 0x08:
????????????/*發(fā)出了一個(gè)起始條件,接下來(lái)將發(fā)送從地址然后清零SI位和STA位*/
????????????I2C_Flag = I2C_STARTED;
????????????I20DAT = I2CWriteBuf[I2C_WR_Index];
????????????I2C_WR_Index++;
????????????I20CONCLR = I2C_STA | I2C_SI;
????????????break;
????????case 0x10:
????????????/*一個(gè)重復(fù)的起始條件發(fā)送完成,接下來(lái)要將發(fā)送從地址然后清零SI位和STA位*/
????????????I2C_Flag = I2C_RESTARTED;
????????????if(I2CMasterMode == 1)
????????????{
????????????????/*注意I2CWriteBuf中的第0位是設(shè)備從地址和寫方向位,因這里是讀操作,故將第0位的方向位變?yōu)樽x*/
????????????????I20DAT = I2CWriteBuf[0] | 0x01;
????????????}
????????????I20CONCLR = I2C_STA | I2C_SI;
????????????break;
????????case 0x18 /*(注:SLA+W表示從設(shè)備地址+寫方向)*/
????????????/*發(fā)送SLA+W后已接收到ACK,接下來(lái)開始發(fā)送數(shù)據(jù)字節(jié)到數(shù)據(jù)寄存器然后清零SI位*/
????????????if(I2C_Flag == I2C_STARTED)
????????????{
????????????????I2C_Flag = I2C_DATA_ACK;
????????????????I20DAT = I2CWriteBuf[I2C_WR_Index];
????????????????I2C_WR_Index++;
????????????}
????????????I20CONCLR = I2C_SI;
????????????break;
????????case 0x28:
????????????/*此狀態(tài)表明已發(fā)送I2DAT中的字節(jié)且接收到ACK,接下來(lái)繼續(xù)發(fā)送下一個(gè)字節(jié)*/
????????case 0x30:
????????????/*已發(fā)送I2DAT中的字節(jié)且接收到非ACK,接下來(lái)可能發(fā)出停止條件或重啟起始條件*/
????????????if(I2C_WR_Index != I2CWriteLength)
????????????{
????????????????/*實(shí)際發(fā)送的字節(jié)數(shù)與要發(fā)送的不相等則繼續(xù)發(fā)送,但可能是最后一次*/
????????????????I20DAT = I2CWriteBuf[I2C_WR_Index];
????????????????I2C_WR_Index++;
????????????????if(I2C_WR_Index != I2CWriteLength)
????????????????{
????????????????????I2C_Flag = I2C_DATA_ACK;
????????????????}
????????????????else
????????????????{
????????????????????/*如果實(shí)際發(fā)送與要發(fā)送的相等了,表明主發(fā)送端數(shù)據(jù)發(fā)送完成*/
????????????????????I2C_Flag = I2C_DATA_NACK;
????????????????????if(I2CReadLength != 0)
????????????????????{
????????????????????????/*如果主發(fā)送端有等待接收的字節(jié),則切換為主接收模式,重啟起始條件*/
????????????????????????I2C_Flag = I2C_REPEATED_START;
????????????????????????I20CONSET = I2C_STA | I2C_SI;
????????????????????}
????????????????}
????????????}
????????????else
????????????{
????????????????/*如果實(shí)際發(fā)送與要發(fā)送的相等了,表明主發(fā)送端數(shù)據(jù)發(fā)送完成*/
????????????????I2C_Flag = I2C_DATA_NACK;
????????????????if(I2CReadLength != 0)
????????????????{
????????????????????/*如果主發(fā)送端有等待接收的字節(jié),則表明需切換為主接收模式,重啟起始條件*/
????????????????????I2C_Flag = I2C_REPEATED_START;
????????????????????I20CONSET = I2C_STA;
????????????????}
????????????}
????????????I20CONCLR = I2C_SI;
????????????break;
????????case 0x40:
????????????/*此狀態(tài)表明已發(fā)送SLA+R后已接收到ACK*/
????????????I20CONCLR = I2C_SI;
????????????break;
????????case 0x50:
????????????/*此狀態(tài)表明已接收數(shù)據(jù)字節(jié)后已接收到ACK*/
????????case 0x58:
????????????/*此狀態(tài)表明已接收數(shù)據(jù)字節(jié)后已接收到非ACK*/
????????????I2CReadBuf[I2C_RD_Index] = I20DAT;
????????????I2C_RD_Index++;
????????????if(I2C_RD_Index != I2CReadLength)
????????????{
????????????????/*如果實(shí)際接收的字節(jié)與要接收的不相等,則繼續(xù)接收*/
????????????????I2C_Flag = I2C_DATA_ACK;
????????????}
????????????else
????????????{
????????????????/*否則接收完畢*/
????????????????I2C_RD_Index = 0;
????????????????I2C_Flag = I2C_DATA_NACK;
????????????}
????????????I20CONCLR = I2C_AA | I2C_SI;
????????????break;
????????case 0x20:
????????????/*此狀態(tài)表明已發(fā)送SLA+W后已接收到非ACK*/
????????case 0x48:
????????????/*此狀態(tài)表明已發(fā)送SLA+R后已接收到非ACK*/
????????????I2C_Flag = I2C_DATA_NACK;
????????????I20CONCLR = I2C_SI;
????????????break;
????????default:
????????????I20CONCLR = I2C_SI;
????????????break;
????}
????VICVectAddr = 0x00;
}
/****************************************************************************
** Function name: I2C0_Start
** Descriptions : 設(shè)置I2C0總線傳輸起始條件
** Input : 無(wú)
** Output : 返回TRUE/FALSE, FALSE為設(shè)置超時(shí)
** Created Date : 2011-03-24
*****************************************************************************/
BOOL I2C0_Start(void)
{
????uint32 timeout = 0;
????BOOL retVal = FALSE;
????/*設(shè)置配置寄存器STA位開始條件*/
????I20CONSET = I2C_STA | I2C_SI;
????I20CONCLR = I2C_SI;
????/*等待起始條件完成*/
????while(1)
????{
????????if(I2C_Flag == I2C_STARTED)
????????{
????????????retVal = TRUE;
????????????break;
????????}
????????if(timeout >= I2C_TIMEOUT)
????????{
????????????retVal = FALSE;
????????????break;
????????}
????????timeout++;
????}
????return retVal;
}
/****************************************************************************
** Function name: I2C0_Stop
** Descriptions : 設(shè)置I2C0總線傳輸停止條件
** Input : 無(wú)
** Output : 返回TRUE
** Created Date : 2011-03-24
*****************************************************************************/
BOOL I2C0_Stop(void)
{
????/*設(shè)置配置寄存器STO位停止條件和清除SI標(biāo)志*/
????I20CONSET = I2C_STO;
????I20CONCLR = I2C_SI;
????/*等待停止條件完成*/
????while(I20CONSET & I2C_STO);
????return TRUE;
}
/****************************************************************************
** Function name: I2C0_Engine
** Descriptions : 完成I2C0總線從開始到停止的傳輸,傳輸過程在中斷服務(wù)程序中進(jìn)行
** Input : 無(wú)
** Output : 返回TRUE/FALSE
** Created Date: 2011-03-24
*****************************************************************************/
BOOL I2C0_Engine(void)
{
????I2C_Flag = I2C_IDLE;
????I2C_RD_Index = 0;
????I2C_WR_Index = 0;
????if(I2C0_Start() != TRUE)
????{
????????I2C0_Stop();
????????return FALSE;
????}
????while(1)
????{
????????if(I2C_Flag == I2C_DATA_NACK)
????????{
????????????I2C0_Stop();
????????????break;
????????}
????}
????return TRUE;
}
??? 從上面代碼中看,如果要使用I2C總線啟動(dòng)一次數(shù)據(jù)傳輸只需要先初始化好發(fā)送或接收緩沖區(qū),然后調(diào)用I2C0_Engine()函數(shù)即可。如下代碼所示:
/****************************************************************************
** Function name: I2C0_ReadWriteTransmission
** Descriptions : I2C總線數(shù)據(jù)讀寫傳輸
** Input : read_buf-讀數(shù)據(jù)緩沖區(qū)
??????????????????read_len-讀數(shù)據(jù)長(zhǎng)度
??????????????????write_buf-寫數(shù)據(jù)緩沖區(qū)
??????????????????write_len-寫數(shù)據(jù)長(zhǎng)度
** Output : 數(shù)據(jù)讀寫傳輸是否成功
** Created Date : 2011-03-24
*****************************************************************************/
BOOL I2C0_ReadWriteTransmission(uint8 **read_buf, uint32 read_len, uint8 *write_buf, uint32 write_len)
{
????uint32 i;
????BOOL result = FALSE;
????/*數(shù)據(jù)傳輸長(zhǎng)度檢查*/
????if(read_len > I2C_BUFSIZE || write_len > I2C_BUFSIZE)
????{
????????return FALSE;
????}
????/*清空I2C接收和發(fā)送緩沖區(qū)內(nèi)容*/
????for(i = 0; i < I2C_BUFSIZE; i++)
????{
????????I2CReadBuf[i] = 0;
????????I2CWriteBuf[i] = 0;
????}
????/*確定I2C總線模式(0為主發(fā)送模式,1為主接收模式)*/
????I2CMasterMode = (read_len == 0)? 0 : 1;
????I2CReadLength = read_len;
????I2CWriteLength = write_len;
????/*要寫入I2C從設(shè)備的數(shù)據(jù)(第一個(gè)字節(jié)包含從設(shè)備地址和方向位)*/
????for(i = 0; i < write_len; i++)
????{
????????I2CWriteBuf[i] = write_buf[i];
????}
????/*啟動(dòng)I2C傳輸*/
????result = I2C0_Engine();
????/*如果有向從設(shè)備讀取的數(shù)據(jù)*/
????if(read_len > 0 && result == TRUE)
????{
????????uint8 *buf = (uint8 *)malloc(read_len * sizeof(uint8));
????????for(i = 0; i < read_len; i++)
????????{
????????????uf[i] = I2CReadBuf[i];
????????}
????????*read_buf = buf;
????}
????return result;
}
1.4?Linux下I2C子系統(tǒng)框架
??? 在Linux下要使用I2C總線并沒有像無(wú)系統(tǒng)中的那樣簡(jiǎn)單,為了體現(xiàn)Linux中的模塊架構(gòu),Linux把I2C總線的使用進(jìn)行了結(jié)構(gòu)化。這種結(jié)構(gòu)分三部分組成,他們分別是:I2C核心部分、I2C總線驅(qū)動(dòng)部分和I2C設(shè)備驅(qū)動(dòng)。結(jié)構(gòu)圖如下:
?????????
?
??? 由此看來(lái),在Linux下驅(qū)動(dòng)I2C總線不像單片機(jī)中那樣簡(jiǎn)單的操作幾個(gè)寄存器了,而是把I2C總線結(jié)構(gòu)化、抽象化了,符合通用性和Linux設(shè)備模型。
/*I2C從設(shè)備地址*/
#define SC16IS740_ADDR 0x92 /*I2C轉(zhuǎn)UART設(shè)備*/
#define ADT75A_ADDR 0x9E /*溫度傳感器設(shè)備*/
#define ADT75A_TEMP 0x00 /*溫度傳感器內(nèi)部寄存器*/
/*從設(shè)備選擇標(biāo)識(shí)*/
#define CHANNEL_GPRS 0
#define CHANNEL_TEMPERATURE 1
/*定義I2C控制寄存器各位操作宏*/
#define BIT(x) (1 << x)
#define I2C_EN BIT(6)
#define I2C_STA BIT(5)
#define I2C_STO BIT(4)
#define I2C_SI BIT(3)
#define I2C_AA BIT(2)
/*用作超時(shí)計(jì)數(shù)*/
#define SAFETY_COUNTER_LIMIT 3000
/******************************************************************
** Function name: I2C0_Init
** Descriptions : I2C0初始化
** Input : 無(wú)
** Output : 無(wú)
** Created Date : 2011-03-24
*******************************************************************/
void I2C0_Init(void)
{
????/*設(shè)置P0.0,P0.1為I2C0接口的SDA和SCL功能*/
????PINSEL0 |= (0x03 << 0) | (0x03 << 2);
????/*設(shè)置I2C0接口功率/時(shí)鐘控制位*/
????PCONP |= (0x01 << 7 );
????/*清空I2C0配置寄存器的各位*/
????I20CONCLR = (0x01 << 2) | (0x01 << 3) | (0x01 << 5) | (0x01 << 6);
????/*使能I2C0為主發(fā)送器模式*/
????I20CONSET = (0x01 << 6);
????/*設(shè)置I2C0總線速率為100 KHz */
????I20SCLH = 0x5A;
????I20SCLL = 0x5A;
}
/****************************************************************************
** Function name: I2C0_ReadRegister
** Descriptions : 從I2C0總線上讀從設(shè)備的數(shù)據(jù)
** Input : 從設(shè)備選擇標(biāo)識(shí)、從設(shè)備內(nèi)部寄存器地址、讀出的字節(jié)數(shù)據(jù)
** Output : 讀取是否成功
** Created Date : 2011-03-28
*****************************************************************************/
BOOL I2C0_ReadRegister(uint32 channel, uint8 registerAddress, uint8 *pData)
{
????/*用作延時(shí)等待計(jì)數(shù)*/
????uint32 loopSafetyCounter = 0;
????uint32 addressSendSafetyCounter = 0;
????/*使用循環(huán)判斷I2C狀態(tài)寄存器I20STAT 的值*/
????do
????{
????????/*向總線發(fā)送I2C起始條件*/
????????I20CONSET = I2C_STA | I2C_SI;
????????I20CONCLR = I2C_SI;
????????/*等待起始條件發(fā)送完成*/
????????loopSafetyCounter = 0;
????????while (~I20CONSET & I2C_SI)
????????{
????????????loopSafetyCounter ++;
????????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????????{
????????????????return FALSE; /*超時(shí)退出*/
????????????}
????????}
????????/*發(fā)送從設(shè)備地址*/
????????if(channel == CHANNEL_GPRS)
????????????I20DAT = SC16IS740_ADDR;
????????else if(channel == CHANNEL_TEMPERATURE)
????????????I20DAT = ADT75A_ADDR;
????????I20CONCLR = I2C_STA | I2C_SI;
????????/*等待從設(shè)備地址發(fā)送完成*/
????????loopSafetyCounter = 0;
????????while (~I20CONSET & I2C_SI)
????????{
????????????loopSafetyCounter ++;
????????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????????{
????????????????return FALSE; /*超時(shí)退出*/
????????????}
????????}
????????addressSendSafetyCounter ++;
????????if (addressSendSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????} while (I20STAT != 0x18); /*在前面已經(jīng)描述了0x18的含義*/
????/*發(fā)送從設(shè)備內(nèi)部寄存器地址,根據(jù)數(shù)據(jù)手冊(cè)描述該內(nèi)部地址要左移3位*/
????I20DAT = registerAddress << 3;
????I20CONCLR = I2C_SI;
????/*等待從設(shè)備內(nèi)部寄存器地址發(fā)送完成*/
????loopSafetyCounter = 0;
????while (~I20CONSET & I2C_SI)
????{
????????loopSafetyCounter ++;
????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????}
????/*重啟I2C起始條件進(jìn)行總線讀*/
????I20CONSET = I2C_STA | I2C_SI;
????I20CONCLR = I2C_SI;
????/*等待重啟條件發(fā)送完成*/
????loopSafetyCounter = 0;
????while (~I20CONSET & I2C_SI)
????{
????????loopSafetyCounter ++;
????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????}
????/*發(fā)送從設(shè)備地址(方向位為讀,注意與上0x01將地址最低位變?yōu)?即為讀方向)*/
????if(channel == CHANNEL_GPRS)
????????I20DAT = SC16IS740_ADDR | 0x01;
????else if(channel == CHANNEL_TEMPERATURE)
????????I20DAT = ADT75A_ADDR | 0x01;
????I20CONCLR = I2C_STA | I2C_SI;
????/*等待從設(shè)備地址發(fā)送完成*/
????loopSafetyCounter = 0;
????while (~I20CONSET & I2C_SI)
????{
????????loopSafetyCounter ++;
????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????}
????/*開始準(zhǔn)備讀取數(shù)據(jù)*/
????I20CONCLR = I2C_SI | I2C_AA;
????/*等待數(shù)據(jù)接收*/
????loopSafetyCounter = 0;
????while (~I20CONSET & I2C_SI)
????{
????????loopSafetyCounter ++;
????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????}
????/*數(shù)據(jù)接收*/
????*pData = I20DAT;
????/*發(fā)送I2C停止條件*/
????I20CONSET = I2C_STO;
????I20CONCLR = I2C_SI;
????/*等待停止條件發(fā)送完成*/
????loopSafetyCounter = 0;
????while (I20CONSET & I2C_STO)
????{
????????loopSafetyCounter ++;
????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????}
????return TRUE;
}
/****************************************************************************
** Function name: I2C0_WriteRegister
** Descriptions : 從I2C0總線上寫從設(shè)備的數(shù)據(jù)
** Input : 從設(shè)備選擇標(biāo)識(shí)、從設(shè)備內(nèi)部寄存器地址、要寫入的數(shù)據(jù)字節(jié)
** Output : 寫入是否成功
** Created Date : 2011-03-28
*****************************************************************************/
BOOL I2C0_WriteRegister(uint32 channel, uint8 registerAddress, uint8 data)
{
????uint32 loopSafetyCounter = 0;
????uint32 addressSendSafetyCounter = 0;
????/*使用循環(huán)判斷I2C狀態(tài)寄存器I20STAT 的值*/
????do
????{
????????/*向總線發(fā)送I2C起始條件*/
????????I20CONSET = I2C_STA | I2C_SI;
????????I20CONCLR = I2C_SI;
????????/*等待起始條件發(fā)送完成*/
????????loopSafetyCounter = 0;
????????while (~I20CONSET & I2C_SI)
????????{
????????????loopSafetyCounter ++;
????????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????????{
????????????????return FALSE; /*超時(shí)退出*/
????????????}
????????}
????????/*發(fā)送從設(shè)備地址*/
????????if(channel == CHANNEL_GPRS)
????????????I20DAT = SC16IS740_ADDR;
????????else if(channel == CHANNEL_TEMPERATURE)
????????????I20DAT = ADT75A_ADDR;
????????I20CONCLR = I2C_STA | I2C_SI;
????????/*等待從設(shè)備地址發(fā)送完成*/
????????loopSafetyCounter = 0;
????????while (~I20CONSET & I2C_SI)
????????{
????????????loopSafetyCounter ++;
????????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????????{
????????????????return FALSE; /*超時(shí)退出*/
????????????}
????????}
????????addressSendSafetyCounter ++;
????????if (addressSendSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????} while (I20STAT != 0x18);
????/*發(fā)送從設(shè)備內(nèi)部寄存器地址*/
????I20DAT = registerAddress << 3;
????I20CONCLR = I2C_SI;
????/*等待寄存器地址發(fā)送完成*/
????loopSafetyCounter = 0;
????while (~I20CONSET & I2C_SI)
????{
????????loopSafetyCounter ++;
????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????}
????/*開始發(fā)送數(shù)據(jù)*/
????I20DAT = data;
????I20CONCLR = I2C_SI;
????/*等待數(shù)據(jù)發(fā)送完成*/
????loopSafetyCounter = 0;
????while (~I20CONSET & I2C_SI)
????{
????????loopSafetyCounter ++;
????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????}
????/*發(fā)送I2C停止條件*/
????I20CONSET = I2C_STO;
????I20CONCLR = I2C_SI;
????/*等待停止條件發(fā)送完成*/
????loopSafetyCounter = 0;
????while (I20CONSET & I2C_STO)
????{
????????loopSafetyCounter ++;
????????if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
????????{
????????????return FALSE; /*超時(shí)退出*/
????????}
????}
????return TRUE;
}
?
評(píng)論