上圖是TPS65987的i2c讀寫協議,和標准i2c協議有點出入,不過也不難理解,在讀的時候i2c slave在發送數據過來之前會先發送1byte數據表示后面會有幾個字節數據過來,在寫的時候i2c host要先寫1byte數據告訴i2c slave接下來會寫幾個bytes數據。
Talk is cheap. Show me the code.
以下代碼是基於STM8S。
/******************************************************************************* **函數名稱:void IIC_Read(unsigned char subaddr , unsigned char Byte_addr , unsigned char *buffer) **功能描述:向IIC器件讀數據 **入口參數: subaddr : 從器件地址 Byte_addr : 確定從器件寫地址的起始地址 *buffer : 讀數據的緩沖區起始地址 **輸出:無 *******************************************************************************/ void TPS65987_IIC_Read(unsigned char subaddr , unsigned char Byte_addr , unsigned char *buffer) { unsigned char i2csr1; unsigned char DataLen; I2C_CR2_bit.ACK = 1; //產生應答信號 I2C_CR2_bit.START = 1; //發送起始信號 while(I2C_SR1_bit.SB == 0); //等待起始信號產生 i2csr1 = I2C_SR1; //SR1.AF?? I2C_DR = subaddr; //發送器件地地址,並清除SB標志位 while(I2C_SR1_bit.ADDR == 0); //等待器件地址發送完成 i2csr1 = I2C_SR1; i2csr1 = I2C_SR3; //讀狀態寄存器1和狀態寄存器3清除發送器件地址標志位 I2C_DR = Byte_addr; while(I2C_SR1_bit.BTF == 0);//等待移位發送器發送完成 i2csr1 = I2C_SR1; //清除BIT標志位 //重新發送起始信號 I2C_CR2_bit.START = 1;//I2C1->CR1 |= I2C_CR1_START; while(I2C_SR1_bit.SB == 0);//等待起始信號產生 i2csr1 = I2C_SR1;//SR1.AF?? I2C_DR = (char)(subaddr | 0x01); //發送器件地地址,並清除SB標志位 while(I2C_SR1_bit.ADDR == 0); //等待器件地址發送完成 i2csr1 = I2C_SR1; i2csr1 = I2C_SR3; //讀狀態寄存器1和狀態寄存器2清除發送器件地址標志位 while (I2C_SR1_bit.RXNE == 0); //先讀取Byte Count到DataLen i2csr1 = I2C_SR1; DataLen = I2C_DR; while(DataLen) { if(DataLen == 1) { I2C_CR2_bit.ACK = 0; //最后一個字節不產生應答信號 I2C_CR2_bit.STOP = 1; //發送停止信號結束數據傳輸 } while(I2C_SR1_bit.RXNE == 0); i2csr1 = I2C_SR1; *buffer = I2C_DR; buffer++; DataLen--; } }
/******************************************************************************* **函數名稱:void IIC_Write(unsigned char subaddr , unsigned char Byte_addr , unsigned char *buffer , unsigned short num) **功能描述:向IIC器件寫數據 **入口參數: subaddr : 從器件地址 Byte_addr : 確定器件寫地址的起始地址 *buffer : 寫數據的起址地址 num : 要寫數據的個數 **輸出:無 *******************************************************************************/ void TPS65987_IIC_Write(unsigned char subaddr , unsigned char Byte_addr , unsigned char *buffer , unsigned short num) { unsigned char i2csr1; //while(I2C1->SR2 & I2C_SR2_BUSY); //判斷I2C模塊是否忙 //發送起始信號 I2C_CR2_bit.START = 1; while(I2C_SR1_bit.SB == 0); //等待起始信號產生 i2csr1 = I2C_SR1; //SR1.AF I2C_DR = (subaddr); //發送從器件地址,並清除SB標志位 while(I2C_SR1_bit.ADDR == 0); //等待器件地址發送完成 i2csr1 = I2C_SR1; i2csr1 = I2C_SR3; //讀狀態寄存器1和狀態寄存器3清除發送器件地址標志位 I2C_DR = Byte_addr; //發送從器件存儲首地址 #if 1 while(I2C_SR1_bit.BTF == 0); //等待移位發送器發送完成 i2csr1 = I2C_SR1; //清除BIT標志位 #else while((I2C_SR1_bit.TXE) == 0);//數據寄存器為空,跳出循環繼續運行 i2csr1 = I2C_SR1; #endif I2C_DR = (unsigned char)num; //把Byte Count先告訴給TPS65987 while(I2C_SR1_bit.BTF == 0);//等待移位發送器發送完成 i2csr1 = I2C_SR1; //清除BIT標志位 i2csr1 = I2C_DR; while(num > 0) { I2C_DR = *buffer; //發送器件存儲首地址 while(I2C_SR1_bit.BTF == 0);//等待移位發送器發送完成 i2csr1 = I2C_SR1; //清除BIT標志位 i2csr1 = I2C_DR; buffer++; num--; } I2C_CR2_bit.STOP = 1; //發送停止信號結束數據傳輸 }
這樣就可以對TPS65987進行讀寫了。
上面的i2c讀寫函數沒有加上timeout功能,如果不想在i2c通信不成功時一直阻塞的話,可以在while循環里面加上,例如:
unsigned int count = 0; while(I2C_SR1_bit.ADDR == 0) //等待器件地址發送完成 { if (++count > 6000) { //count大於6000立即返回 I2C_CR2_bit.STOP = 1; return; } }
注意:
從Windows下上位機工具也可以進行TPS65987的register讀寫,TPS65981_2_6_7_8 Application Customization 6.1.1上顯示的i2c1地址為0x20, i2c2的地址為0x38;注意0x20/0x38是七位地址位的值,進行i2c讀寫時的地址要左移一位,即0x20/0x38 << 1等於0x40/0x70。
如果直接用地址0x20/0x38進行讀寫會怎么樣呢,結果就是地址發過去沒收到ACK。下圖是用0x38地址去讀寄存器值的時候示波器抓到的波形(黃色波形是SCL,紫色波形是SDA)。
從波形上看0x38地址發過去是沒有ACK的,所以slave地址0x38肯定是錯誤的了。
后面用示波器量了一下TPS65981_2_6_7_8 Application Customization 6.1.1上位機軟件和TPS65987 EVM進行i2c讀寫時的波形,發現i2c2的地址發過去的確實是0x70。