I2C总线标准代码
/********
起始信號,SCL=1,時鐘總線為高電平,數(shù)據(jù)總線由高電平變?yōu)榈碗娖絊DA=1-->SDA=0
void I2C_Start(void)
{
I2C_SCL_HIGH();//SCL=1
I2C_SDA_HIGH();//SDA=1? 開始都為高電平,當(dāng)SDA為低電平為起始信號
I2C_DELAY();
I2C_SDA_LOW();
I2C_DELAY();
I2C_SCL_LOW();
I2C_DELAY();
}
停止信號 SCL=1,SDA=0,時鐘總線為高電平,數(shù)據(jù)總線由低電平變?yōu)楦唠娖絊DA=0-->SDA=1
void I2C_Stop(void)
{
????I2C_SDA_Low();
????I2C_SCL_High();
????I2C_Delay();
????I2C_SDA_High();
????I2C_Delay();
}
發(fā)送一個字節(jié)8位
u8 I2C_SendByte(uint8_t Byte)
{
????uint8_t i;
????for(i = 0 ; i < 8 ; i++)/* 先發(fā)送高位字節(jié) */
????{
????????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();
????}
}
?
//CPU從I2C總線設(shè)備上讀取一個字節(jié)(8bit數(shù)據(jù))
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;
}
//CPU產(chǎn)生一個ACK信號
void I2C_Ack(void)
{
? ? I2C_SDA_Low();
? ? I2C_Delay();
? ? I2C_SCL_High();
? ? I2C_Delay();
? ? I2C_SCL_Low();
? ? I2C_Delay();
? ? I2C_SDA_High();
}
//CPU產(chǎn)生一個非ACK信號
void I2C_NoAck(void)
{
? ? I2C_SDA_High();
? ? I2C_Delay();
? ? I2C_SCL_High();
? ? I2C_Delay();
? ? I2C_SCL_Low();
? ? I2C_Delay();
}
//CPU產(chǎn)生一個時鐘,并讀取器件的ACK應(yīng)答信號
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;
}
寫數(shù)據(jù)時序圖進(jìn)行分解,經(jīng)分解后如下圖所示:
結(jié)合I2C總線協(xié)議的知識,我們可以知道I2C寫數(shù)據(jù)由一下10個步驟組成。?
??第一步,發(fā)送一個起始信號。?
??第二步,發(fā)送7bit從機(jī)地址,即OZ9350的地址。此處需要注意,發(fā)送數(shù)據(jù)時,無法發(fā)送7bit數(shù)據(jù),此處發(fā)送了7bit地址+1bit讀寫選擇位,即發(fā)送7bit+r/w。最低位為1表示讀,為0表示寫。?
??第三步,產(chǎn)生一個ACK應(yīng)答信號,此應(yīng)答信號為從機(jī)器件產(chǎn)生的應(yīng)答。?
??第四步,發(fā)送寄存器地址,8bit數(shù)據(jù)。?
??第五步,產(chǎn)生一個ACK應(yīng)答信號,此應(yīng)答信號為從機(jī)器件產(chǎn)生的應(yīng)答。?
??第六步,發(fā)送一個數(shù)據(jù),8bit數(shù)據(jù)。?
??第七步,產(chǎn)生一個ACK應(yīng)答信號,此應(yīng)答信號為從機(jī)器件產(chǎn)生的應(yīng)答信號。?
??第八步,發(fā)送一個CRC校驗(yàn)碼,此CRC校驗(yàn)值為2、4、6步數(shù)據(jù)產(chǎn)生的校驗(yàn)碼。?
??第九步,既可以發(fā)送一個應(yīng)答信號,也可以發(fā)送一個無應(yīng)答信號,均有從機(jī)器件產(chǎn)生。?
??第十步,發(fā)送一個停止信號。?
??接下來,按照以上十個步驟,可以寫出i2c寫數(shù)據(jù)的函數(shù)。代碼如下: ?
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
}
讀數(shù)據(jù)的時序圖經(jīng)分解后如下圖所示:
通過分解后的時序圖,可以看到OZ9350的讀數(shù)據(jù)由以下13個步驟組成。?
??第一步,發(fā)送一個起始信號。?
??第二步,發(fā)送7bit從機(jī)地址,即OZ9350的地址。此處需要注意,發(fā)送數(shù)據(jù)時,無法發(fā)送7bit數(shù)據(jù),此處發(fā)送了7bit地址+1bit讀寫選擇位,即發(fā)送7bit+r/w。最低位為1表示讀,為0表示寫。?
??第三步,產(chǎn)生一個ACK應(yīng)答信號,此應(yīng)答信號為從機(jī)器件產(chǎn)生的應(yīng)答。?
??第四步,發(fā)送寄存器地址。?
??第五步,產(chǎn)生一個ACK應(yīng)答信號,此應(yīng)答信號為從機(jī)器件產(chǎn)生的應(yīng)答。?
??第六步,再次發(fā)送一個騎士信號。?
??第七步,發(fā)送7bit從機(jī)地址,即OZ9350的地址。此處需要注意,發(fā)送數(shù)據(jù)時,無法發(fā)送7bit數(shù)據(jù),此處發(fā)送了7bit地址+1bit讀寫選擇位,即發(fā)送7bit+r/w。最低位為1表示讀,為0表示寫。?
??第八步,產(chǎn)生一個ACK應(yīng)答信號,此應(yīng)答信號為從機(jī)器件產(chǎn)生的應(yīng)答。?
??第九步,讀取一個字節(jié)(8bit)的數(shù)據(jù)。?
??第十步,產(chǎn)生一個ACK應(yīng)答信號,此應(yīng)答信號為CPU產(chǎn)生。?
??第十一步,讀取一個CRC校驗(yàn)碼。?
??第十二步,產(chǎn)生一個NACK信號。此無應(yīng)答信號由CPU產(chǎn)生。?
??第十三步,產(chǎn)生一個停止信號。?
??接下來,由以上分析步驟,可以寫出OZ9350的I2C讀數(shù)據(jù)代碼。如下所示:
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
}
寫:MCU在數(shù)據(jù)總線上的數(shù)據(jù)穩(wěn)定之后,檢測邊沿信號(上升沿)寫數(shù)據(jù)到器件;
讀:MCU發(fā)出邊沿信號(下降沿)告訴器件發(fā)送數(shù)據(jù),檢測到邊沿信號之后,器件改變(更新)數(shù)據(jù),等待穩(wěn)定之后MCU讀取數(shù)據(jù)
數(shù)據(jù)的寫操作:圖中演示了I2C連續(xù)寫數(shù)據(jù),兩個字節(jié)的連續(xù)寫入,更多字節(jié)同樣
數(shù)據(jù)的讀操作:在上圖中,可以認(rèn)為寫入了設(shè)備地址及寄存器地址,再次重啟總線后,發(fā)送讀命令,連續(xù)讀取兩個字節(jié),發(fā)送NACK,發(fā)送停止信號;
?
總結(jié)
- 上一篇: Linux编程简介——VI
- 下一篇: 隐藏网页文件的后缀(IIS测试通过)!