1. 程式人生 > 其它 >串列埠 接收端_硬核分析串列埠通訊協議

串列埠 接收端_硬核分析串列埠通訊協議

技術標籤:串列埠 接收端

串列埠通訊協議,基本上是每個微控制器開發人員必會的協議,是所有通訊協議裡面最最常用的。但是很多人並沒有去研究過細節,這篇文章乾貨滿滿,將帶你深入串列埠協議的內部。

1、串列埠協議簡介

什麼是串列埠通訊?

簡而言之,串列埠通訊就是通過一根導線(TX,只討論傳送,當然一定還包括GND),將需要的資料按bit流傳送給接收端,既然是通訊,參與的雙方當然要制定資料的傳輸方式以及規範。

隨便開啟一個串列埠助手都可以看到,如果需要使用串列埠就需要配置埠、波特率、資料位、停止位、校驗位、流控這些引數。

2de13d1732314cfa8859e3274d2a13fc.png

如果接收端和傳送端的設定不同,就可能會導致資料異常。

埠:這個就不用解釋了,使用串列埠晶片安裝驅動後就可以在電腦上看到埠號了。

現在,我們從電平訊號角度分析一下波特率、資料位、停止位、校驗位、流控(由於用的比較少,暫不分析)的作用。

2、電平訊號分析

開啟串列埠工具,分別調整如下引數

①調整引數9600/8/N/1,傳送十六進位制資料55 AA

c516d98b047aedb37479c3983cefe143.png

dadde05e81beadd64979569bfb441e05.png

②調整引數115200/8/N/1傳送十六進位制資料55 AA

c3dcb3bbf2c3e407c6b0b86edff1b7ca.png

e137104af9bb13edb45f5e4a563dad17.png

③調整引數9600/7/N/1傳送十六進位制資料55 AA

22b73016a0ddeffd3e0d29a3ffcb79a0.png

④調整引數9600/8/O/1傳送十六進位制資料55 BA

8dab30751ec4ff784c78ba149a14959a.png

⑤調整引數9600/8/E/1傳送十六進位制資料55 BA

f05869d0255fcfc963fb60ac5ea18d71.png

⑥調整引數9600/8/N/2傳送十六進位制資料55 AA

23c34eb65683be53fcbe5ab344adf768.png

其中B代表起始位,E代表停止位,很明顯看出是先傳輸的低bit,再傳輸的高bit。

根據①②分析,調整波特率並不會影響整體波形形狀,只有bit的持續時長不同,9600波特率時的一個bit大概佔104us,115200波特率大概是9us,這是可以算出來的9600:1000ms/9600 = 104.1us,115200:1000/115200=8.6us。分析可知波特率越大,傳輸速度越快。

根據①③分析,調整資料位後,55本來應該是01010101但是資料位調整到7之後,55變成了1010101,AA本來是10101010變成了0101010,分析可知錯誤的資料位會將傳輸的位元組從高位開始截斷,可能導致資料丟失。一般來說除非特殊需求,否則不會調整資料位引數。

根據①④⑤分析,將校驗位調整至奇校驗(ODD)後,傳輸的bit多了一個奇校驗位,就是說如果本來的8個bit的bit位按位加起來是偶數,則在最高位需要補一位1,否則補0,55傳輸變成了 (1)01010101,BA變成(0)10111010,同樣偶校驗(EVEN)、MASK校驗(校驗位始終為1)、SPACE校驗(校驗位始終為0)是相同的道理。

根據①⑥分析,將停止位設定為2後,傳輸的結束位(圖片中的E)變成之前的2倍,分析可知,設定停止位,可改變資料傳輸完成後的停止位bit持續的時間。

3、為什麼需要起始位和停止位?

波特率、資料位、校驗位很好理解,都是為了實現不同的業務需求,但是起始位和停止位存在的意義是什麼呢?

起始位:平時這根導線上是高電平,如果接收方檢測到低電平了,說明要開始接受資料了,是傳送方通知接收方開始接受的一種方式。(不考慮流控)

停止位:傳送方通知接收方傳送完成的一種方式。傳送方傳送完一個位元組後,“暫停一會”再繼續傳送下一個位元組。

這時帥氣的小夥伴就要問了,起始位存在的意義可以理解,但是為什麼需要停止位呢?不是提前都約定好了傳輸8個bit,接收方不能就接受到8個bit後認為一個位元組傳輸結束就OK了嗎?

能思考到這一步的小夥伴已經很棒了。如果我們是設計師,我們從設計的角度設計一下接收方接受資料的虛擬碼

//接收端接收資料執行緒
while(1) {
    //接收到起始位下降沿電平
    if((isGetStartFlag == 0) && getDownEdge()) {
        Delayus(52);
        //檢測起始位低電平
        if(isLowPin()) {
            isGetStartFlag = 1;
            getBitIndex = 0;
        }
    }
 
     //已經接收到起始位
    if(isGetStartFlag) {
        Delayus(104);
  
        if(getHighOrLow() == high) {
            //接收到1bit
        }else {
            //接收到0bit
        }
        getBitIndex++;
        if(getBitIndex == 8) {
            //位元組接收完成
            isGetStartFlag = 0;
            getBitIndex = 0;
        }
    }
}

按照這種虛擬碼邏輯,如果不存在停止位,會產生兩個問題。

1、可能無法檢查下降沿

如果上一個位元組最後一次傳輸是0,而下一個位元組的起始位也是0,那麼下一個位元組的起始位就檢測不到下降沿,無法觸發下一個位元組的傳輸,就會丟失資料。這時帥氣的小夥伴又要問了,那我不檢查下降沿,只檢查低電平不行嗎?直接把getDownEdge換成isLowPin函式不就可以了嗎,確實可以,這樣又會引入第二個問題。

2、時鐘同步問題

假設A跟B說,10分鐘後你提醒我喝水,然後B就看著自己的表,10分鐘後提醒A,這時A一看錶,才過去9分鐘。這種場景現實也會出現,造成的原因是A和B的表不同步或者有誤差這個誤差的引入就導致了A和B所指的“10分鐘”不相同,在我們這個串列埠的場景裡,就是傳送方和接收方約定了9600波特率,也就相當於約定了104.1us傳輸一個位元組,按照上面的虛擬碼,幾個採集點應該是

e17db4494fcda572c8c76cfda80e0a55.png

但是由於誤差,可能導致採集點偏移為

d087c2003f9210f4bcb01a2c80cd4a8d.png

這兩個圖能大致表達時鐘不同步,導致接收裝置的採集點後移,如果這時引入了停止位,第二個BYTE開始時,由於還是從下降沿開始採集,所以會重置上一個BYTE引入的誤差。

b197fd2f772e717211ca9008fa170f75.png

4、總結

這篇乾貨基本把串列埠協議分析透徹了,大家在分析協議的時候,最好是從設計的角度去思考這樣設計的作用,如果有什麼分析不對的地方,歡迎大家指正。希望這篇文章能給你帶來一點作用。