1. 程式人生 > >嵌入式系統通訊協議設計

嵌入式系統通訊協議設計

公司裡做專案,嵌入式系統大大小小,到處都是。因為都是一個系統裡的,所以都需要通訊,既然通訊就涉及到協議問題。

談及協議,很多工程師覺得協議的設計相對簡單,主要是報文的設計。大多數時候,協議的應用場景簡單,沒有複雜的互動。這麼做的確也是沒什麼太大的問題。然而,就是這麼簡單的場景,仍有一些協議會在實際中發生意想不到的問題。歸根結蒂,還是沒有把握協議涉及的規律。下面我們簡單的聊聊協議設計的規律。

協議設計中面臨的問題:

1.設計者大多數情況下,從應用出發,僅僅考慮了基本需求的滿足,沒有考慮擴充套件需求的滿足;

2.從osi七層理論上,我們往往設計的協議時站在比較高層的角度去設計,往往忽視了RS485/RS232, I2C, CAN, ETHERNET等物理層承載特點,設計缺乏對具體應用的針對性,導致潛在問題的產生;

3.容錯和效率的考慮不足。

基本需求肯定是完成系統的基本功能。然而可能因為需求定義的不完整,系統設計人員沒有前瞻性;協議中沒有定義版本號,沒有對協議相容性的測試,導致老產品和新產品協議不相容,而又無法採用簡單的軟體辦法解決。這是個常見問題,最簡單的辦法是在握手協議中增加協議的版本號,用以判斷是否對該協議支援以及為後續的軟體做相容準備。協議看似好像只有報文設計,就像一個人一樣,他在父母的眼裡永遠是個孩子,在自己的孩子面前是父母,在朋友面前是朋友。我相信所有這些側面合起來才是一個完整的人。UML 從不同的角度去觀察系統會得到不同的圖。協議也是如此,協議的報文只是協議靜態特性的一個方面,協議還有更重要的動態特性。如出錯後怎麼辦,重發?重發幾次?節點損壞如何從網路中剔除?怎麼樣才是一個完整的通訊過程?持續的時間是多少?最壞情況下是怎麼樣的?最好的情況下是什麼樣的?誰發起通訊?重要的協議可能要保證非常可靠,如何確定接受者接收的完全正確,並且可靠執行?往往這些問題已經超出了對報文自身的考慮,而是對系統解決方法的一種設計。這裡有個小例子,一個RS485的半工通訊,主機向從機發送資料,希望從機可靠儲存該資料;從機接收到該資料驗證完畢後,寫入自身的儲存裝置,然後再回應主機,寫入成功或者失敗。但這裡有個問題,rs485是個主/從結構,無法同時傳送資料,只能由主機點名從機迴應。如果寫入時間過長,從機迴應報文的時間也必定過長;如果從機很多的話,這個時間就經不起浪費了。可能修改為,從機收到主機的資訊後,立即應答收到。主機再分發其他從機的資料,分發完畢後,再由主機採用查詢協議查詢從機寫入的成功與否。 當然,也可以採用一些系統級的辦法,只要從機收到資料後,從機一定保證資料寫入成功,那麼這個問題也變得簡單了。主機也不用再查詢寫入是否成功了。軟體的設計也就相對簡單很多。

RS485/RS232也有雙工通訊,但在實際中用得少。這裡除了省線材之外,恐怕最重要的是因為RS485/RS232不帶衝突檢測,要麼採用大家輪樁做主機,要麼一個主機,點名讓大家發言的辦法。所以,通訊採用一問一答的方法比較多,這比較符合半工的工作狀態。當然不排除一些雙工的應用場景。實際應用中,大多數還是採用半工的辦法。這裡協議的設計主要考慮單點較多;多播和單播,因為不能確定從機是否接收成功,所以重要的協議在多播和廣播之後還要查詢,這個是很麻煩的事情。軟體過程因此而複雜很多。RS485/RS232的通訊有自己的檢測錯誤的辦法,比如說奇偶校驗,奇偶校驗是一種簡單的錯誤校驗,並不能100%的擋住錯誤;對於可靠地協議,可能還是要設計自己的CRC或者校驗和等方法。但CRC校驗雖然可以用查表的辦法,但計算時間比奇偶校驗和校驗和等方法計算量還是過大了些。在一些實時性和低端應用場合,可能時間開銷大了些。所以,如果報文不是過大,還是可以考慮奇偶校驗和校驗和;如果過大,先考慮crc8,再考慮crc16和crc32,不要一竿子切。

I2C的通訊一般只用於板級,但現在也有用於現場匯流排的趨勢。I2C設計之初是支援多主多從,兩個主機可以同時傳送資訊,仲裁獲勝的主機獲得匯流排,繼續傳送。有仲裁不代表可以同時雙向傳送資訊,即主機和從機的地位還是不同的,主機點名從機迴應資訊;雖然現在的CPU所帶的I2C硬體同時支援主模式和從模式,但在同一時刻,這兩個模式是不相容的。對於一個節點要麼是主要麼是從,而每次通訊都是由主發起,從被動接收,這就導致了和rs232無本質的區別。且,I2C的物理層協議也決定了,其通訊方式也沒有rs232靈活,只能工作在半工狀態。兩個CPU傳遞一些簡單的資訊,在CPU無多餘的rs232情況下,還是非常有用得。由於是板級的通訊,訊號的完整性保證了以後,基本上不可能存在錯誤,也不需要額外的校驗方法了。

Ethernet和CAN匯流排類似,所有節點對等,無主從之說,誰都可以發起資訊。由於衝突的檢測,使得仲裁失敗的節點稍後重試,物理層完成,不需要軟體參與,因而給協議設計帶來了極大的便利。比如說,先前的那個問題,一個廣播協議出去,不再像 Rs232/rs485那樣,再去一一查詢確認。有問題的裝置直接上報問題就好。大大的簡便了問題的處理。RS232/RS485的節點如果發生問題,需要上報,也只能等到主機點名時才能有機會上報。Ethernet/Can就不用了,發生問題後,直接主動上傳。可以確保問題和緊急情況的及時處理。如果Ethernet是基於TCP的協議,效率低了,但卻保證了很多特性,資料的順序到達,可靠性等等。IP 層以下的協議有很大的問題就是不能保證資料的順序到達,多個路徑的長短會影響協議到達的順序,有些系統在設計時為了效率,採用UDP或者MAC層直接通訊。那麼最好還是採用較為保守的策略,防止協議報文先後到達產生不必要的錯誤。Ethernet物理層 帶有CRC32校驗,自己再做校驗實在沒必要。

協議的效率是個較複雜的話題,以RS232為例,RS232如果1個起始位置,1個停止位置,沒有奇偶校驗。那麼傳送一個位元組需要10Bit,對於9600bps的波特率,1秒鐘最多傳輸960個位元組。大約是1ms一個位元組。如果停止位加長,協議一次性運送的有用的位元組數還要更低。除去必要的幀頭,幀尾,地址,校驗資訊,真正有用得資訊除以總的總線上運送的位元組數,就是協議的帶載能力。很顯然,如果我用多播和廣播,明顯地提高效率。對於RS232這種,廣播也許並不是個好的選擇,尤其是回頭確認一遍。也許提高波特率是個不錯的主意。這隨之而來的問題是,1Mbps的通訊系統,1Start,1stop, non-parity, 1個位元組只需要10us,這麼快的中斷不是普通的CPU能承受的。所以,可能需要DMA來接收。DMA 接收的話,就牽涉到變長的協議和定長的協議。變長的協議要動態的判斷是否收到一個完整的包,而定長的協議,對於高速的RS232有無可比擬的優勢。大大降低計算的複雜度。定長的協議就牽涉到協議的長度。我們一般把最頻繁出現的協議的長度作為全部協議報文的長度。對於超長的協議,由於使用次數不多,拆成多條定長協議吧報文完成。比如說,我們的系統控制命令長度為所有協議的長度,因為80%的協議報文都是系統控制命令。而20%的報文是其他出現頻率較低的報文,如系統韌體升級報文,本身韌體的大小就很大,就算超長的報文也不可能容納。砍成與控制命令報文等同的長度。看似比較零散,每包卻都有各自獨立性。都可以做單獨的報文傳送,前後的耦合性降到最低,也就是說,雖然大協議被拆分成小的等長協議報文,每個等長報文在傳送過程中出錯,可以單獨再發送,整個通訊序列無需重置。通過合理的設計,協議的效率自然而然的就被提升了。

Ethernet的設計相對寬鬆,其底層太強大了。很多工作都做了,所以,等長不等長對Ethernet系統無所謂。關鍵要解決Ethernet的通訊模型問題。如果是嵌入式伺服器,TCP 保持半開連結不能太耗費資源。如果是UDP或者mac層的協議,需要對協議序列的先後到達順序進行解耦,防止出現不必要的問題。如果一個大協議包,需要拆成三個包傳送,三個包順序任意無影響;任意包發生錯誤,只需要將錯誤包重發成功即可。Ethernet由於速度高,協議帶載能力可以得到很好的補充。另外,由於Ethernet是一個完美支援多播和廣播的網路,實際中使用廣播太多造成了廣播風暴,導致網路效能急劇下降,所以現在分了虛擬區域網,就是為了抑制廣播風暴,提高效率。實際使用中還是要儘量的合理設計系統,避免過多的廣播協議的使用,以免拖慢整個網路系統。

原文地址:https://blog.csdn.net/coolbacon/article/details/18496559