性无码一区二区三区在线观看,少妇被爽到高潮在线观看,午夜精品一区二区三区,无码中文字幕人妻在线一区二区三区,无码精品国产一区二区三区免费

liuxiaofei126
認證:VIP會員
所在專題目錄 查看專題
多功能控制小車
STM32定時器的來龍去脈
底層程序如何在STM32上編譯運行
從IIC實測波形入手搞懂IIC通信
實例講解(一)移位寄存器
單片機之USB 硬件和數(shù)據(jù)的四種傳輸方式
作者動態(tài) 更多
stm32知識點總結(jié)
2021-11-24 23:12
工程師單片機LED項目
2021-11-11 07:46
電容型負載對跟隨器的影響
2021-10-28 23:33
圖騰柱和互補推挽兩個“小冤家”
2021-10-22 23:15
學(xué)習(xí)單片機的捷徑
2021-10-17 23:07

從IIC實測波形入手搞懂IIC通信

1 IIC基礎(chǔ)知識

首先復(fù)習(xí)一下IIC基礎(chǔ)知識,這部分看不懂的請先帶著疑問,然后我們通過分析IIC的真實波形,這些疑問可能就豁然開朗了~

1.1 IIC是什么

IIC(Inter Integrated Circuit,集成電路總線)是一種由 PHILIPS 公司開發(fā)的兩線式串行總線,用于連接微控制器及其外圍設(shè)備。它是由數(shù)據(jù)線 SDA 和時鐘 SCL 構(gòu)成的串行總線,可發(fā)送和接收數(shù)據(jù)。在 CPU (單片機)與IIC模塊之間、IIC模塊與IIC模塊之間進行雙向傳送。

IIC的一些特點:

  • IIC是半雙工,而不是全雙工
  • IIC是真正的多主機總線,(對比SPI在每次通信前都需要把主機定死,而IIC可以在通訊過程中,改變主機),如果兩個或更多的主機同時請求總線,可以通過沖突檢測和仲裁防止總線數(shù)據(jù)被破壞
  • 起始和終止信號都是由主機發(fā)出的,連接到I2C總線上的器件,若具有I2C總線的硬件接口,則很容易檢測到起始和終止信號
  • 起始信號后必須發(fā)送一個7位從機地址+1位方向位,用“0”表示主機發(fā)送數(shù)據(jù)**,**“1”表示主機接收數(shù)據(jù)。
  • 每當(dāng)主機向從機發(fā)送完一個字節(jié)的數(shù)據(jù),主機總是需要等待從機給出一個應(yīng)答信號,以確認從機是否成功接收到了數(shù)據(jù)
  • 起始信號是必需的,結(jié)束信號和應(yīng)答信號,都可以不要

:實際使用中,一般是單片機作為主機,其它器件作為從機,單片機先向器件發(fā)送信息表示要讀取數(shù)據(jù),之后轉(zhuǎn)變傳輸方向,器件發(fā)送數(shù)據(jù)到單片機。

1.2 IIC物理連接

使用IIC通信的IIC器件有很多,比如陀螺儀加速度計MPU6050,EEPROM存儲芯片AT24C02等,通過IIC總線,可以與單片機之間進行數(shù)據(jù)傳輸。

  • IIC通信線只有只有兩根,數(shù)據(jù)線SDA的高低電平傳輸2進制的數(shù)據(jù),時鐘線SCL通過方波信號提供時鐘節(jié)拍
  • 多個IIC器件可以并聯(lián)在IIC總線上,每個器件有特定的地址,分時共享IIC總線
  • 實際使用IIC當(dāng)然還要連接電源以及共地哦

1.3 IIC時序

網(wǎng)上查找IIC的基礎(chǔ)知識,可能會搜到這樣的時序圖:

看起來好復(fù)雜的樣子,這時可能一部分人就放棄思考了。

1.3.1 IIC起始結(jié)束信號

好吧,換個簡單點的圖,你也可能會搜到這樣的圖:

這張圖看起來更簡單一些,描述了IIC的起始和停止條件:

  • 起始:時鐘線SCL為高時,數(shù)據(jù)線SDA由高到低
  • 停止:時鐘線SCL為高時,數(shù)據(jù)線SDA由低到高

注:SDA和SCL同時為高時,為IIC總線的空閑狀態(tài)

1.3.2 IIC應(yīng)答

再來看下面這張圖:

這表示IIC的應(yīng)答機制

  • 下面的波形:SCL,主機產(chǎn)生的時鐘脈沖
  • 上面的波形:SDA,主機發(fā)送的8位數(shù)據(jù)
  • 中間的波形:SDA,從機在第9個時鐘信號進行拉低回應(yīng),表示收到了主機發(fā)來的數(shù)據(jù),拉高則表示不應(yīng)答

注:實際上,上面和中間是同樣的SDA線,這里只是分開示意。因為IIC應(yīng)答是一種相互關(guān)系,單片機發(fā)數(shù)據(jù)給IIC器件,IIC器件要進行應(yīng)答,表示收到了數(shù)據(jù),同樣,單片機接收IIC器件的數(shù)據(jù)后,也要給IIC器件一個應(yīng)答。

既然發(fā)送完都需要對方回應(yīng),那什么時候使用不應(yīng)答呢?就是在讀取到本次數(shù)據(jù)后,如果不需要繼續(xù)讀取,則發(fā)送非應(yīng)答,對方以為你沒收到這次數(shù)據(jù),則就不會繼續(xù)發(fā)送了。

1.3.3 IIC完整傳輸時序

  • 開始標志(S)發(fā)出后,主設(shè)備會傳送一個7 位的Slave 地址,并且后面跟著一個第8位,稱為Read/Write 位。
  • R/W 位表示主設(shè)備是在接受從設(shè)備的數(shù)據(jù)還是在向其寫數(shù)據(jù)。
  • 然后,主設(shè)備釋放SDA 線,等待從設(shè)備的應(yīng)答信號(ACK)。每個字節(jié)的傳輸都要跟隨有一個應(yīng)答位。
  • 應(yīng)答產(chǎn)生時,從設(shè)備將SDA 線拉低并且在SCL 為高電平時保持低。
  • 數(shù)據(jù)傳輸以停止標志(P)結(jié)束,然后釋放總線。但主設(shè)備也可以產(chǎn)生重復(fù)的開始信號去操作另一臺從設(shè)備,而不發(fā)出結(jié)束標志。
  • 所有的SDA 信號變化都要在SCL 時鐘為低電平時進行,除了開始和結(jié)束標志

1.4 常用的數(shù)據(jù)收發(fā)方式(時序)

上面1.3小節(jié)是IIC的基礎(chǔ)時序,在實際使用中,一般是對某個IIC器件的某個寄存器進行讀寫操作,因此,對于寄存器的讀寫操作,還要遵循下面的組合時序邏輯。

1.4.1 寫一個字節(jié)

用于對IIC器件某個寄存器的配置,如對MPU6050的某些參數(shù)進行設(shè)置。

  • 寫寄存器時,主設(shè)備除了發(fā)出開始標志和地址位,還要加一個R/W 位,0 為寫,1 為讀
  • 在第9 個時鐘周期(高電平時),MPU6050 產(chǎn)生應(yīng)答信號
  • 主設(shè)備開始傳送寄存器地址,并接到應(yīng)答
  • 然后開始傳送寄存器數(shù)據(jù),仍然要有應(yīng)答信號
  • 最后主設(shè)備發(fā)送停止信號。

1.4.2 連續(xù)寫多個字節(jié)

對連續(xù)地址的寫入,這個用的較少。

通信時序與上面的“寫一個字節(jié)”類似,上面是寫一個字節(jié)后就停止了,若要連續(xù)寫,則繼續(xù)寫即可,只要可以收到從機Ack。

1.4.3 讀一個字節(jié)

用于讀取IIC器件某個寄存器的數(shù)值。

  • 首先由主設(shè)備產(chǎn)生開始信號,然后發(fā)送從設(shè)備地址位和一個寫數(shù)據(jù)位,等待應(yīng)答
  • 然后發(fā)送寄存器地址,才能開始讀寄存器
  • 收到應(yīng)答信號后,主設(shè)備再發(fā)一個開始信號,然后發(fā)送從設(shè)備地址位和一個讀數(shù)據(jù)位
  • 然后,作為從設(shè)備的MPU6050 產(chǎn)生應(yīng)答信號并開始發(fā)送寄存器中的數(shù)據(jù)
  • 通信以主設(shè)備產(chǎn)生的拒絕應(yīng)答信號(nACK)和結(jié)束標志(Stop)結(jié)束
  • 拒絕應(yīng)答信號(nACK)產(chǎn)生定義為SDA 數(shù)據(jù)在第9 個時鐘周期一直為高

1.4.4 連續(xù)讀多個字節(jié)

也是用于讀取IIC器件某個寄存器的數(shù)值,當(dāng)某些數(shù)據(jù)一位字節(jié)不夠表示,或有一組連續(xù)的數(shù)據(jù)需要讀時,可以使用該模式。

通信時序與上面的“讀一個字節(jié)”類似,上面是讀一個字節(jié)后就nAck叫停,若要連續(xù)寫,則發(fā)送Ack,直到不需要繼續(xù)讀時再回復(fù)nAck。

復(fù)習(xí)了這么多,之前對IIC懵懵懂懂的是否依然犯迷糊,好了,現(xiàn)在從理論進入實踐,看看真實的IIC是什么樣子。

2 初識IIC真實波形

下面這張圖是通過示波器抓取的IIC波形,可以看到:

  • 時鐘線SCL是一種間歇性的方波(需要通信時才產(chǎn)生方波)
  • 數(shù)據(jù)線SDA根據(jù)SCL提供的節(jié)拍,高電平代表數(shù)據(jù)1,低電平代表數(shù)據(jù)0
  • 沒有數(shù)據(jù)傳輸時,SDA和SCL均為高電平狀態(tài)
  • 起始信號后,數(shù)據(jù)是9個一組,包括8位的數(shù)據(jù)和另一方的1位回應(yīng)
  • 圖中紅色數(shù)字表示單片機發(fā)送的8位數(shù)據(jù),黃色數(shù)字表示IIC器件回應(yīng)的信號,低電平0表示器件收到了單片機發(fā)來的數(shù)據(jù)。
  • 現(xiàn)在對IIC波形有沒有多了一些直觀的認識?下面再進入編程階段,看看程序是怎么控制這兩根線的。

    3 編寫IIC通信函數(shù)

    IIC通信可以使用單片機自帶的硬件IIC,它提供了固定的引腳接口和函數(shù)庫。也可以自己通過軟件編寫來實現(xiàn)IIC時序,這時就可以任選引腳,也方便其它硬件平臺的移植。

    下面通過軟件IIC的編寫,從軟件角度理解IIC通信邏輯。

    以下函數(shù)都是單片機在執(zhí)行,即主機發(fā)出的動作,所以一定要從單片機的角度思考哦~

    另外,不要看到程序就匆匆掠過,為幫助理解,我對代碼進行了一定的注解,仔細分析每條代碼,想想與IIC的邏輯如何對應(yīng)起來,IIC邏輯還沒懂的,讀完本篇,分析過真實的IIC波形后,再來看看代碼,會有不一樣的體會。

    3.1 起始IIC_Start()

    //==================================
    //產(chǎn)生IIC起始信號
    //==================================
    void IIC_Start(void)
    {
    	SDA_OUT();   //sda線輸出
    	IIC_SDA=1;	  
    	delay_us(2);	
    	IIC_SCL=1;   //時鐘線為高時
    	delay_us(2);
     	IIC_SDA=0;   //數(shù)據(jù)線由高到低
    	delay_us(4);
    	IIC_SCL=0;   //時鐘線拉低,鉗住IIC總線,準備發(fā)送數(shù)據(jù) 
    }
    

    最后一句SCL拉低,然后就準備產(chǎn)生時鐘信號,發(fā)送數(shù)據(jù)了。

    3.2 停止IIC_Stop()

    //==================================
    //產(chǎn)生IIC停止信號
    //==================================
    void IIC_Stop(void)
    {
    	SDA_OUT();  //sda線輸出
    	IIC_SCL=0;  //確保時鐘線為低時,數(shù)據(jù)線才能變化為0,否則這就可能成起始信號了!
    	delay_us(2);
    	IIC_SDA=0;
     	delay_us(2);
    	IIC_SCL=1;  //時鐘線為高時
    	IIC_SDA=1;  //數(shù)據(jù)線由低到高
    	delay_us(4);							   	
    }
    

    停止前也要確保SCL是拉低的狀態(tài)。

    最后SDA和SCL都為高,即釋放IIC總線,IIC總線進入空閑狀態(tài)。

    3.3 等待應(yīng)答IIC_wait_Ack()

    //==================================
    //等待應(yīng)答信號到來
    //用于發(fā)送模式下,發(fā)送8位后,等待器件應(yīng)答第9位
    //返回值:1,接收應(yīng)答失敗
    //        0,接收應(yīng)答成功
    //==================================
    u8 IIC_Wait_Ack(void)
    {
    	u8 ucErrTime=0;
    	SDA_IN();                 //SDA設(shè)置為輸入  
    	IIC_SDA=1;delay_us(1);	  //SDA先拉高,若被從機拉低則說明收到應(yīng)答信號
    	IIC_SCL=1;delay_us(1);	  //SCL拉高,產(chǎn)生第9位的脈沖
    	while(READ_SDA)
    	{
    		ucErrTime++;
    		if(ucErrTime>250)
    		{
    			IIC_Stop();
    			return 1;
    		}
    	}
    	IIC_SCL=0;//時鐘輸出0 	  //SCL拉低,結(jié)束第9位的脈沖 
    	return 0;  
    } 
    

    在一定是時間內(nèi)檢測SDA是否被從機拉低,被拉低則說明從機收到了數(shù)據(jù)。

    3.4 產(chǎn)生應(yīng)答IIC_Ack()

    //==================================
    //產(chǎn)生ACK應(yīng)答
    //用于讀取模式(SDA為in)讀了8位器件數(shù)據(jù)后,在第9位給出一個應(yīng)答,我還要繼續(xù)讀
    //==================================
    void IIC_Ack(void)
    {
    	IIC_SCL=0;    //確保時鐘線為低時,數(shù)據(jù)線才能變化為0,否則這就可能成起始信號了!
    	SDA_OUT();    //SDA由讀取改為發(fā)送
    	delay_us(2);
    	IIC_SDA=0;    //拉低SDA,表示應(yīng)答
    	delay_us(2);
    	IIC_SCL=1;    //SCL先上升
    	delay_us(2);
    	IIC_SCL=0;    //SCL再下降,形成一個脈沖,應(yīng)答才生效
    }
    

    單片機在接收器件數(shù)據(jù)后,進行回應(yīng),表示接收到了器件的數(shù)據(jù)。

    該函數(shù)用在連續(xù)讀取多個字節(jié)時,每讀完一個字節(jié)(8位),產(chǎn)生回應(yīng),表示還要進行讀,這時器件就可以繼續(xù)發(fā)數(shù)據(jù)了。

    當(dāng)單片機不需要繼續(xù)讀,如連續(xù)讀的最后一個字節(jié),或只讀一個字節(jié),單片機發(fā)送非應(yīng)答信號,這時器件以為單片機沒有收到數(shù)據(jù),接下來就不會再發(fā)數(shù)據(jù)了。

    非應(yīng)答函數(shù)如下,就是拉高SDA:

    3.5 不產(chǎn)生應(yīng)答IIC_nAck()

    //==================================
    //不產(chǎn)生ACK應(yīng)答	
    //用于讀取模式(SDA為in)讀了8位器件數(shù)據(jù)后,在第9位給出一個應(yīng)答,我不想讀了
    //==================================
    void IIC_NAck(void)
    {
    	IIC_SCL=0;    //確保時鐘線為低時,數(shù)據(jù)線才能變化為0,否則這就可能成起始信號了!
    	SDA_OUT();    //SDA由讀取改為發(fā)送
    	IIC_SDA=1;    //拉高SDA,表示不應(yīng)答
    	delay_us(2);
    	IIC_SCL=1;    //SCL先上升
    	delay_us(2);
    	IIC_SCL=0;    //SCL再下降,形成一個脈沖,不應(yīng)答才生效
    }
    

    3.6 IIC發(fā)送一個字節(jié)

    //==================================
    //IIC發(fā)送一個字節(jié)
    //返回從機有無應(yīng)答
    //1,有應(yīng)答
    //0,無應(yīng)答	
    //==================================
    void IIC_Send_Byte(u8 txd)
    {                        
        u8 t;   
    	SDA_OUT(); 	               //SDA發(fā)送模式
        IIC_SCL=0;                 //拉低時鐘開始數(shù)據(jù)傳輸
        for(t=0;t<8;t++)
        {              
            IIC_SDA=(txd&0x80)>>7; //SDA高低電平表示數(shù)據(jù)1和0
            txd<<=1; 	  
            delay_us(2);           //對TEA5767這三個延時都是必須的
            IIC_SCL=1;             //SCL先上升
            delay_us(2); 
            IIC_SCL=0;	           //SCL再下降,形成一個脈沖,發(fā)送一位數(shù)據(jù)生效
            delay_us(2);
        }	 
    } 
    

    發(fā)送一個字節(jié),就是分8次循環(huán),產(chǎn)生8個時鐘信號,并將SDA賦值為0或1。

    3.7 IIC讀取一個字節(jié)

    //==================================
    //讀1個字節(jié)
    //ack=1時,發(fā)送ACK,ack=0,發(fā)送nACK  
    //==================================
    u8 IIC_Read_Byte(unsigned char ack)
    {
    	unsigned char i,receive=0;
    	SDA_IN();                  //SDA輸入模式
        for(i=0;i<8;i++ )
    	{
            IIC_SCL=0;             //SCL先下降,通過循環(huán),形成時鐘脈沖
            delay_us(2);
    		IIC_SCL=1;             //SCL上升
            receive<<=1;
            if(READ_SDA)
    			receive++;         //讀取并組合記錄數(shù)據(jù),++表示讀到1了,最低位置1
    		delay_us(1); 
        }	
        //讀取8位后,主機需要變?yōu)榘l(fā)送模式,在第9位進行應(yīng)答或不應(yīng)答
        //此時CLK還是高電平狀態(tài),不過下面的應(yīng)答會先將CLK拉低的	
        if (!ack)
    	{
    		//讀1個字節(jié),或讀多個字節(jié)讀到最后一個字節(jié)時,使用nACK
    		//然后配合使用IIC停止信號
            IIC_NAck();//發(fā)送nACK
    	}
        else
    	{
    		//讀多個字節(jié)還沒讀完時,使用ACK,表示現(xiàn)在讀的ok,還要繼續(xù)讀
            IIC_Ack(); //發(fā)送ACK 
    	}		
        return receive;
    }
    

    讀取一個字節(jié),也是分8次循環(huán),產(chǎn)生8個時鐘信號,并讀取SDA的高低電平信號,最后,根據(jù)要不要繼續(xù)讀下一個字節(jié),發(fā)送第9位的Ack或nACK。

    4 真實IIC波形詳細分析

    4.1 讀取從機數(shù)據(jù)(單字節(jié)讀)

    這張圖展示IIC讀某個器件的寄存器的一個字節(jié)的真實波形(注:實際是讀了2個不同寄存器的值,每個寄存器讀了1個字節(jié),所以,可以先只看前半部分哦~),我已對波形進行了詳細的注解。

    對照著圖,再來溫習(xí)一下各個信號的特點:

    • 起始信號:時鐘線SCL為高時,數(shù)據(jù)線SDA由高到低
    • 停止信號:時鐘線SCL為高時,數(shù)據(jù)線SDA由低到高
    • 數(shù)據(jù)信號:連續(xù)的8位,每一個SCL脈沖時鐘對應(yīng)的SDA,高電平為數(shù)據(jù)1,低電平為數(shù)據(jù)0
    • 應(yīng)答信號:第9位(數(shù)據(jù)信號后),由對方產(chǎn)生的回應(yīng),0為產(chǎn)生回應(yīng),1為不產(chǎn)生回應(yīng)

    這幅圖中,單片機先產(chǎn)生起始信號,然后發(fā)送7位器件地址+1位寫標志(綠色的0),并等待從機回應(yīng)(從機拉低SDA表示收到數(shù)據(jù)),接著發(fā)送8位寄存器地址,并等待從機回應(yīng)。然后,單片機先再次產(chǎn)生起始信號,發(fā)送7位器件地址+1位讀標志(綠色的1),并等待從機回應(yīng)。從機收到讀的信號后,從機開始發(fā)送8位數(shù)據(jù),主機接收到數(shù)據(jù)后,主機發(fā)送nAck不應(yīng)答信號(圖中的Ack(1),主機將SDA拉高,從機則認為主機剛才沒有收到它發(fā)送的數(shù)據(jù),從機將不再繼續(xù)發(fā)送),接著主機發(fā)送結(jié)束信號,讀取完成。

    此圖后半部分是以相同方式讀了另一個寄存器的值。

    上圖中,SCL信號都是由單片機產(chǎn)生,SDA信號由單片機和IIC器件(從機)共同產(chǎn)生,當(dāng)需要對IIC器件的寄存器寫時,單片機產(chǎn)生SDA數(shù)據(jù),當(dāng)需要讀取IIC器件的寄存器數(shù)據(jù)時,改變傳輸方向,IIC器件產(chǎn)生SDA數(shù)據(jù)。

    對于主機和從機什么時候控制SDA,還可以參考這個圖幫助理解:

    4.2 讀取從機數(shù)據(jù)(多字節(jié)讀)

    上面是單字節(jié)讀的波形,再來看看多字節(jié)的波形,前面的寫器件地址、寫寄存器地址1與單字節(jié)讀一樣,這張圖只顯示了后面不一樣的部分,主要區(qū)別在于單片機接收到數(shù)據(jù)1后,產(chǎn)生低電平的應(yīng)答,從而可以繼續(xù)讀取數(shù)據(jù)2。

    (注意,因為傳感器這次測得的數(shù)據(jù)不一樣,所以讀出的數(shù)據(jù)也不一樣哦~)

  • 注:以上的IIC真實波形,是使用是硬件IIC,自己編寫的軟件IIC測得的波形,可能在兩個信號的前后延時時間上稍有差別,但整體的時序邏輯肯定是一樣的。

    4.3 配置從機寄存器(單字節(jié)寫與多字節(jié)寫)

    對于寄存器的配置,也就是IIC的寫寄存器操作,我就不放圖了,參考上面的“常用的數(shù)據(jù)收發(fā)方式(時序)”以及上面的IIC讀寄存器的真實波形,IIC的寫寄存器的真實波形,應(yīng)該可以通過寫程序出來的,

聲明:本內(nèi)容為作者獨立觀點,不代表電子星球立場。未經(jīng)允許不得轉(zhuǎn)載。授權(quán)事宜與稿件投訴,請聯(lián)系:editor@netbroad.com
覺得內(nèi)容不錯的朋友,別忘了一鍵三連哦!
贊 12
收藏 15
關(guān)注 210
成為作者 賺取收益
全部留言
0/200
  • 小董 2021-04-06 19:51
    分析很到位
    回復(fù)
  • dy-blNlwnWV 2021-03-25 22:59
    對我很有幫助
    回復(fù)
  • 千里知外 2021-01-19 20:30
    非常清晰
    回復(fù)
  • 星球居民-YBPLIKJ1 2020-12-19 09:11
    非常實用的
    回復(fù)
  • lihui710884923 2020-12-16 22:05
    透徹
    回復(fù)
  • keyhei66 2020-12-16 21:57
    不錯
    回復(fù)
  • heiha88 2020-12-16 21:53
    大開眼界,真是好文
    回復(fù)