4年前寫過一篇關于GPIO模擬串口的文章:GPIO模擬串口
4年來陸陸續(xù)續(xù)有人聯(lián)系我要示例代碼,可見GPIO模擬串口是一個實際需求。最近我也又碰到了該需求,因為客戶用的MCU本身只有2個串口,而實際需要3個串口,所以需要額外模擬一個出來。
于是把之前的代碼又重新移植并測試了下,之前代碼發(fā)送的時候波特率可以達到115200bps,但是接收卻不行。當時沒有細致分析過原因,這次認真的分析了一下。
這次使用的MCU主頻運行在48Mhz,模擬串口發(fā)送的邏輯和實現(xiàn)比較簡單,按照1/波特率周期,在定時器中斷服務函數(shù)里去發(fā)送串口對應的bit位即可。實測波特率可以支持到256000bps。
接收的邏輯是:當RX引腳檢測到下降沿時,進入GPIO中斷,然后開啟一個定時器,第一次定時器周期設置為1/波特率的一半(目的是為了在中心處判斷是否為低電平,以表示是否為起始位),再之后就可以設置定時器周期為1/波特率,每隔此周期在定時器中斷服務函數(shù)里去采樣RX引腳電平,將數(shù)據(jù)接收完畢。
為什么接收時,波特率高了之后就數(shù)據(jù)異常呢? 未修改代碼之前,我測試波特率最高只能支持到19200bps,
我們在接收定時器中斷服務函數(shù)里加入點代碼,在串口每一個bit位處理前后進行一次GPIO翻轉(zhuǎn)。通過波形來分析接收的處理時序:
加入上述代碼后,接收波特率最高只能支持到14400bps了,以下是波形圖,其中Channel 0是翻轉(zhuǎn)IO的波形,Channel 1是串口接收引腳波形
從上圖中我們首先看到第一個IO翻轉(zhuǎn)時間為14us,明顯高于后續(xù)其他翻轉(zhuǎn)時間。這個原因經(jīng)分析是代碼里用了一個除法運算導致的。
我們優(yōu)化一下代碼,不用每次都在這里做一次除法。改完之后時間從原來的14us縮短為2us。除法導致運行時間長具體可以查看之前的文章:在KEIL中勾選微庫后,延時函數(shù)為什么不準了?
另外發(fā)現(xiàn)RX下降沿到IO口開始反轉(zhuǎn)的時間是50.8us,理論值應該是35us才對。
為什么延遲了這么多呢?問題出在這里,這里也用了除法,同樣的我們也做一下修改
改完之后,這個時間就到了39us,偏差就小了很多。
可以進一步優(yōu)化,在進入下降沿中斷里一開始就先配置并啟動定時器
這樣對應的時間還可以更準確一點。
經(jīng)過以上修改后,19200bps接收正常,但是38400bps波特率還是異常,我們看一下波形:
問題出在第一個起始位采樣點有3us偏移,導致后續(xù)的采樣點相比自身的中心點偏的越來越大,以至于第2字節(jié)數(shù)據(jù)的起始位沒有來得及處理,進而導致后續(xù)的數(shù)據(jù)處理錯誤。
我們將采樣首次的采樣時間做進一步優(yōu)化,讓其更靠近中心點,
這樣修改后波特率可以達到56000bps,但是再增加到57600bbps后還是有問題。其實原因也很簡單。還是因為首次1/2周期采集依然有偏差,隨著后續(xù)不斷按照1/波特率周期去采集,誤差越來越大。
我們進一步做優(yōu)化,將后續(xù)按照固定周期去采集的方式改為動態(tài)調(diào)整周期值,最簡單的方式是周期大小做交替變化,這樣到最后一個字節(jié)就不會偏差那么大。
改完之后可以支持到57600bps,但是115200bps還是不行。
通過觀測波形可以看到,第一個IO翻轉(zhuǎn)下降沿到第二個IO翻轉(zhuǎn)下降沿的時間間隔出現(xiàn)了錯誤,理論應該是9us,但實際只有5us。
經(jīng)分析原因是隨著波特率的提高,定時器的周期越來越短,當波特率為115200bps時,1/波特率的一半 周期只有約4us,而起始位的代碼執(zhí)行時間已經(jīng)接近4us,所以定時器周期還沒有更新生效的時候,原來的一半周期中斷又來了。代碼優(yōu)化如下,首先更改周期。
這樣就可以達到115200bps的波特率:
以上記錄本次調(diào)試過程中遇到的問題及解決辦法??偨Y一下:要想GPIO模擬串口能夠提高波特率,需要通過精確的定時器配置、精簡的中斷服務程序、動態(tài)誤差補償?shù)却胧﹣韺崿F(xiàn)。關注公眾號:掃碼加入嵌入式交流群: