文章目錄
IIC簡介
IIC(又稱I2C):Inter-Integrated Circuit,一種兩線式串行總線,用於連接微控制器及其外圍設備。它是由數據線 SDA 和時鐘 SCL 構成的串行總線,可發送和接收數據。(半雙工通信)在 CPU 與被控 IC 之間、 IC 與 IC 之間進行雙向傳送, 高速 IIC 總線一般可達 400kbps 以上。
I2C 總線在傳送數據過程中共有三種類型信號, 它們分別是:開始信號、結束信號和應答信號。
IIC協議
空閒狀態
I2C總線總線的SDA和SCL兩條信號線同時處於高電平時,規定爲總線的空閒狀態。此時各個器件的輸出級場效應管均處在截止狀態,即釋放總線,由兩條信號線各自的上拉電阻把電平拉高。(即使用時會接上拉電阻,所以雙高電平表示無信號)
起始信號與結束信號
- 起始信號:當SCL爲高期間,SDA由高到低的跳變,啓動信號是一種電平跳變時序信號,而不是一個電平信號。(類似邊沿觸發)
- 停止信號:當SCL爲高期間,SDA由低到高的跳變。(同上)
應答信號ACK
**發送器每發送一個字節(8個位)就在時鐘脈衝9期間釋放數據線(半雙工),由接收器反饋一個應答信號。**應答信號爲低電平時,規定內有效應答位(ACK簡稱應答位),表示接收器已經成功地接收了該字節,應答信號爲高電平時,規定爲非應答位(NACK),一般表示接收器接收該字節沒有成功。
**對於反饋有效應答位ACK的要求是,接收器在第9個時鐘脈衝之前的低電平期間將SDA線拉低,並且確保在該時鐘的高電平期間爲穩定的低電平。**如果接收器是主控器,則在它收到最後一個字節後,發送一個NACK信號,以通知被控發送器結束數據發送,並釋放SDA線,以便主控接收器發送一個停止信號P.
數據有效性
I2C總線進行數據傳送時,時鐘信號爲高電平期間,數據線上的數據必須保持穩定,只有在時鐘線上的信號爲低電平期間,數據線上的高電平或低電平狀態才允許變化。
即。數據在SCL的上升沿到來之前就需準備好。並在在下降沿到來之前必須穩定。
數據的傳送
在I2C總線上傳送的每一位數據都有一個時鐘脈衝相對應(或同步控制),即在SCL串行時鐘的配合下,在SDA上逐位地串行傳送每一位數據。數據位的傳輸是邊沿觸發。(參考ACK應答信號的圖片)
邊沿觸發:在數字電平變化的電壓上升沿或下降沿到一定閥值時就產生觸發。邊沿觸發一般時間短,邊沿觸發一般時間都是us級的,響應要快的,
傳輸過程
相關配置
標題初始化IIC(這裏用IO口模擬)
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;//初始化結構體
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 輸出高
}
發送起始信號
void IIC_Start(void)
{
SDA_OUT(); //SDA線輸出(半雙工需要)
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//當SCL爲高期間,SDA由高到低的跳變
delay_us(4);
IIC_SCL=0;//準備發送或接收數據 (數據傳輸需要拉低)
}
發送結束信號
void IIC_Stop(void)
{
SDA_OUT();//SDA線輸出
IIC_SCL=0;
IIC_SDA=0;
delay_us(4);
IIC_SCL=1;
IIC_SDA=1; //當SCL爲高期間,SDA由低到高的跳變
delay_us(4);
}
應答信號ACK
接收ACK
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)//READ_SDA:輸入的數據
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;//接收應答失敗,停止傳輸
}
}
IIC_SCL=0;
return 0; ////接收應答成功,可繼續傳輸
}
發送ACK
u8 IIC_Read_Byte(unsigned char ack)//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();//發送nACK
else
IIC_Ack(); //發送ACK
return receive;
}
void IIC_Ack(void) //發送ACK
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
void IIC_NAck(void) //發送NACK
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=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;//IIC_SDA此時等於相應位
txd<<=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
例子
//在AT24C16指定地址讀出一個數據
//ReadAddr:開始讀數的地址
//返回值 :讀到的數據
u8 AT24C16_ReadOneByte(u16 ReadAddr)
{
u8 temp=0;
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //發送寫命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr>>8);//發送高地址
IIC_Wait_Ack();
}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;
}
//在AT24C16指定地址寫入一個數據
//WriteAddr :寫入數據的目的地址
//DataToWrite:要寫入的數據
void AT24C16_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);
}