這里要先介紹I2C總線中幾個專有名詞:
l 發(fā)送者:將數(shù)據(jù)發(fā)送到總線的設(shè)備
l 接收者:從總線接收數(shù)據(jù)的設(shè)備
l 主設(shè)備:產(chǎn)生時鐘信號、啟動通信、發(fā)送I2C命令和終止通信的設(shè)備
l 從設(shè)備:監(jiān)聽總線、能被主設(shè)備尋址的設(shè)備
l 多主設(shè)備:I2C能夠擁有多個主設(shè)備,而且每個主設(shè)備都能夠發(fā)送命令
l 仲裁:當多個主設(shè)備請求使用總線時,決定哪一個主設(shè)備可以占用的一個過程
l 同步:同步多個設(shè)備時鐘信號的一個過程
上面是從宏觀上對I2C總線介紹了下,接下來將深入細節(jié)研究其通信過程:
n 串行數(shù)據(jù)傳送:
在總線備用時SDA和SCL都必須保持高電平狀態(tài),只有關(guān)閉I2C總線時才能使SCL鉗位在低電平。在I2C總線數(shù)據(jù)傳輸時,在時鐘線高電平期間,數(shù)據(jù)線上必須保持有穩(wěn)定的邏輯電平(也就是說在數(shù)據(jù)傳輸期間只有時鐘線低電平期間,才允許數(shù)據(jù)線上的電平發(fā)生變化)。

圖 7_3 串行數(shù)據(jù)發(fā)送
因此在如圖7_3中對于每一個時鐘脈沖期間一比特的數(shù)據(jù)將會被傳送,SDA只能在時鐘信號為低電平時才能改變。下面是代碼中發(fā)送一字節(jié)的函數(shù):在循環(huán)體內(nèi)每次將dat內(nèi)的最高位移出到CY中,進而賦值給SDA(這時SCL為低,SDA可改變)。接著拉高SCL并保持5us,最后再拉低SCL實現(xiàn)一個時鐘脈沖將dat中最高位送出。依此循環(huán)8次實現(xiàn)將dat全部傳出。
//------------------------------------------------
//向I2C總線發(fā)送一個字節(jié)數(shù)據(jù)
//------------------------------------------------
void I2C_SendByte(uchar dat)
{
uchar i;
for (i=0; i《8; i++) //8位計數(shù)器
{
dat 《《= 1; //移出數(shù)據(jù)的最高位
SDA = CY; //送數(shù)據(jù)口
SCL = 1; //拉高時鐘線
Delay5us(); //延時
SCL = 0; //拉低時鐘線
Delay5us(); //延時
}
I2C_RecvACK();
}
n 開始和結(jié)束條件:
命令不會沒有任何預兆直接發(fā)送的,每一個I2C命令的發(fā)送總是開始于開始條件并結(jié)束于終止條件。這里所謂的開始條件和終止條件起始也是由SCL和SDA組合形成的(如圖7_4)。

圖 7_4 開始和結(jié)束條件
如果時鐘線保持高電平期間,數(shù)據(jù)線出現(xiàn)由高到低的電平變化,則會啟動I2C總線,此時為I2C的起始信號:
//------------------------------------------------
//I2C起始信號
//------------------------------------------------
void I2C_Start()
{
SDA = 1; //拉高數(shù)據(jù)線
SCL = 1; //拉高時鐘線
Delay5us(); //延時
SDA = 0; //產(chǎn)生下降沿
Delay5us(); //延時
SCL = 0; //拉低時鐘線
}
若在時鐘線保持高電平期間,數(shù)據(jù)線出現(xiàn)由低到高的電平變化,則會停止I2C總線的數(shù)據(jù)傳輸,此時為I2C的終止信號:
//------------------------------------------------
//I2C停止信號
//------------------------------------------------
void I2C_Stop()
{
SDA = 0; //拉低數(shù)據(jù)線
SCL = 1; //拉高時鐘線
Delay5us(); //延時
SDA = 1; //產(chǎn)生上升沿
Delay5us(); //延時
}
開始條件之后I2C總線被認為是忙狀態(tài),只有當停止信號之后其他主設(shè)備才能使用該總線。此外,當開始條件之后主設(shè)備能夠多次發(fā)出開始信號。這些開始信號和第一次發(fā)出的開始信號類似,他們后面經(jīng)常會跟從設(shè)備的地址。這樣可以方便實現(xiàn)在I2C總線忙期間,當前占線的主設(shè)備可以和不同的從設(shè)備進行通信。
n I2C數(shù)據(jù)傳送:
I2C總線上傳送的每一個字節(jié)均為8位,但是每啟動一次I2C總線,其后的數(shù)據(jù)傳送字節(jié)數(shù)是沒有限制的。同時每傳送一字節(jié)的數(shù)據(jù)后面都要跟隨一個接收者回應(yīng)的應(yīng)答位(低電平為應(yīng)答信號,高電平為非應(yīng)答信號),當全部數(shù)據(jù)發(fā)送完畢后主設(shè)備發(fā)送終止信號。

圖 7_5 數(shù)據(jù)傳送圖
所以在上面向I2C總線發(fā)送一字節(jié)的數(shù)據(jù)的代碼的最后有一個I2C_RecvACK()函數(shù)。(如下)該函數(shù)負責接收接收者發(fā)送過來的應(yīng)答信號,也即圖7_5中的第9個時鐘脈沖的期間的相應(yīng)操作。
//------------------------------------------------
//I2C接收應(yīng)答信號
//------------------------------------------------
bit I2C_RecvACK()
{
SCL = 1; //拉高時鐘線
Delay5us(); //延時
CY = SDA; //讀應(yīng)答信號
SCL = 0; //拉低時鐘線
Delay5us(); //延時
return CY;
}
電子發(fā)燒友App





評論