IIC通信協議
IIC是同步半雙工通信,一個數據線SDA和一個時鐘SCL線,可以接受和發送數據。在CPU與被控IC之間、IC與IC之間進行雙向傳送。
空閑狀態
IIC總線的SDA和SCL兩條信號線同時處于高電平時,規定為總線的空閑狀態。
起始信號
當SCL為高期間,SDA由高到低的跳轉
代碼實現為:
//產生IIC起始信號
void IIC_Start(void)
{SDA_OUT(); //sda線輸出模式IIC_SDA=1; IIC_SCL=1;delay_us(4);IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4);IIC_SCL=0;//鉗住I2C總線,準備發送或接收數據
}
停止信號
當SCL為高期間,SDA由低到高的跳轉
代碼實現:
//產生IIC停止信號
void IIC_Stop(void)
{SDA_OUT();//sda線輸出IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highdelay_us(4);IIC_SCL=1; delay_us(4); IIC_SDA=1;//發送I2C總線結束信號
}
應答信號
發送器每發送一個字節,就在時鐘脈沖9期間釋放數據線,由接收器反饋一個應答信號。應答信號為低電平時,規定為有效應答位,表示接收器已經成功接收到了該字節。應答信號為高電平時,規定為非應答位,一般表示接收器接收該字節沒有成功。
對于反饋有效應答位ACK的要求是,接收器第9個時鐘脈沖之前的低電平將SDA線拉低,并且確保在該時鐘的高電平期間為穩定的低電壓。
代碼實現為:
//產生ACK應答
void IIC_Ack(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=0;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}//不產生ACK應答
void IIC_NAck(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=1;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}
//等待應答信號到來
//返回值:1,接收應答失敗
// 0,接收應答成功
u8 IIC_Wait_Ack(void)
{u8 ucErrTime=0;SDA_IN(); //SDA設置為輸入 IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA){ucErrTime++;if(ucErrTime>250){IIC_Stop();return 1;}}IIC_SCL=0;//時鐘輸出0 return 0;
}
數據的有效性
IIC總線進行數據傳送時,時鐘信號為高電平期間,數據線上的數據必須保持穩定,只有在時鐘線上的信號為低電平期間,數據線上的高電平或低電平才允許變化。即數據在SCL的上升沿到來之前就需要準備好,并且在下降沿到來之前必須保持穩定。
數據的傳送
在IIC總線上傳送的每一位數據都有一個時鐘脈沖相對應(同步控制),即在SCL串行時鐘的配合下,在SDA上逐位串行傳送每一位數據,數據位的傳輸是邊沿觸發。
發送一字節代碼實現為:
//IIC發送一個字節
//返回從機有無應答
//1,有應答
//0,無應答
void IIC_Send_Byte(u8 txd)
{ u8 t; SDA_OUT(); IIC_SCL=0;//拉低時鐘開始數據傳輸for(t=0;t<8;t++){ IIC_SDA=(txd&0x80)>>7;txd<<=1; delay_us(2); //對TEA5767這三個延時都是必須的IIC_SCL=1;delay_us(2); IIC_SCL=0; delay_us(2);}
}
數據傳輸之前IIC_SCL必須等于0,即時信號為低電平,然后準備一位數據,準備好之后,IIC_SCL=1,即時鐘信號為高電平,數據就傳輸過去了
對于下面的代碼:
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
無符號類型左移丟棄最高位,低位補0,無符號類型右移丟棄最低位,低位補0,txd&0x80讓txd第8位數據不變,其他為變成0(txd的值不受影響),左移7位,將最高位的數據賦值給IIC_SDA,當IIC_SCL=1時,第八位數據就傳輸過去了,txd<<=1讓第7位數據變成第8位數據,下次循環就傳輸原來第7位數據了。循環8次將一字節數據傳輸完畢。
讀取數據代碼實現:
//讀1個字節,ack=1時,發送ACK,ack=0,發送nACK
u8 IIC_Read_Byte(unsigned char ack)
{unsigned char i,receive=0;SDA_IN();//SDA設置為輸入for(i=0;i<8;i++ ){IIC_SCL=0; delay_us(2);IIC_SCL=1;receive<<=1;if(READ_SDA)receive++; delay_us(1); } if (!ack)IIC_NAck();//發送nACKelseIIC_Ack(); //發送ACK return receive;
}
對于下面的代碼:
receive<<=1;if(READ_SDA)receive++;
READ_SDA是獲取SDA線上的電壓,高電壓說明說明傳送過來的數據是1,receive++,下一次的時候, receive<<=1,receive先右移一位,然后接受數據,這樣循環8次,就能保證接受的數據在原來的位置上了。
與EEPROM通信
硬件連接
EEPROM是一種掉電后數據不丟失的存儲芯片,可以在電腦上或專用設備上擦除已有信息,重新編程。
24C02的總容量是256個字節,接口是IIC
模式選擇
硬件連接中,A2=A1=A0=0(接地),所以,我們讀數據時,只需寫入0xA1,表示讀模式,寫數據時,只需寫入0xA0,表示寫模式
寫時序
下面是寫入一個字節數據的過程
- 一個IIC_START信號
- 寫入模式,寫入0xA0
- 等待一個ACK
- 發送寫入數據的地址(0~255)
- 等待一個ACK
- 發送一個字節
- 等待一個ACK
- 一個STOP信號
具體代碼如下
//在AT24CXX指定地址寫入一個數據
//WriteAddr :寫入數據的目的地址
//DataToWrite:要寫入的數據
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{ IIC_Start(); if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0); //發送寫命令IIC_Wait_Ack();IIC_Send_Byte(WriteAddr>>8);//發送高地址 }else IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //發送器件地址0XA0,寫數據 IIC_Wait_Ack(); IIC_Send_Byte(WriteAddr%256); //發送低地址IIC_Wait_Ack(); IIC_Send_Byte(DataToWrite); //發送字節 IIC_Wait_Ack(); IIC_Stop();//產生一個停止條件 delay_ms(10);
}
讀數據
下面是讀一個字節數據的過程
- 產生一個START信號
- 寫入0xA0,表示是寫模式
- 等待一個ACK
- 寫入讀數據的地址
- 等待一個ACK
- 產生一個START信號
- 寫入0xA1,表示是讀模式
- 等待一個ACK
- 讀取數據
實現代碼如下:
//在AT24CXX指定地址讀出一個數據
//ReadAddr:開始讀數的地址
//返回值 :讀到的數據
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{ u8 temp=0; IIC_Start(); if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0); //發送寫命令IIC_Wait_Ack();IIC_Send_Byte(ReadAddr>>8);//發送高地址 }else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //發送器件地址0XA0,寫數據 IIC_Wait_Ack(); IIC_Send_Byte(ReadAddr%256); //發送低地址IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(0XA1); //進入接收模式 IIC_Wait_Ack(); temp=IIC_Read_Byte(0); IIC_Stop();//產生一個停止條件 return temp;
}