IIC總線 1 IIC總線概述(文檔有圖)I2C總線兩線制包括:串行數(shù)據(jù)SDA(Serial Data)、串行時鐘SCL(Serial Clock)。總線必須由主機(jī)(通常為微控制器)控制,主機(jī)產(chǎn)生串行時鐘(SCL)控制總線的傳輸方向,并產(chǎn)生起始和停止條件。 IIC總線特征:同步串行半雙工(同一時刻只能是一種身份) 2 IIC總線拓?fù)鋱D SDA:雙向串行數(shù)據(jù)線,數(shù)據(jù)是一位一位傳輸,既可以從主機(jī)發(fā)送到從機(jī),也可以從從機(jī)發(fā)送到主機(jī) SCL:時鐘線(單向),驅(qū)動數(shù)據(jù)線SDA的信號由時鐘線SCL提供,只能由主機(jī)發(fā)送,從機(jī)接收 主機(jī):主機(jī)產(chǎn)生串行時鐘(SCL)控制總線的傳輸方向,并產(chǎn)生起始條件(占用總線)和停止條件(釋放總線) 從機(jī):從機(jī)也能發(fā)送數(shù)據(jù)給主機(jī),但是從機(jī)永遠(yuǎn)不會主動給主機(jī)發(fā)送數(shù)據(jù)。 主機(jī)是主宰 主機(jī)是如何找到從機(jī)來進(jìn)行通信的呢? 2.1 主從設(shè)備通信主機(jī)如何能找到對應(yīng)的從機(jī)與其進(jìn)行通信? 每個從機(jī)都有一個唯一的器件地址,主機(jī)就是通過這個器件地址去找到對應(yīng)的從機(jī)與其通信。 器件地址誰分配?如何分配?(具體查看模塊手冊) 在IIC總線上,從機(jī)的器件地址可以為7位或者10位,一般情況下都是7位器件地址。 在器件地址中包含了固定地址(在高位,不可變)和可編程地址(在低位,可變) 器件地址的位數(shù)是由廠家決定 固定地址的位數(shù)和內(nèi)容也是由廠家決定 可編程地址的位數(shù)由廠家決定 可編程地址的內(nèi)容由使用者決定 3 IIC數(shù)據(jù)幀 UART數(shù)據(jù)幀格式:起始位+數(shù)據(jù)位(5~8)+校驗位+停止位。 IC數(shù)據(jù)幀格式:起始條件+數(shù)據(jù)位(8位)+應(yīng)答位+停止條件 起始條件:一次通信的開始(主機(jī)占用總線) 數(shù)據(jù)位:從發(fā)送器到接收器,連續(xù)的8位數(shù)據(jù) 應(yīng)答位:當(dāng)接收器成功接收到發(fā)送器的8位數(shù)據(jù)后,必須應(yīng)答。0代表應(yīng)答,1代表非應(yīng)答。 停止條件:一次通信的結(jié)束(主機(jī)釋放總線,雙線電平拉高) 4 標(biāo)準(zhǔn)IIC協(xié)議空閑狀態(tài) 開始信號 停止信號 應(yīng)答信號 數(shù)據(jù)的有效性 數(shù)據(jù)傳輸 4.1 空閑狀態(tài)(都拉高,數(shù)據(jù)比時鐘快)空閑狀態(tài):此時各個器件的輸出級場效應(yīng)管均處在截止?fàn)顟B(tài),即釋放總線,由兩條信號線各自的上拉電阻把電平拉高。
4.2 起始條件(C高D變低)和停止信號(C高D變高、都高) 起始信號:當(dāng)SCL為高期間,SDA由高到低的跳變;啟動信號是一種電平跳變時序信號,而不是一個電平信號。 停止信號:當(dāng)SCL為高期間,SDA由低到高的跳變;停止信號也是一種電平跳變時序信號,而不是一個電平信號。 起始條件偽代碼 SCL=1 SDA=1 //起始前都是高電平 //延時,起始條件建立時間 SDA=0//SDA變低,產(chǎn)生起始條件 //延時,起始條件的保持時間 SCL=0//一個周期的結(jié)束 | 停止條件偽代碼 SCL=1 SDA=0//低 //延時,停止條件建立時間 SDA=1//SDA變高 產(chǎn)生停止條件 //延時,本次停止條件到下一個起始條件的時間間隔 | 4.3 位傳輸(C低D準(zhǔn)備數(shù)據(jù),C拉高讀取D)SCL串行時鐘的配合下,在SDA上逐位地串行傳送每一位數(shù)據(jù)。數(shù)據(jù)的準(zhǔn)備是在SCL的低電平,數(shù)據(jù)位的傳輸是上邊沿觸發(fā)。 拉低準(zhǔn)備數(shù)據(jù),拉高采集數(shù)據(jù) 主機(jī)發(fā)送一位數(shù)據(jù)給從機(jī):(主機(jī)輸出) SCL=0//主機(jī)拉低時鐘線 SDA=0/1//主機(jī)在總線上準(zhǔn)備數(shù)據(jù) //延時,讓數(shù)據(jù)穩(wěn)定在數(shù)據(jù)線上 SCL=1//主機(jī)拉高時鐘線 從機(jī)在時鐘上升沿從總線上采集數(shù)據(jù) //延時,給時間從機(jī)采集數(shù)據(jù) | 主機(jī)讀取從機(jī)發(fā)送的一位數(shù)據(jù):(主機(jī)輸入) SCL=0//主機(jī)拉低時鐘線 從機(jī)在總線上準(zhǔn)備數(shù)據(jù)(從機(jī)自動進(jìn)行,主機(jī)不動作) //延時,讓數(shù)據(jù)穩(wěn)定在數(shù)據(jù)線上 SCL=1//主機(jī)拉高時鐘線 主機(jī)讀取SDA//主機(jī)在時鐘上升沿從總線上采集數(shù)據(jù) //延時,給時間主機(jī)機(jī)采集數(shù)據(jù) | 4.4 應(yīng)答位(第九位發(fā)送高(不應(yīng)答)低(應(yīng)答)) 發(fā)送器每發(fā)送一個字節(jié),就在時鐘脈沖9期間釋放數(shù)據(jù)線,由接收器反饋一個應(yīng)答信號。 應(yīng)答信號為低電平時,規(guī)定為有效應(yīng)答位(ACK簡稱應(yīng)答位),表示接收器已經(jīng)成功地接收了該字節(jié); 應(yīng)答信號為高電平時,規(guī)定為非應(yīng)答位(NACK),一般表示接收器接收該字節(jié)沒有成功 主機(jī)讀取從機(jī)的應(yīng)答:(主機(jī)讀取一位數(shù)據(jù)) 主機(jī)每發(fā)送1個字節(jié)給從機(jī)后,都必須通過這個應(yīng)答位查看從機(jī)是否能正常收到,如果一旦讀到的是非應(yīng)答信號(‘1’),表明沒有正常接收到當(dāng)前字節(jié)數(shù)據(jù),通信就要終止(主機(jī)發(fā)送停止信號) SCL=0//主機(jī)拉低時鐘線 (還是拉低給數(shù)據(jù),拉高采集數(shù)據(jù)) 從機(jī)根據(jù)自己接受的情況,給不給主機(jī)應(yīng)答信號 //延時,讓數(shù)據(jù)穩(wěn)定在數(shù)據(jù)線上 SCL=1//主機(jī)拉高時鐘線 主機(jī)讀取SDA//主機(jī)在時鐘上升沿從總線上采集應(yīng)答信號 //延時,給時間主機(jī)機(jī)采集數(shù)據(jù) 如果采集到的0,表示有應(yīng)答,如果采集到的是1,表示非應(yīng)答 |
主機(jī)發(fā)送一個應(yīng)答給從機(jī):(主機(jī)發(fā)送一位數(shù)據(jù)) 主機(jī)每讀取完從機(jī)發(fā)送過來的一個字節(jié)數(shù)據(jù)后,都必須給從機(jī)一個應(yīng)答信號。如果主機(jī)讀取完當(dāng)前字節(jié)后還想從機(jī)繼續(xù)給它發(fā)下一個字節(jié)數(shù)據(jù),就要給從機(jī)應(yīng)答(‘0’),如果主機(jī)讀取完當(dāng)前字節(jié)后不想從機(jī)再給它發(fā)數(shù)據(jù),那么主機(jī)發(fā)送非應(yīng)答信號(‘1’)給從機(jī)。 SCL=0//主機(jī)拉低時鐘線 SDA=0/1//主機(jī)根據(jù)自己的情況,決定給不給應(yīng)答從機(jī) //延時,讓數(shù)據(jù)穩(wěn)定在數(shù)據(jù)線上 SCL=1//主機(jī)拉高時鐘線 從機(jī)在時鐘上升沿從總線上采集應(yīng)答位 //延時,給時間從機(jī)采集數(shù)據(jù) |
5 IIC的尋址方式 器件地址(8位)組成:7位從設(shè)備地址+1位方向位 從設(shè)備地址包含了固定地址和可編程地址 方向位決定有效數(shù)據(jù)位的傳輸方向,主機(jī)---》從機(jī)(主機(jī)寫) 還是 從機(jī)----》主機(jī)(主機(jī)讀) 5.1 IIC一次完整通信1. 主機(jī)發(fā)送起始條件(主機(jī)占用總線,喚醒總線所有的從機(jī)) 2. 主機(jī)發(fā)送器件地址(總線上所有的從機(jī)就會拿這個器件跟自身進(jìn)行比較,匹配成功的那個從機(jī)就會回復(fù) 一個應(yīng)答信號給主機(jī),并根據(jù)方向位來決定數(shù)據(jù)傳輸方向) 3. 進(jìn)行有效數(shù)據(jù)交流(每傳輸完一個直接數(shù)據(jù)都要給應(yīng)答) 4. 主機(jī)發(fā)送停止信號(釋放總線,結(jié)束本次通信) 5.2 IIC三種通信方式只讀主機(jī)只讀取數(shù)據(jù) 只寫:主機(jī)僅發(fā)送數(shù)據(jù) 有讀有寫 6 GPIO口模擬IIC通信協(xié)議GPIO初始化 作為SCL的GPIO口:時鐘線SCL只能由主機(jī)(MCU)發(fā)出,SCL既有低電平也有高電平,所以這個GPIO口可以配置成推挽輸出,另外總線結(jié)構(gòu)本來就有上拉電阻,所以也可以配置成開漏輸出。 作為SDA的GPIO口:數(shù)據(jù)線SDA是雙向的,有時候需要從MCU發(fā)送,有時候又要輸入到MCU里。剛好,在M4里面,當(dāng)GPIO口配置成輸出模式時,輸入電路并沒有被關(guān)閉。但是,當(dāng)在采集輸入信號的時候,IO口的輸出電路就很有可能會影響到輸入信號的采集,所以必須要配置成開漏輸出,在讀取輸入信號前輸出‘1’,目的是讓輸出電路從IO口中斷開。 準(zhǔn)備數(shù)據(jù)要延時,讀取數(shù)據(jù)也要延時 起始條件 void IIC_Start(void) { IIC_SCL=1; IIC_SDA_OUT=1; Systick_Delay_us(1);//延時,起始條件建立時間 IIC_SDA_OUT=0;//產(chǎn)生起始條件 Systick_Delay_us(2);//延時,起始條件的保持時間 IIC_SCL=0;//一個周期的結(jié)束 } |
停止條件 void IIC_Stop(void) { IIC_SCL=1; IIC_SDA_OUT=0; Systick_Delay_us(1);//延時,停止條件建立時間 IIC_SDA_OUT=1;//產(chǎn)生停止條件 Systick_Delay_us(1);//延時,本次停止條件到下一個起始條件的時間間隔 } |
主機(jī)發(fā)送應(yīng)答 void IIC_Send_ACK(u8 ack) { IIC_SCL=0;//主機(jī)拉低時鐘線 if(ack)//主機(jī)根據(jù)自己的情況,決定給不給應(yīng)答從機(jī) { IIC_SDA_OUT=1; } else { IIC_SDA_OUT=0; } Systick_Delay_us(2);//延時,讓數(shù)據(jù)穩(wěn)定在數(shù)據(jù)線上 IIC_SCL=1;//主機(jī)拉高時鐘線,從機(jī)在時鐘上升沿從總線上采集應(yīng)答位 Systick_Delay_us(1);//延時,給時間從機(jī)采集數(shù)據(jù) } |
主機(jī)讀取應(yīng)答 u8 IIC_Revice_Ack(void) { u8 ack=0; IIC_SCL=0;//主機(jī)拉低時鐘線 IIC_SDA_OUT=1;//切換成讀模式---讓輸出電路從IO口斷開************************* //從機(jī)根據(jù)自己接受的情況,給不給主機(jī)應(yīng)答信號 Systick_Delay_us(2);//延時,讓數(shù)據(jù)穩(wěn)定在數(shù)據(jù)線上 IIC_SCL=1;//主機(jī)拉高時鐘線 if(IIC_SDA_IN)//主機(jī)在時鐘上升沿從總線上采集應(yīng)答信號 { ack=1; } Systick_Delay_us(1);//延時,給時間主機(jī)機(jī)采集數(shù)據(jù) IIC_SCL=0;//完整周期 return ack; } |
主機(jī)發(fā)送一個字節(jié)數(shù)據(jù)給從機(jī) u8 IIC_Send_Byte(u8 data) { u8 i; for(i=0;i<8;i++) { IIC_SCL=0;//主機(jī)拉低時鐘線 //主機(jī)在總線上準(zhǔn)備數(shù)據(jù) if(data&0x80) IIC_SDA_OUT=1; else IIC_SDA_OUT=0; Systick_Delay_us(2);//延時,讓數(shù)據(jù)穩(wěn)定在數(shù)據(jù)線上 IIC_SCL=1;//主機(jī)拉高時鐘線 //從機(jī)在時鐘上升沿從總線上采集數(shù)據(jù) Systick_Delay_us(1);//延時,給時間從機(jī)采集數(shù)據(jù) data<<=1;//讓次高位成為最高位 } return IIC_Revice_Ack( ); } |
主機(jī)讀取從機(jī)的一個字節(jié)數(shù)據(jù) u8 IIC_Revice_Byte(u8 ack) { u8 i; u8 data=0; for(i=0;i<8;i++) { IIC_SCL=0;//主機(jī)拉低時鐘線 IIC_SDA_OUT=1;//切換成讀模式---讓輸出電路從IO口中斷開*************************** //從機(jī)在總線上準(zhǔn)備數(shù)據(jù) Systick_Delay_us(2);//延時,讓數(shù)據(jù)穩(wěn)定在數(shù)據(jù)線上 IIC_SCL=1;//主機(jī)拉高時鐘線 data<<=1;//空出最低位來接受數(shù)據(jù) //主機(jī)在時鐘上升沿從總線上采集數(shù)據(jù) if(IIC_SDA_IN) { data |=1; } Systick_Delay_us(1);//延時,給時間主機(jī)機(jī)采集數(shù)據(jù) } IIC_Send_ACK(ack); return data; } |
7 IIC驅(qū)動溫濕度傳感器 7.1 SHT20命令 7.2 SHT20測量時序 7.3 示例代碼 float Read_SHT20_Data(u8 cmd) { u8 ack; u16 data=0; float DATA; IIC_Start( );//起始信號 ack = IIC_Send_Byte(SHT20_ADDR&0XFE);//發(fā)送器件地址+寫方向 if(ack)//沒有應(yīng)答 { IIC_Stop( ); return -1; } ack = IIC_Send_Byte(cmd);//發(fā)送測量命令 if(ack)//沒有應(yīng)答,等待從機(jī)應(yīng)答 { IIC_Stop( ); return -1; } do { Delay_ms(10);//給時間測量 IIC_Start( ); //開始信號,測量中 ack = IIC_Send_Byte(SHT20_ADDR | 0x01);//發(fā)送器件地址+讀方向 }while(ack);//沒有應(yīng)答則繼續(xù)詢問,知道有應(yīng)答,表明測量結(jié)束 data |= IIC_Revice_Byte(0) <<8;//高位結(jié)果 data |= IIC_Revice_Byte(1) ; //低位結(jié)果 IIC_Stop( ); //------數(shù)字信號轉(zhuǎn)換成模擬信號 data &=0xFFFC;//清除兩位狀態(tài)位 if(cmd==T_MEASURE) { DATA=-46.85+175.72*data/65536.0; } else if(cmd==RH_MEASURE) { DATA=-6.0+125.0*data/65536.0; } return DATA; } |
|