封裝stm32串列埠的底層時,遇到了串列埠幀同步的問題。雖然以前也遇到類似場合,寫出來的**基本能夠解決問題,但是在邏輯上總是不能徹底的解釋一些細節。
當前的工作環境:
串列埠的方案是開啟收發的dma以及dma的中斷。(堅決不考慮直接使用串列埠中斷。乙個位元組中斷一次太費資源)。dma陣列作為串列埠的fifo佇列(並不是真正意義上的佇列)。
當前的需求:
1、時間節拍到來時,檢查是否有收到資料。沒有則跳出,有則進入下一步
2、檢查資料中的包格式,比如包頭是否正確,幀長度是否對齊,crc(目前還沒有做進去)等
3、包格式檢查出錯誤,回包時新增標誌位,宣告包格式錯誤請求重發。包格式沒有錯誤則進行解包並設定對應的暫存器和賦值。
4、具有合理的接收緩衝區,大於緩衝區的資料進行放棄。
5、能夠及時檢測出丟位元組,多位元組等幀長度出錯的問題。
幾套嘗試過的方案:
1、dma陣列的長度和幀長度相等。
觸發條件:dma計數值減到0(即已經收滿乙個幀的長度的資料)產生dma中斷,將觸發標誌位寫1。pc機上可以通過開啟乙個執行緒監視緩衝區數量實現。
解包操作:設定共用體,其中結構體為幀協議,同時公用乙個u8 陣列作為dma陣列。判斷觸發條件,若滿足,讀取共用體中的包頭包尾,若正確,繼續讀取成員,解包賦值。
緩衝機制:無。dma設定為normal模式,計數減到0後即停止。有新的資料到來也不會被傳入陣列。pc機上可以手動關閉串列埠。
報錯機制:
幀錯位:在包頭檢查中會發現,捨棄當前幀,設定重發標誌位請求重發。
位元組缺失:位元組缺失的幀傳送完成後不會滿足觸發條件,等到第2幀的資料的前幾個位元組填滿缺失的幀後,觸發解包操作。在檢查包圍的時候,報錯響應。捨棄位元組缺失幀,但是難以保證位元組缺失幀的後幾幀能順利接收。而且出錯和報錯響應不同步。即報錯響應出現在錯誤的下一幀。
位元組超出:位元組超出的幀會及時響應,並且由於包尾錯誤,會立即響應報錯並請求重發。
解包過快:不會出現解包速度大於收包速度。因為資料滿乙個幀長度才會解包。
2、dma陣列指向元素型別為幀結構體的鍊錶
觸發條件:dma計數值減到0(即已經收滿乙個幀的長度的資料)產生dma中斷,dma中斷中對list進行push_back操作,增加乙個element,然後將dma的記憶體位址指向新的element的首位址。觸發條件是list的size大於1(在沒有收到任何報文之前,得有乙個空element用於放置馬上要到來的報文);
解包操作:檢查list第1個元素的包頭包尾,如果正確,讀取成員解包賦值,然後對list進行pop_front,直到list的size等於1.
緩衝機制:鍊錶天然的緩衝機制,唯一擔心的是堆溢位,可以設定乙個上限,在中斷裡判斷。
報錯機制:
幀錯位:在包頭檢查中會發現,但是需要丟棄緩衝區內錯位幀之後所有的幀。因為後邊的必然都錯位了。
位元組缺失:第2幀到來時,檢查包尾時發現。同樣存在報錯響應不同步的問題。
位元組超出:報錯同步響應。丟棄緩衝區中所有幀
解包過快:不存在這個問題。理由同 方式1.
3、dma陣列指向多倍於幀長度的陣列首位址
觸發條件:緩衝佇列非空。觸發響應後,立即將緩衝佇列memcpy到臨時陣列進行解包。同時清空佇列。
解包操作:在臨時陣列中搜尋包頭的第1個位元組,一單滿足,立即檢查:包頭第2位元組,包尾是否在緩衝區長度內,包尾是否正確。如果4個條件均滿足,立即開始解包賦值。完成後重複上一步,在陣列中搜尋第2個包頭。直到最後在緩衝區末端,殘留幀的前一部分,捨棄該無尾幀。
緩衝機制:由緩衝佇列作為緩衝。
報錯機制:
幀錯位:在臨時陣列中不存在幀錯位的概念,幀錯位完全可以被正確解包。
位元組缺失:在解包步驟中被檢測到包尾有誤,則請求重發。而且能同步響應。
位元組超出:同位元組缺失
解包過快:由於觸發方式為緩衝佇列非空。如果查詢觸發條件時,恰好接收了部分幀,則仍然能滿足觸發條件。那麼此時這個接收了一部分的幀會作為位元組缺失的幀被捨棄並進行報錯
小結:
三種方式對比下來,第3種方式有著較優越的效能,而且能夠很好地移植到pc機上實現。但是對於解包過快的問題,仍然需要討論。
位元組缺失同步響應和解包過快的矛盾:
問題可以被化簡為:乙個10位元組的幀,解包時,如果包裡只有9個位元組,那這一幀到底是沒發完還是位元組缺失。
如果使用「已收到大於10個位元組的資料」作為解包觸發條件,那麼解包時永遠有10個位元組,判斷最後乙個位元組是否是包尾,即可。但是位元組缺失永遠只能在下一幀響應。
如果使用「緩衝區資料多於0」作為解包觸發條件,雖然位元組缺失能立即被響應,但是也有可能將未發完的幀誤判。
因此需要針對當前的應用進行分析。目前對於微控制器的幀率和幀長度為:
波特率:115200
傳送幀率:5f/s
傳送幀長:20 bytes
接收幀檢測週期:1ms
接收幀長度:10 bytes
傳送1byte資料,由於沒有校驗位,1個停止位,因此需要10bits。
那麼傳輸速率為11520b/s(約11kb/s),即傳輸1byte需要86.8us(約0.1ms)。
傳送幀每一幀20bytes,需要1.736ms(約2ms)
接收幀每一幀10bytes,需要0.858ms(約1ms)
因此對於當前的情況下微控制器的接收條件,1ms解包一次,完全不需要緩衝區,但是卻有很大可能發生在幀截斷。
因此應該採用「已收到等於幀長度個位元組的資料」作為觸發條件。放棄位元組缺失幀的同步響應。
但是對於pc機端,如果同樣為1ms間隔檢測觸發條件,接收幀的時常變為1.736毫秒,那麼乙個間隔內是必然收不滿1幀的。
因此同樣可以採用「已收到等於幀長度個位元組的資料」作為觸發條件。放棄位元組缺失幀的同步響應。
但是對於qt上的串列埠類,現在還沒有摸清他的工作原理,尚無法討論何種方法比較合適。
/**********************************11月1日更新分割線****************************************/
乙個能夠提高缺位元組幀報錯響應速度的方案:
判斷幀是丟位元組還是未發完的區分方式其實是在時間上。
比如之前提到的115200波特率,20bytes的幀,其傳送時間應該小於2ms。
因此,當:接收緩衝區有資料,單資料未到達20bytes時,若這種狀態維持超過2ms,則說明傳輸已經完成,缺位元組。
而程式本身的step timer已經有了計時的功能。因此,實現方式如下。
宣告乙個標誌位1:fifo佇列有資料但不滿幀長度。
宣告乙個計數器1:標誌位1的計數。
當fifo佇列資料從0跳變到1時,set標誌位1。
checkmailbox時,標誌位1已置位,則將計數器1的值加1。
由於20bytes的幀在2ms內應該傳送完。而解算週期為1ms。
故,當計數器1的值大於2時,如果fifo佇列資料長度仍然沒有達到幀長度,說明該包有資料丟失。set報錯標誌位。
即可檢測出丟位元組的幀。
串列埠同步通訊和串列埠非同步通訊
串列埠掃盲貼 現實中不存在絕對精確的時鐘,標稱值同樣是1mhz,發端和接收端的時鐘總會存在差異,如果接收端不進行時鐘同步,而是使用自己本地的時鐘,則足夠長的時間後接收到的資料總會出現不可預知的重複或丟失,導致接收錯誤。因此發端必須將與資料速率相關的時鐘訊號傳輸給接收端,時鐘訊號可以走單獨的訊號線,也...
串列埠通訊 同步通訊方式
1 同步通訊方式的特點 採用同步通訊時,將許多字元組成乙個資訊組,這樣,字元可以乙個接乙個地傳輸,但是,在每組資訊 通常稱為幀 的開始要加上同步字元,在沒有資訊要傳輸時,要填上空字元,因為同步傳輸不允許有間隙。在同步傳輸過程中,乙個字元可以對應5 8位。當然,對同乙個傳輸過程,所有字元對應同樣的數字...
C 串列埠程式設計 寫串列埠 同步通訊方式
char buf native string int len strlen buf byte wchar byte buf 將char轉換成byte writechar wchar,len 寫串列埠,串列埠一開啟,將串列埠一的資訊wchar寫到串列埠二中。bool writechar byte m ...