生活随笔
收集整理的這篇文章主要介紹了
I2C总线介绍
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
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í)序的控制,來產(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è)地址來確定與哪個(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í)鐘來實(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器件無法訪問總線;而在停止條件產(chǎn)生后,本次數(shù)據(jù)傳輸?shù)闹鲝脑O(shè)備將釋放總線,總線再次處于空閑狀態(tài)。如圖所示:
??? 在了解起始條件和停止條件后,我們?cè)賮砜纯丛谶@個(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ǔ)上,也就是說,主設(shè)備在傳輸有效數(shù)據(jù)之前要先指定從設(shè)備的地址,地址指定的過程和上面數(shù)據(jù)傳輸?shù)倪^程一樣,只不過大多數(shù)從設(shè)備的地址是7位的,然后協(xié)議規(guī)定再給地址添加一個(gè)最低位用來表示接下來數(shù)據(jù)傳輸?shù)姆较?#xff0c;0表示主設(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?I2C總線硬件接口電路示例
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腳來決定使用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來確定總線訪問本器件的從設(shè)備地址,這里接的低電平,所以為0;A1和A0是器件內(nèi)部頁地址,在對(duì)器件擦除或者編程時(shí)使用,雖然這里也接的低電平,但器件內(nèi)部并不使用引腳的輸入值,也就是說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。從保留位讀出的值未被定義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。從保留位讀出的值未被定義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 3個(gè)位不使用且總是為0 | 0 |
| 7:3 | Status | 這些位給出I2C接口的實(shí)時(shí)狀態(tài),不同的值代表不同的狀態(tài),狀態(tài)碼請(qǐng)參考數(shù)據(jù)手冊(cè)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í)才使用。主模式中該寄存器無效。
| 位 | 符號(hào) | 描述 | 復(fù)位值 |
| 0 | GC | 通用調(diào)用使能位 | 0 |
| 7:1 | Address | 從模式的I2C器件地址I2C器件地址 | 0x00 |
SCH占空比寄存器(I2SCLH): 16位寄存器,用于定義SCL高電平所保持的PCLK周期數(shù)。
| 位 | 符號(hào) | 描述 | 復(fù)位值 |
| 15:0 | SCLH | SCL高電平周期選擇計(jì)數(shù)高電平周期選擇計(jì)數(shù) | 0x0004 |
SCL占空比寄存器(I2SCLL): 16位寄存器,用于定義SCL低電平所保持的PCLK周期數(shù)。
| 位 | 符號(hào) | 描述 | 復(fù)位值 |
| 15:0 | SCLL | SCL低電平周期選擇計(jì)數(shù)低電平周期選擇計(jì)數(shù) | 0x0004 |
?
??? 在前面的I2C總線特征中我們提到過,I2C總線的速率通過可編程時(shí)鐘來調(diào)整,即必須通過軟件對(duì)I2SCLH和I2SCLL寄存器進(jìn)行設(shè)置來選擇合適的數(shù)據(jù)頻率和占空比。 頻率由下面的公式得出(fPCLK是PCLK的頻率)。
?
1.3.2?LPC2368中I2C總線操作
??? 在1.1.4中我們已經(jīng)講過了對(duì)I2C總線的操作,但那只是從協(xié)議和時(shí)序上的描述,那我們?nèi)绾螐能浖先ンw現(xiàn)出來呢?接下來我們就討論這個(gè)問題。
??? 對(duì)I2C總線上主從設(shè)備的讀寫可使用兩種方法,一是使用輪詢的方式,二是使用中斷的方式。輪詢方式即是在一個(gè)循環(huán)中判斷I2C狀態(tài)寄存器當(dāng)前的狀態(tài)值來確定總線當(dāng)前所處的狀態(tài),然后根據(jù)這個(gè)狀態(tài)來進(jìn)行下一步的操作。中斷方式即是使能I2C中斷,注冊(cè)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)值含義請(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 : 無 ** Output : 無 ** Created Date : 2011-03-24? *****************************************************************************/ void?I2C0_Exception(void) { ????volatile?uint32 stat_value; ????stat_value?=?I20STAT;
????switch(stat_value) ????{ ????????case?0x08: ????????????/*發(fā)出了一個(gè)起始條件,接下來將發(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ā)送完成,接下來要將發(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ā)送下一個(gè)字節(jié)*/ ????????case?0x30: ????????????/*已發(fā)送I2DAT中的字節(jié)且接收到非ACK,接下來可能發(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 : 無 ** 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 : 無 ** 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 : 無 ** 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總線并沒有像無系統(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)圖如下:
???????????
??? 由此看來,在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 : 無 ** 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接口功率/時(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; } |
總結(jié)
以上是生活随笔為你收集整理的I2C总线介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。