IIC詳解,包括原理、過程,最後一步步教你實現IIC之三

(3)、在傳送過程中,當需要改變傳遞方向時,起始信號和從機地址都被重複一次產生一次,但兩次讀/寫方向位正好相反

IIC詳解,包括原理、過程,最後一步步教你實現IIC之三

16、時序:

IIC詳解,包括原理、過程,最後一步步教你實現IIC之三

注:主機做的都是編程控制,從機做的都是自主控制,也可以說是硬件控制,如主機給應答信號是編程控制,但是從機給應答信號是硬件控制,我們只需要檢查在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;

}


分享到:


相關文章: