1. 程式人生 > >I2C通信時序圖解析

I2C通信時序圖解析

分析 前端 讀數 講解 步驟 oid 2.4 lock nbsp

一、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通信的時序圖,在理解原理的基礎上還需要自己多動手練習,只有這樣才能熟練掌握。如果在博文中出現錯誤之處,還望指正。

I2C通信時序圖解析