1. 程式人生 > 其它 >stm32 ucosii訊息佇列 串列埠_【工作筆記】總結一下曾經在串列埠方面犯的錯誤

stm32 ucosii訊息佇列 串列埠_【工作筆記】總結一下曾經在串列埠方面犯的錯誤

技術標籤:stm32 ucosii訊息佇列 串列埠vb使用mscommon接收串列埠資料錯誤串列埠丟包受什麼影響串列埠異或校驗串列埠接收700個字元不完全串列埠接收資料異或和

專案雖然做得不算多,但是關於串列埠的程式倒是寫了不少,可能是我太笨了吧,每次寫,每次錯,錯得五花八門,應有盡有,很少能一次性順順當當地搞定,更好笑的是有些錯誤是犯了好幾次的,每次找到這樣的錯誤,我都想給自己一個耳光。就在幾個小時前,我又犯了一個愚蠢的錯誤,痛定思痛,這次一定要總結起來,算是給自己敲個警鐘吧。75fe4421ebcc0bf6635e6fbaca735924.png


  • 程式邏輯錯誤

我通常在串列埠接收中斷裡進行協議的判斷,用不同的狀態量來記錄接收的情況,當完整地接收到一幀資料時,主控部分就可以對資料進行操作了。

假設要接收的資料協議格式為

(1) 幀頭: 2Byte, "0xFF,0xEE";

(2) 幀序號:1Byte, 逐包遞增;

(3) 命令字: 1Byte

(4) 幀長:1Byte

(5) 資料: nByte

(6) 校驗: 1Byte

(7) 幀尾: 1Byte 0xD5

下面就是一段自己曾經寫的串列埠中斷接收程式:

u8 USART1_RX_BUF[USART1_RECV_LEN];              //串列埠接收陣列u8 Usart1_State=0;                                                           //串列埠接收標誌u8 xorchkm1=0;                                                                //異或校驗u8 length1=0;                                                                    //資料長度標誌u8 index = 0;                                                                     //陣列元素序號標誌u8 data_cmd ;                                                                   //命令字u8 data_num;                                                                    //幀序號        void USART1_IRQHandler(void){  OS_ERR err;  u8 res;  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  {       res =USART_ReceiveData(USART1);            if(Usart1_State==0)        {          if(res==0xFF)     //幀頭          {            index = 0;            xorchkm1=0;             Usart1_State = 1;            USART1_RX_BUF[index++] = 0xFF;                       }          else           {             Usart1_State = 0;                    }        }        else if(Usart1_State == 1)        {          if(res == 0xEE)  //幀頭          {            Usart1_State = 2;            USART1_RX_BUF[index++] = 0xEE;          }          else {Usart1_State = 0;}        }        else if(Usart1_State == 2)        {          USART1_RX_BUF[index++] = res;          data_num = res;                    Usart1_State = 3;          xorchkm1 = xorchkm1^res;        }        else if(Usart1_State == 3)        {          USART1_RX_BUF[index++] = res;          data_cmd = res;          Usart1_State = 4;          xorchkm1 = xorchkm1^res;        }        else if(Usart1_State == 4)        {          USART1_RX_BUF[ index++] = res;          length1 = res;          Usart1_State = 5;          xorchkm1 = xorchkm1^res;        }        else if(Usart1_State == 5)        {          USART1_RX_BUF[ index++] = res;          xorchkm1 = xorchkm1^res;          if( index== (length1 +5) )                            {            Usart1_State = 6;          }          else          {            Usart1_State = 5;          }                }        else if(Usart1_State == 6)        {           if(xorchkm1 == res)           {             USART1_RX_BUF[ index1 ++] = res;             Usart1_State = 7;           }           else           {             Usart1_State = 0;             xorchkm1 = 0;                   }        }        else if( Usart1_State == 7 )        {          if(res == 0xD5)          {            Usart1_State = 8;            USART1_RX_BUF[ index++] = res;            xorchkm1 = 0;            index = 0;                }          else          {            Usart1_State = 0;            xorchkm1 = 0;            index = 0;          }        }        }}

邏輯不難,但是稍有不慎就會出錯,尤其是程式中的狀態量,經常會粗心忘了清零,導致接收不正常,雖然通過debug模式斷點除錯可以找到問題所在,但是耽誤時間,影響效率,得不償失,應當引起重視。


  • 資料丟包嚴重

串列埠接收端丟包嚴重時,如果不是因為明顯錯誤導致,一般都是由於資料傳輸速度快,主控處理不過來造成的,想辦法降低傳輸速度:

(1)波特率: 最直接的方法就是降低波特率,我做過一個小實驗,用幀序號來判斷丟包情況,當丟包時亮起一盞燈,波特率越大,燈閃得頻率越高;

(2)傳送間隔: 如果條件允許,可以將傳送端的傳送頻率降低,採用定時傳送的方式,每間隔一段時間傳送;

(3)上拉電阻: 根據網上有些文章提到的方法,還可以在串列埠接收端增加上拉電阻,有時候即使傳送端沒有發出資料,接收端也會誤接到一些資料,頻繁進入中斷也會降低微控制器處理效率,在電路上增加上拉電阻的目的就在於降低誤接的概率,提高微控制器的處理效率,該方法我並沒有親自試過,但是加上上拉電阻效果肯定是好於不加的;

(4) 訊息佇列: 一般的串列埠中斷接收,用一個全域性變數資料來接收是完全沒問題的,就像上文中的程式,用一個狀態量來控制中斷接收,當Usart1_State為8時,表示接收到了一個完整的資料包,主程式此時開始做出相應的處理,處理完之後把狀態量Usart1_State 清零,中斷可以繼續接收。但是當接收資料量較大,較快時,會出現一個問題: 在主程式從全域性變數數組裡獲取協議時,還沒來得及處理完,傳送端已經發來了資料,此時就有可能漏掉幾個包,導致資料丟包。

如果把全域性變數換成訊息佇列的方式,就可以減小這種情況帶來的損失,如下圖所示:

OSQPost((OS_Q*)&USART_Msg,           (void*)&USART1_RX_BUF,           (OS_MSG_SIZE)USART1_RECV_LEN,           (OS_OPT)OS_OPT_POST_FIFO,           (OS_ERR*  )&err);

主程式從訊息佇列裡獲取資料

u8  *RS422_RX_BUF; RS422_RX_BUF=OSQPend((OS_Q*)&USART_Msg,(OS_TICK)0,(OS_OPT)OS_OPT_PEND_BLOCKING,(OS_MSG_SIZE*)&size,(CPU_TS*)0,(OS_ERR*)&err);

  • 誤碼嚴重

(1) 干擾過大 在曾經的專案中,根據串列埠接收協議做控制,但是會發現有嚴重的失控以及錯誤控制情況,後經檢驗發現是因為串列埠接收附近有一個射頻訊號,由於該訊號的功率較大,導致輻射嚴重,影響了串列埠的正確接收。

遇見這種情況,首先應該考慮隔離干擾源,儘量減少干擾帶來的影響;同時,在訊號輸出端對資料協議進行封裝,比如,加長幀頭,幀尾,校驗等,確保能完全濾除錯誤協議,從而排除誤碼帶來的錯誤控制。

(2) 硬體問題 其實硬體故障帶來的問題遠不止誤位元速率這一點點,但是一些較為明顯的問題(比如串列埠壓根不工作,或者電平不正確等等)是比較容易判斷故障源的,這裡所說的硬體帶來的誤位元速率,是一些不太容易發現,甚至有時會認為是軟體問題的情況。

比如之前一次除錯中,串列埠接收時而成功,時而又不成功,非常困惑,最後一步步排查,發現居然是一個反相器晶片的管腳虛焊了;9400236372513a85694290e2e143ee6c.png在昨天的除錯中,有一個情況更令人無語,從電路到程式,從暫存器到邏輯,能檢查的地方都檢查了,但仍然時好時壞,調了一下午,發現是其中一個介面不太好,更換後就無誤了


除錯過程中會出現各種各樣的問題,越是有問題,越是應該冷靜,有時候很簡單的問題越是容易讓人忽略,提高技能的同時,虛心向別人求教,勤於總結反思,平穩自己的心態,才不失為一個優秀的技術人員。c44d4b951119a17ee7d1da8be6643973.png

End~

本文轉自“Bernice堅果丁”