1. 程式人生 > >I2C協議(非原創,轉載他人,用於學習)

I2C協議(非原創,轉載他人,用於學習)

ret truct 等待 停止 def 失敗 ont .net 模擬

I2C協議:1、空閑狀態 2、開始信號 3、停止信號 4、應答信號 5、數據的有效性 6、數據傳輸

IIC詳解

1、I2C總線具有兩根雙向信號線,一根是數據線SDA,另一根是時鐘線SCL

技術分享圖片

2、IIC總線上可以掛很多設備:多個主設備,多個從設備(外圍 設備)。上圖中主設備是兩個單片機,剩下的都是從設備。

3、多主機會產生總線裁決問題。當多個主機同時想占用總線時,企圖啟動總線傳輸數據,就叫做總線競爭。I2C通過總線仲裁,以決定哪臺主機控制總線

4、上拉電阻一般在4.7k~10k之間
技術分享圖片

5、每個接到I2C總線上的器件都有唯一的地址。主機與其它器件間的數據傳輸可以是由主機發送數據到其它器件,這時主機 即為發送器,總線上收數據的器件則為接收器。

6、I2C總線的數據傳送:
(1)、數據位的有效性規定:
技術分享圖片

(2)、起始與終止信號:SCL為高期間,
SDA : 由高到低,起始信號
SDA:由低到高,終止信號
技術分享圖片

7、起始信號和終止信號都是由主機發送的。在起始信號產生之後,總線就處於被占用的狀態,在終止信號產生之後,總線就處於空閑狀態。

8、連接到I2C總線上的器件,若具有I2C總線的硬件接口,則很容易檢測到起始和終止信號。

9、每當發送器傳輸完一個字節的數據之後,發送端會等待一定的時間,等接收方的應答信號。接收端通過拉低SDA數據線,給發送端發送一個應答信號,以提醒發送端我這邊已經接受完成,數據可以繼續傳輸,接下來,發送端就可以繼續發送數據了。

10、數據傳送格式:主機發送給從機
技術分享圖片

11、I2C模擬方式 的特殊情況:
技術分享圖片

12、總線尋址:
(1)、主機向從機發送8位數據,這8位數據是在起始信號之後發送的第一個字節,後面的字節都是數據,不再是尋址,除非又重新來一個起始信號。
技術分享圖片

(2)、主機給從機發送第一個字節(總線尋址那個字節),若是讀命令,則從機接收到該 命令之後,主動往主機發送數據。

(3)、主機發送地址時,總線上的每個從機都將這7位地址碼與自己的地址進行比較,若相同,則認為自己正在被主機尋址,根據R/T位將自己確定為發送器和接收器

(4)、從機地址的確定:第0位是讀寫位。(如對於24C02這塊存儲器,它若作為從機,那麽它的地址中7~4位是固定的,更改不了,第3~1位是可以更改的,每一位根據硬件的管教連接來確定,連接高電平那就是1,低電平就是0)
技術分享圖片

13、在起始信號後必須傳送一個從機的地址(7位),第8位是數據的傳送方向位(R/T),用“0”表示主機發送數據(T),“1”表示主機接收數據(R)。

14、每次數據傳送總是由主機產生的終止信號來結束。但是,若主機希望繼續占用總線進行新的數據傳送,則可以不產生終止信號,馬上再次發出起始信號對另一從機進行尋址。

15、在總線的一次數據傳輸中,可以有一下幾種組合方式:

(1)、主機向從機發送數據,數據傳送方向在整個傳遞過程中不變:
技術分享圖片

(2)、主機在第一個字節後,立即從從機讀數據(傳輸方向不變):
技術分享圖片

(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;
}

---------------------
作者:shaguahaha
來源:CSDN
原文:https://blog.csdn.net/shaguahaha/article/details/70766665
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

I2C協議(非原創,轉載他人,用於學習)