51微控制器——I2C匯流排
微控制器——I2C
宗旨:技術的學習是有限的,分享的精神是無限的。
UART 屬於非同步通訊,比如電腦傳送給微控制器,電腦只負責把資料通過TXD 傳送出來即可,接收資料是微控制器自己的事情。而 I2C 屬於同步通訊, SCL 時鐘線負責收發雙方的時鐘節拍, SDA 資料線負責傳輸資料。 I2C 的傳送方和接收方都以 SCL 這個時鐘節拍為基準進行資料的傳送和接收。
I2C匯流排包括SCL,SDA 兩根訊號線,其中SCL是時鐘線,SDA是資料線。
1、起始訊號
UART 通訊是從一直持續的高電平出現一個低電平標誌起始位;而 I2C 通訊的起始訊號的定義是 SCL 為高電平期間, SDA 由高電平向低電平變化產生一個下降沿,表示起始訊號。
2、資料傳輸
UART 是低位在前,高位在後;而 I2C 通訊是高位在前,低位在後。UART 通訊資料位是固定長度,波特率分之一,一位一位固定時間傳送完畢就可以了。而 I2C 沒有固定波特率,但是有時序的要求,要求當 SCL 在低電平的時候, SDA 允許變化。
3、停止訊號
UART 通訊的停止位是一位固定的高電平訊號; 而 I2C 通訊停止訊號的定義是 SCL 為高電平期間, SDA 由低電平向高電平變化產生一個上升沿,表示結束訊號。
4、寫完從器件之後等待從器件的應答
在主器件完成對從器件的寫操作時候(每次會有一個位元組的資料),主器件會等待從器件傳送指示訊號,這個指示訊號是說從器件已經接受到了主器件的資料,這是由從器件的硬體來完成的,不需要主器件來軟體操作,只需要等待;
5、主器件讀完資料後向從器件傳送應答訊號
這其實包括兩種情況,一種是主器件讀完後還要繼續讀就要傳送一個繼續讀的訊號(其實就是傳送0),另一種就是不再繼續讀了,就要傳送停止讀訊號(其實就是傳送1)。
6、I2C定址模式
I2C 通訊的起始訊號(Start)後,首先要傳送一個從機的地址,這個地址一共有 7位,緊跟著的第 8 位是資料方向位(R/W),“ 0”表示接下來要傳送資料(寫),‘“ 1”表示接下來是請求資料(讀)。第九位 ACK應答。
#include<reg52.h> #include<intrins.h> #define I2CDelay() {_nop_();_nop_();_nop_();_nop_();} sbit I2C_SCL = P3 ^ 7; sbit I2C_SDA = P3 ^ 6; /* 產生匯流排起始訊號 */ void I2CStart() { I2C_SDA = 1; //首先確保SDA、SCL都是高電平 I2C_SCL = 1; I2CDelay(); I2C_SDA = 0; //先拉低SDA I2CDelay(); I2C_SCL = 0; //再拉低SCL } /* 產生匯流排停止訊號 */ void I2CStop() { I2C_SCL = 0; //首先確保SDA、SCL都是低電平 I2C_SDA = 0; I2CDelay(); I2C_SCL = 1; //先拉高SCL I2CDelay(); I2C_SDA = 1; //再拉高SDA I2CDelay(); } /* I2C匯流排寫操作,dat-待寫入位元組,返回值-從機應答位的值 */ bit I2CWrite(unsigned char dat) { bit ack; //用於暫存應答位的值 unsigned char mask; //用於探測位元組內某一位值的掩碼變數 for (mask = 0x80; mask != 0; mask >>= 1) //從高位到低位依次進行 { if ((mask & dat) == 0) //該位的值輸出到SDA上 { I2C_SDA = 0; } else { I2C_SDA = 1; } I2CDelay(); I2C_SCL = 1; //拉高SCL I2CDelay(); I2C_SCL = 0; //再拉低SCL,完成一個位週期 } I2C_SDA = 1; //8位資料傳送完後,主機釋放SDA,以檢測從機應答 I2CDelay(); I2C_SCL = 1; //拉高SCL ack = I2C_SDA; //讀取此時的SDA值,即為從機的應答值 I2CDelay(); I2C_SCL = 0; //再拉低SCL完成應答位,並保持住匯流排 return (~ack); //應答值取反以符合通常的邏輯: //0=不存在或忙或寫入失敗,1=存在且空閒或寫入成功 } /* I2C匯流排讀操作,併發送非應答訊號,返回值-讀到的位元組 */ unsigned char I2CReadNAK() { unsigned char mask; unsigned char dat; I2C_SDA = 1; //首先確保主機釋放SDA for (mask = 0x80; mask != 0; mask >>= 1) //從高位到低位依次進行 { I2CDelay(); I2C_SCL = 1; //拉高SCL if(I2C_SDA == 0) //讀取SDA的值 { dat &= ~mask; //為0時,dat中對應位清零 } else { dat |= mask; //為1時,dat中對應位置1 } I2CDelay(); I2C_SCL = 0; //再拉低SCL,以使從機發送出下一位 } I2C_SDA = 1; //8位資料傳送完後,拉高SDA,傳送非應答訊號 I2CDelay(); I2C_SCL = 1; //拉高SCL I2CDelay(); I2C_SCL = 0; //再拉低SCL完成非應答位,並保持住匯流排 return dat; } /* I2C匯流排讀操作,併發送應答訊號,返回值-讀到的位元組 */ unsigned char I2CReadACK() { unsigned char mask; unsigned char dat; I2C_SDA = 1; //首先確保主機釋放SDA for (mask = 0x80; mask != 0; mask >>= 1) //從高位到低位依次進行 { I2CDelay(); I2C_SCL = 1; //拉高SCL if(I2C_SDA == 0) //讀取SDA的值 { dat &= ~mask; //為0時,dat中對應位清零 } else { dat |= mask; //為1時,dat中對應位置1 } I2CDelay(); I2C_SCL = 0; //再拉低SCL,以使從機發送出下一位 } I2C_SDA = 0; //8位資料傳送完後,拉低SDA,傳送應答訊號 I2CDelay(); I2C_SCL = 1; //拉高SCL I2CDelay(); I2C_SCL = 0; //再拉低SCL完成應答位,並保持住匯流排 return dat; }