(3)、在傳送過程中,當需要改變傳遞方向時,起始信號和從機地址都被重複一次產生一次,但兩次讀/寫方向位正好相反
16、時序:
注:主機做的都是編程控制,從機做的都是自主控制,也可以說是硬件控制,如主機給應答信號是編程控制,但是從機給應答信號是硬件控制,我們只需要檢查在SDA為高期間,SCL保持低電平一些時間,即可判定從機給了主機應答信號。
17、模擬IIC編程
(1)、開引腳的時鐘:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
(2)、宏定義:
#define I2C_SCL GPIO_Pin_6#define I2C_SDA GPIO_Pin_7#define GPIO_I2C GPIOB#define I2C_SCL_H GPIO_SetBits(GPIO_I2C, I2C_SCL) //把PB6置高#define I2C_SCL_L GPIO_ResetBits(GPIO_I2C, I2C_SCL) //把PB6置低#define I2C_SDA_H GPIO_ResetBits(GPIO_I2C, I2C_SDA) //把PB7置高#define I2C_SCL_L GPIO_ResetBits(GPIO_I2C, I2C_SDA) //把PB7置低
(3)、配置函數
void I2C_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=I2C_SCL | I2C_SDA;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //推輓輸出模式
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
(4)、SDA有輸出方向和輸入方向,配置SDA的這兩個模式:
void I2C_OUT(void) //SDA是輸出方向
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=I2C_SDA;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //推輓輸出模式
GPIO_Init(GPIOB,&GPIO_InitStructure);
I2C_SCL_H;
I2C_SDA_H; //把兩條線都變成高電平
}
void I2C_IN(void) //SDA是輸入方向
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=I2C_SDA;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_IPU; //輸入上拉模式
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
(5)、產生起始信號: 看上面的時序圖寫
void I2C_Start(void) //在SCL高電平,SDA由高到低,在此之前,SDA的高電平必須保持>4.7us,起始信號變成低電平之後,還要延時>4us
{
I2C_SDA_OUT(); //SDA是輸出方向,即由主機發送的
I2C_SDA_H;
I2C_SCL_H;
delay_us(5); //延時5個微妙
I2C_SDA_L; //起始信號
delay_us(5);
I2C_SCL_L;
}
(6)、主機產生停止信號:
void I2C_Stop(void)
{
I2C_SDA_OUT();
I2C_SCL_L;
I2C_SDA_L;
I2C_SCL_H;
delay_us(5);
I2C_SDA_H;
delay_us(5);
}
(7)、主機產生應答信號:
void I2C_ACK(void)
{
I2C_SDA_OUT();
I2C_SCL_L;
I2C_SDA_L;
delay_us(2);
I2C_SCL_H;
delay_us(5);
I2C_SCL_L;
// I2C_SDA_H;
}
(8)、主機不發送應答信號:
void I2C_NACK(void)
{
I2C_SDA_OUT();
I2C_SCL_L;
I2C_SDA_H;
delay_us(2);
I2C_SCL_H;
delay_us(5);
I2C_SCL_L;
}
(9)、等待信號,當發送器發送一個數據之後,需要等待從接收端發過來的應答信號,主機等待從機應答。
u8 I2C_Wait_ACK(void) //SDA為低電平時,表明從機給了應答
{
int time=0; //計數器
I2C_SDA_IN(); //表明是從機的SDA
I2C_SDA_H;
delay_us((1);
I2C_SCL_H;
delay_us(1);
while(GPIO_ReadInputDataBit(GPIO_I2C,I2CC_SDA)) //等待應答信號
{
time++;
if(time>250) //等待時間過長,產生停止信號,返回1,表示接收應答失敗
{
I2C_Stop();
return 1;
}
//應答成功,則SCL變低
I2C_SCL_L;
return 0;
}
}
(10)、主機發送一個字節的數據,從高位開始發送。SCL位高電平的時候,數據必須保持穩定,所以可以在SCL為低電平時組織數據,SCL為高電平時發送或者接收數據
void send_Byte(u8 data)
{
I2C_SDA_OUT();
//數據準備
I2C_SCL_L;
delay_us(2);
for(int i=0;i<8;i++) //從高位開始一位一位地傳送
{
//發數據放到數據線上
if((data & 0x80)>0) //當前的最高位為1
I2C_SDA_H; //拉高數據線
else
I2C_SDA_L;
data<<1; //數據左移一位
//開始發送數據
I2C_SCL_H;
delay_us(2);
//上一個數據發送完畢,為下一個數據發送準備
I2C_SCL_L;
delay_us(2);
}
}
(11)、主機接收一個字節數據
u8 rev_Byte(u8 ack)
{
u8 rev_Data; //接收到的數據
I2C_SDA_IN();
for(int i=0;i<8;i++)
{
//數據準備
I2C_SCL_L;
delay_us(2);
I2C_SCL_H; //主機開始讀數據,從機不能再改變數據了,即改變SDA的電平
if(GPIO_ReadInputDataBit(GPIO_I2C,I2CC_SDA) ) //接收到的是1
rev_Data++;
rev_Data<<1;
delay_us(1);
}
if(ack==0) //說明主機不需要給從機應答
I2C_NACK();
else //主機需要給應答
I2C_ACK();
return rec_Data;
}
閱讀更多 淡定丶理性 的文章