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