GPIO模擬IIC通信(讀取MPU6050)
開發(fā)板:STM32F407VET? ? ?傳感器:正點原子MPU6050,IIC模塊
GPIO模擬IIC通信的時候,需要注意輸出一定要設(shè)置為開漏輸出,外部上拉。這個是GPIO的輸入輸出圖。因為開漏輸出可以進行線與操作。推挽輸出可能造成IO口短路,燒毀IO口。

首先了解IIC通信的時序,這里以MPU6050舉例子。

這個圖就是IIC的通信時序(所有的IIC設(shè)備都符合這個規(guī)則):我之前也寫了一個IIC筆記(可能也有點抽象)
起始信號:SCL高電平,SDA下降沿。停止信號:SCL高電平,SDA上升沿。
等待應(yīng)答:主設(shè)備寫一個數(shù)據(jù)之后,從設(shè)備需要應(yīng)答,主設(shè)備將SDA拉高,讀取SDA電平(由于有線與的操作),讀到低電平則認為子設(shè)備應(yīng)答了,可以繼續(xù)的發(fā)送數(shù)據(jù)。
主動應(yīng)答:主設(shè)備讀取一個數(shù)據(jù)之后,主動的將SDA拉低,從設(shè)備就知道你回應(yīng)了它,他就會繼續(xù)發(fā)送下一個字節(jié)的數(shù)據(jù)。如果SDA拉高,則從設(shè)備就會認為你不需要下一個數(shù)據(jù),這就是非應(yīng)答了。
非應(yīng)答:讀取一個數(shù)據(jù)后,主設(shè)備將SDA拉高,不管子設(shè)備了,然后就直接發(fā)送停止信號結(jié)束通信。
來看一下MPU6050的官方文檔給的讀寫時序圖

單字節(jié)的寫入用偽代碼的方式寫出來就長這樣子。
多字節(jié)的寫入,只是在寫入數(shù)據(jù)之后,主動的應(yīng)答(主動拉低SDA),就可以發(fā)送下一個數(shù)據(jù)。長度是沒有像限制的。(發(fā)送的長度是沒有限制的,所以每次等待應(yīng)答都要實際的讀取SDA輸入端的數(shù)據(jù),不要認為時序圖對了就可以了??赡軙l(fā)生一些意外)

從上圖可以看到,讀取數(shù)據(jù)比寫入稍微復(fù)雜一點點,但是按照時序圖,將時序依次完成就可以讀取到數(shù)據(jù)了。
下面是實際的代碼部分
首先是一些宏,這些宏可以使得代碼更簡潔直觀。(使用的標(biāo)準庫)
首先是初始化GPIO,GPIO一定要是開漏輸出,外部上拉!??!其他的沒有什么值得注意的
初始化GPIO之后,就可以根據(jù)時序,來寫程序了。
????應(yīng)答這一部分得注意一下,應(yīng)答是和讀寫相關(guān)的。等待應(yīng)答(寫入操作)需要返回是否應(yīng)答,這個地方一定要讀取SDA的電平狀態(tài)(注:讀取SDA電平之前一定要把SDA置高!??!)!雖然之前把SDA置高了,但是由于線與操作,SDA會被應(yīng)答的從設(shè)備拉低。所以讀到的電平狀態(tài)為低電平。這里我直接從輸入寄存器(IDR)中查看的輸入端口電平。(當(dāng)然樂意的話,輸出的電平也可以直接用輸出寄存器(ODR)去修改。)
????應(yīng)答之后,把SCL拉低,然后把SDA拉高,釋放SDA,我三個應(yīng)答的末尾都是這樣子的。這樣子三個應(yīng)答之后,狀態(tài)都是一樣的。
GPIO模擬發(fā)送一個字節(jié)的電平狀態(tài)。這沒啥好說的。只需要記住只有SCL低電平的時候才能夠修改SDA的電平狀態(tài)
GPIO模擬發(fā)送一個字節(jié)的電平狀態(tài),再次強調(diào):讀取SDA電平之前一定要將SDA置高!
按照時序,將上面的部分組合起來即可,這里需要注意的是,仔細的檢查各個部分之間的電平是不是存在沖突。
到了這里,已經(jīng)是一個可以調(diào)用的API了。(每一次寫入,后頭都跟了一個等待子設(shè)備應(yīng)答,認為返回0(SDA低電平)子設(shè)備應(yīng)答了,返回1(SDA高電平)子設(shè)備沒有應(yīng)答)
????地址問題:地址都是用的7位地址(0x68)。在使用的時候,高七位是設(shè)備地址,bit0是表示讀寫操作的位,0是寫入,1是讀取。所以寫入的時候就直接左移一位(0x68<<1)表示寫入操作。讀取就左移一位,然后讓最后一位置為1,這可以通過或(|)運算做到:(0x68<<1)|0x01
讀取函數(shù)相對角為復(fù)雜一點點,從時序圖上就看出啦了。按照時序圖寫下來就可以正常使用了。
這個時候就可以反過來寫MPU6050的程序了!?。?/p>
MPU6050的寄存器詳解可以看一下這個博客:
https://blog.csdn.net/m0_46278925/article/details/110568112
初始化完了之后,就可以從數(shù)據(jù)的寄存器中讀取數(shù)據(jù)了。每個數(shù)據(jù)占兩個字節(jié),所以將高位左移八位,然后低位或操作,就可得到原始數(shù)據(jù)。在main函數(shù)中調(diào)用這個函數(shù),就可以得到數(shù)據(jù)了??!
但是這樣子是不是覺得不舒服呢,對連續(xù)的寄存器單個單個字節(jié)的讀取,實在是太蠢了。總共需要12個字節(jié)的數(shù)據(jù),從0x3B讀取到0x48也只是需要14個字節(jié)數(shù)據(jù)。(中間有兩個字節(jié)是溫度數(shù)據(jù))。
????我們可以繼續(xù)編寫一次讀取多個字節(jié)的函數(shù)。依舊是按照時序圖來編寫。這個時候,讀取到數(shù)據(jù)之后就需要主動的拉低SDA應(yīng)答從設(shè)備,告訴從設(shè)備繼續(xù)發(fā)送下一個字節(jié)的數(shù)據(jù),等讀取完14個字節(jié)數(shù)據(jù)后,再給一個非應(yīng)答信號。
這就是所有函數(shù)了?。?!將這些函數(shù)羅列出來。
到了如果想要嘗試GPIO模擬串口,并且擁有MPU6050這個傳感器,copy這些代碼就可以嘗試運行了。這些代碼都寫在main.c中。延時函數(shù)和串口函數(shù)需要自己去寫。當(dāng)然我使用的GPIOC組的Pin8和Pin9做SDA和SCL。修改了引腳,那么讀取SDA的電平信息的宏也需要改變??梢园俣炔殚咷PIOx_IDR寄存器,當(dāng)然也可以直接用標(biāo)準庫的函數(shù)去讀取哈。
在串口中展示以下成果?。?!下面這個圖是將傳感器拔掉,重新插上的時候的結(jié)果。自設(shè)沒沒有應(yīng)答一定要立刻停止本次通信。

如果有需要這個代碼的話,可以Q我。需要STM32的延時函數(shù)也可以Q我。

制作不易,點贊投幣