IIC詳解 1、I2C總線具有兩根雙向信號線,一根是數(shù)據(jù)線SDA,另一根是時(shí)鐘線SCL 2、IIC總線上可以掛很多設(shè)備:多個(gè)主設(shè)備,多個(gè)從設(shè)備(外圍 設(shè)備)。上圖中主設(shè)備是兩個(gè)單片機(jī),剩下的都是從設(shè)備。 3、多主機(jī)會(huì)產(chǎn)生總線裁決問題。當(dāng)多個(gè)主機(jī)同時(shí)想占用總線時(shí),企圖啟動(dòng)總線傳輸數(shù)據(jù),就叫做總線競爭。I2C通過總線仲裁,以決定哪臺(tái)主機(jī)控制總線 4、上拉電阻一般在4.7k~10k之間
5、每個(gè)接到I2C總線上的器件都有唯一的地址。主機(jī)與其它器件間的數(shù)據(jù)傳輸可以是由主機(jī)發(fā)送數(shù)據(jù)到其它器件,這時(shí)主機(jī) 即為發(fā)送器,總線上收數(shù)據(jù)的器件則為接收器。 6、I2C總線的數(shù)據(jù)傳送: (1)、數(shù)據(jù)位的有效性規(guī)定:
(2)、起始與終止信號:SCL為高期間, SDA : 由高到低,起始信號 SDA:由低到高,終止信號
7、起始信號和終止信號都是由主機(jī)發(fā)送的。在起始信號產(chǎn)生之后,總線就處于被占用的狀態(tài),在終止信號產(chǎn)生之后,總線就處于空閑狀態(tài)。 8、連接到I2C總線上的器件,若具有I2C總線的硬件接口,則很容易檢測到起始和終止信號。 9、每當(dāng)發(fā)送器傳輸完一個(gè)字節(jié)的數(shù)據(jù)之后,發(fā)送端會(huì)等待一定的時(shí)間,等接收方的應(yīng)答信號。接收端通過拉低SDA數(shù)據(jù)線,給發(fā)送端發(fā)送一個(gè)應(yīng)答信號,以提醒發(fā)送端我這邊已經(jīng)接受完成,數(shù)據(jù)可以繼續(xù)傳輸,接下來,發(fā)送端就可以繼續(xù)發(fā)送數(shù)據(jù)了。 10、數(shù)據(jù)傳送格式:主機(jī)發(fā)送給從機(jī)
11、I2C模擬方式 的特殊情況:
12、總線尋址: (1)、主機(jī)向從機(jī)發(fā)送8位數(shù)據(jù),這8位數(shù)據(jù)是在起始信號之后發(fā)送的第一個(gè)字節(jié),后面的字節(jié)都是數(shù)據(jù),不再是尋址,除非又重新來一個(gè)起始信號。
(2)、主機(jī)給從機(jī)發(fā)送第一個(gè)字節(jié)(總線尋址那個(gè)字節(jié)),若是讀命令,則從機(jī)接收到該 命令之后,主動(dòng)往主機(jī)發(fā)送數(shù)據(jù)。 (3)、主機(jī)發(fā)送地址時(shí),總線上的每個(gè)從機(jī)都將這7位地址碼與自己的地址進(jìn)行比較,若相同,則認(rèn)為自己正在被主機(jī)尋址,根據(jù)R/T位將自己確定為發(fā)送器和接收器 (4)、從機(jī)地址的確定:第0位是讀寫位。(如對于24C02這塊存儲(chǔ)器,它若作為從機(jī),那么它的地址中7~4位是固定的,更改不了,第3~1位是可以更改的,每一位根據(jù)硬件的管教連接來確定,連接高電平那就是1,低電平就是0)
13、在起始信號后必須傳送一個(gè)從機(jī)的地址(7位),第8位是數(shù)據(jù)的傳送方向位(R/T),用“0”表示主機(jī)發(fā)送數(shù)據(jù)(T),“1”表示主機(jī)接收數(shù)據(jù)(R)。 14、每次數(shù)據(jù)傳送總是由主機(jī)產(chǎn)生的終止信號來結(jié)束。但是,若主機(jī)希望繼續(xù)占用總線進(jìn)行新的數(shù)據(jù)傳送,則可以不產(chǎn)生終止信號,馬上再次發(fā)出起始信號對另一從機(jī)進(jìn)行尋址。 15、在總線的一次數(shù)據(jù)傳輸中,可以有一下幾種組合方式: (1)、主機(jī)向從機(jī)發(fā)送數(shù)據(jù),數(shù)據(jù)傳送方向在整個(gè)傳遞過程中不變:
(2)、主機(jī)在第一個(gè)字節(jié)后,立即從從機(jī)讀數(shù)據(jù)(傳輸方向不變):
(3)、在傳送過程中,當(dāng)需要改變傳遞方向時(shí),起始信號和從機(jī)地址都被重復(fù)一次產(chǎn)生一次,但兩次讀/寫方向位正好相反
16、時(shí)序:
注:主機(jī)做的都是編程控制,從機(jī)做的都是自主控制,也可以說是硬件控制,如主機(jī)給應(yīng)答信號是編程控制,但是從機(jī)給應(yīng)答信號是硬件控制,我們只需要檢查在SDA為高期間,SCL保持低電平一些時(shí)間,即可判定從機(jī)給了主機(jī)應(yīng)答信號。 17、模擬IIC編程 (1)、開引腳的時(shí)鐘:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); (2)、宏定義:
(3)、配置函數(shù) 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的這兩個(gè)模式: 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)、產(chǎn)生起始信號: 看上面的時(shí)序圖寫 void I2C_Start(void) //在SCL高電平,SDA由高到低,在此之前,SDA的高電平必須保持>4.7us,起始信號變成低電平之后,還要延時(shí)>4us { I2C_SDA_OUT(); //SDA是輸出方向,即由主機(jī)發(fā)送的 I2C_SDA_H; I2C_SCL_H; delay_us(5); //延時(shí)5個(gè)微妙 I2C_SDA_L; //起始信號 delay_us(5); I2C_SCL_L; } (6)、主機(jī)產(chǎn)生停止信號: 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)、主機(jī)產(chǎn)生應(yīng)答信號: 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)、主機(jī)不發(fā)送應(yīng)答信號: 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)、等待信號,當(dāng)發(fā)送器發(fā)送一個(gè)數(shù)據(jù)之后,需要等待從接收端發(fā)過來的應(yīng)答信號,主機(jī)等待從機(jī)應(yīng)答 u8 I2C_Wait_ACK(void) //SDA為低電平時(shí),表明從機(jī)給了應(yīng)答 { int time=0; //計(jì)數(shù)器 I2C_SDA_IN(); //表明是從機(jī)的SDA I2C_SDA_H; delay_us((1); I2C_SCL_H; delay_us(1); while(GPIO_ReadInputDataBit(GPIO_I2C,I2CC_SDA)) //等待應(yīng)答信號 { time++; if(time>250) //等待時(shí)間過長,產(chǎn)生停止信號,返回1,表示接收應(yīng)答失敗 { I2C_Stop(); return 1; } //應(yīng)答成功,則SCL變低 I2C_SCL_L; return 0; } } (10)、主機(jī)發(fā)送一個(gè)字節(jié)的數(shù)據(jù),從高位開始發(fā)送。SCL位高電平的時(shí)候,數(shù)據(jù)必須保持穩(wěn)定,所以可以在SCL為低電平時(shí)組織數(shù)據(jù),SCL為高電平時(shí)發(fā)送或者接收數(shù)據(jù) void send_Byte(u8 data) { I2C_SDA_OUT(); //數(shù)據(jù)準(zhǔn)備 I2C_SCL_L; delay_us(2); for(int i=0;i<8;i++) //從高位開始一位一位地傳送 { //發(fā)數(shù)據(jù)放到數(shù)據(jù)線上 if((data & 0x80)>0) //當(dāng)前的最高位為1 I2C_SDA_H; //拉高數(shù)據(jù)線 else I2C_SDA_L; data<<1; //數(shù)據(jù)左移一位 //開始發(fā)送數(shù)據(jù) I2C_SCL_H; delay_us(2); //上一個(gè)數(shù)據(jù)發(fā)送完畢,為下一個(gè)數(shù)據(jù)發(fā)送準(zhǔn)備 I2C_SCL_L; delay_us(2); } } (11)、主機(jī)接收一個(gè)字節(jié)數(shù)據(jù) u8 rev_Byte(u8 ack) { u8 rev_Data; //接收到的數(shù)據(jù) I2C_SDA_IN(); for(int i=0;i<8;i++) { //數(shù)據(jù)準(zhǔn)備 I2C_SCL_L; delay_us(2); I2C_SCL_H; //主機(jī)開始讀數(shù)據(jù),從機(jī)不能再改變數(shù)據(jù)了,即改變SDA的電平 if(GPIO_ReadInputDataBit(GPIO_I2C,I2CC_SDA) ) //接收到的是1 rev_Data++; rev_Data<<1; delay_us(1); } if(ack==0) //說明主機(jī)不需要給從機(jī)應(yīng)答 I2C_NACK(); else //主機(jī)需要給應(yīng)答 I2C_ACK(); return rec_Data; } |
|