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