I2C通信時序圖解析


I2C通信時序圖解析

 

一、I2C協議簡介

  I2C 通訊協議(Inter-Integrated Circuit)是由 Phiilps 公司開發的,由於它引腳少,硬件實現簡單,可擴展性強,不需要 USART、CAN 等通訊協議的外部收發設備,現在被廣泛地 使用在系統內多個集成電路(IC)間的通訊。 
  關於I2C協議的更多內容,可閱讀《I2C總線協議》,本博文主要分析I2C波形圖,對於I2C的基礎知識不在做介紹。

二、I2C協議標准代碼

2.1 起始信號&停止信號

  起始信號:當 SCL 線是高電平時 SDA 線從高電平向低電平切換。 
  停止信號:當 SCL 線是高電平時 SDA 線由低電平向高電平切換。

2.1.1 起始信號代碼

void I2C_Start(void)
{
    I2C_SDA_High();     //SDA=1
    I2C_SCL_High();     //SCL=1
    I2C_Delay();
    I2C_SDA_Low();
    I2C_Delay();
    I2C_SCL_Low();
    I2C_Delay();
}

2.1.2 停止信號代碼

void I2C_Stop(void)
{
    I2C_SDA_Low();
    I2C_SCL_High();
    I2C_Delay();
    I2C_SDA_High();
    I2C_Delay();
}

2.2 發送一個字節

  CPU向I2C總線設備發送一個字節(8bit)數據

u8 I2C_SendByte(uint8_t Byte)
{
    uint8_t i;
 
    /* 先發送高位字節 */
    for(i = 0 ; i < 8 ; i++)
    {
        if(Byte & 0x80)
        {
            I2C_SDA_High();
        }
        else
        {
            I2C_SDA_Low();
        }
        I2C_Delay();
        I2C_SCL_High();
        I2C_Delay();
        I2C_SCL_Low();
        I2C_Delay();
 
        if(i == 7)
        {
            I2C_SDA_High();                     /* 釋放SDA總線 */
        }
        Byte <<= 1;                             /* 左移一位  */
 
        I2C_Delay();
    }
} 

2.3 讀取一個字節

  CPU從I2C總線設備上讀取一個字節(8bit數據)

u8 I2C_ReadByte(void)
{
    uint8_t i;
    uint8_t value;
 
    /* 先讀取最高位即bit7 */
    value = 0;
    for(i = 0 ; i < 8 ; i++)
    {
        value <<= 1;
        I2C_SCL_High();
        I2C_Delay();
        if(I2C_SDA_READ())
        {
            value++;
        }
        I2C_SCL_Low();
        I2C_Delay();
    }
 
    return value;
}

2.4 應答

2.4.1 CPU產生一個ACK信號

void I2C_Ack(void)
{
    I2C_SDA_Low();
    I2C_Delay();
    I2C_SCL_High();
    I2C_Delay();
    I2C_SCL_Low();
    I2C_Delay();
 
    I2C_SDA_High();
}

2.4.2 CPU產生一個非ACK信號

void I2C_NoAck(void)
{
    I2C_SDA_High();
    I2C_Delay();
    I2C_SCL_High();
    I2C_Delay();
    I2C_SCL_Low();
    I2C_Delay();
}

2.4.3 CPU產生一個時鍾,並讀取器件的ACK應答信號

uint8_t I2C_WaitToAck(void)
{
    uint8_t redata;
 
    I2C_SDA_High();
    I2C_Delay();
    I2C_SCL_High();
    I2C_Delay();
 
    if(I2C_SDA_READ())
    {
        redata = 1;
    }
    else
    {
        redata = 0;
    }
    I2C_SCL_Low();
    I2C_Delay();
 
    return redata;
}  

三、I2C通信時序圖解析

  有了上邊的I2C總線標准代碼的基礎,下面我們進入本博文所要講解的內容,怎么分析I2C的時序圖,以O2Micro的OZ9350為例,OZ9350是一款模擬前端(AFE)的IC器件。是一款性價比不錯的電源管理芯片,由於其通訊是通過I2C來進行通訊的,所以這里用OZ9350的I2C通訊做例子進行講解。

3.1 寫數據

  首先我們先來看一下寫數據的時序圖,如下圖所示

首先我們先來看一下寫數據的時序圖,如下圖所示: 
  將上圖中的寫數據時序圖進行分解,經分解后如下圖所示: 

  結合I2C總線協議的知識,我們可以知道OZ9350的I2C寫數據由一下10個步驟組成。 
  第一步,發送一個起始信號。 
  第二步,發送7bit從機地址,即OZ9350的地址。此處需要注意,發送數據時,無法發送7bit數據,此處發送了7bit地址+1bit讀寫選擇位,即發送7bit+r/w。最低位為1表示讀,為0表示寫。 
  第三步,產生一個ACK應答信號,此應答信號為從機器件產生的應答。 
  第四步,發送寄存器地址,8bit數據。 
  第五步,產生一個ACK應答信號,此應答信號為從機器件產生的應答。 
  第六步,發送一個數據,8bit數據。 
  第七步,產生一個ACK應答信號,此應答信號為從機器件產生的應答信號。 
  第八步,發送一個CRC校驗碼,此CRC校驗值為2、4、6步數據產生的校驗碼。 
  第九步,既可以發送一個應答信號,也可以發送一個無應答信號,均有從機器件產生。 
  第十步,發送一個停止信號。 
  接下來,按照以上是個步驟,可以寫出OZ9350的i2c寫數據的函數。代碼如下:

u8 I2C_WriteBytes(void)
{
    I2C_Start();                    //1
 
    I2C_SendByte(Slaver_Addr | 0);  //2
    I2C_WaitToAck();                //3
 
    I2C_SendByte(Reg_Addr);         //4
    I2C_WaitToAck();                //5
 
    I2C_SendByte(data);             //6
    I2C_WaitToAck();                //7
 
    I2C_SendByte(crc);              //8
    I2C_WaitToAck();                //9
 
    I2C_Stop();                     //10
}

3.2 讀數據

  讀數據的時序圖如下圖所示: 

  讀數據的時序圖經分解后如下圖所示: 

  通過分解后的時序圖,可以看到OZ9350的讀數據由以下13個步驟組成。 
  第一步,發送一個起始信號。 
  第二步,發送7bit從機地址,即OZ9350的地址。此處需要注意,發送數據時,無法發送7bit數據,此處發送了7bit地址+1bit讀寫選擇位,即發送7bit+r/w。最低位為1表示讀,為0表示寫。 
  第三步,產生一個ACK應答信號,此應答信號為從機器件產生的應答。 
  第四步,發送寄存器地址。 
  第五步,產生一個ACK應答信號,此應答信號為從機器件產生的應答。 
  第六步,再次發送一個騎士信號。 
  第七步,發送7bit從機地址,即OZ9350的地址。此處需要注意,發送數據時,無法發送7bit數據,此處發送了7bit地址+1bit讀寫選擇位,即發送7bit+r/w。最低位為1表示讀,為0表示寫。 
  第八步,產生一個ACK應答信號,此應答信號為從機器件產生的應答。 
  第九步,讀取一個字節(8bit)的數據。 
  第十步,產生一個ACK應答信號,此應答信號為CPU產生。 
  第十一步,讀取一個CRC校驗碼。 
  第十二步,產生一個NACK信號。此無應答信號由CPU產生。 
  第十三步,產生一個停止信號。 
  接下來,由以上分析步驟,可以寫出OZ9350的I2C讀數據代碼。如下所示:

u8 I2C_ReadBytes(void)
{
    u8 data;
    u8 crc;
 
    I2C_Start();                    //1
 
    I2C_SendByte(Slaver_Addr | 0);  //2
    I2C_WaitToAck();                //3
 
    I2C_SendByte(Reg_Addr);         //4
    I2C_WaitToAck();                //5
 
    I2C_Start();                   //6
 
    I2C_SendByte(Slaver_Addr | 1);  //7 1-讀
    I2C_WaitToAck();                //8
 
    data=I2C_ReadByte();            //9
 
    I2C_Ack();                      //10
 
    crc=I2C_ReadByte();             //11
 
    I2C_NoAck();                    //12
 
    I2C_Stop();                     //13
}

四、結語

  關於怎樣分析I2C通信的時序圖,在理解原理的基礎上還需要自己多動手練習,只有這樣才能熟練掌握。如果在博文中出現錯誤之處,還望指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM