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