1. 程式人生 > >51微控制器——I2C匯流排

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)。

6I2C定址模式

        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;
}