李逸楠,孫麗君,王 欣,所玉君
(1.天津津航計算技術研究所,天津 300308;2.天津市航空電子綜合顯示控制重點實驗室,天津 300308;3.天津航海技術研究所,天津 300131)
在嵌入式系統中,分系統間最常用的數據傳輸方式是采用各種串口進行通訊,比如RS232/RS422/RS485等[1-3]。拋開底層硬件設計的不同,通常在鏈路層通過一些協議來保證發送方與接收方數據的一致性。
SLIP協議是一種常用的鏈路層通訊協議,規定了幀頭、幀尾、在幀頭幀尾之間如果出現幀頭或幀尾字符,則按照一定的規則進行替換,保證發送方在幀頭幀尾間不會出現幀頭幀尾對應的字符[4];接收方收到數據后,也需要按照幀頭幀尾把數據解析出來,再按照替換規則把有效數據還原。這時得到的數據就可以按照上層協議進一步解析,得到有意義的數據。不同的項目有具體的上層協議,解析的方法不盡相同,但基本思路是一致的。本文涉及到的協議及硬件可返回的數據如下。
以0xc0為幀頭,0xc0為幀尾,幀頭幀尾間如果出現0xc0,則以連續兩個字符0xdb和0xdc取代;如果幀頭幀尾間出現0xdb,那么就用連續兩個字符0xdb和0xdd替代;接收方也按照這個規則還原接收到的數據。
經SLIP協議解析后的數據為一個完整的數據幀,格式如表1所示。

表1 消息幀格式
UINT16 ReciveCοunt:自硬件上電以來收取到的所有字符數,數據類型為UINT16,無符號的16位整型。
UINT8 DataRev[2048]:數據接收緩存,硬件接收到數據之后會按順序記錄在緩存中,最多可存放2 048個字節,當超過2 048個字節,新數據從頭開始存放。
根據背景說明,可以分析出數據解析具體需求有以下幾點:①需要根據硬件提供的信息計算出每周期接收到的新數據個數,并從數據緩存中取出新數據;②需要按照SLIP協議解析出原始數據,并將接收方回復的數據按照SLIP協議處理后發送;③如果一個周期內未接收到一個完整的數據幀,需要將不完整的數據幀存儲起來,待下一個周期接收到數據之后,進行數據拼接,保證數據的完整性;④需要將解析出的原始數據整理為上述數據幀格式。一個周期內如果有多條數據幀,則都需要保存起來。
按照上述需求分析,采用模塊化的設計思想設計出以下幾個軟件模塊。數據解析過程的總流程如圖1所示。
該模塊用來判斷一個周期內收取到的數據中是否存在不完整的數據幀,如果存在,則給出需要保存的數據個數,并將需保存的數據存儲在全局數組中。算法的關鍵是如何判斷是否存在不完整的數據幀。本文采用的方法如下:判斷接收到的數據中最后一個c0是否就是本次接收到的最后一個數據,如果是,說明本次接收的是一幀完整的數據;如果最后一個c0不是最后一個數據,說明本次接收到了不完整的數據,需要將最后一個c0之后的數據保存起來。
需要特別注意的是,主要有以下兩種情況需要單獨處理:①本周期內就只收到了一個數據,而這個數據恰好就是c0。如果按照之前的條件判斷,不會把這單獨的c0認為是一個不完整的數據幀,就不會保存這個c0,導致下一周期收取的數據沒有幀頭或幀尾,解析數據就會出錯。正確的做法是遇到僅有一個c0的情況,要把這個c0保存起來,在下一周期拼接在收到數據之前。②收到數據不止一個,且最后一個c0確實也是本周期收到數據的最后一個,但這個c0之前還是個c0。如果按照最開始的條件判斷,會誤認為這是一幀完整的數據,而事實上,連著的兩個c0前者是上一幀數據的結尾,后者是下一幀數據的開頭。正確的做法是當遇到最后一個c0就是最后一個數據的情況,還需要額外判斷這個c0之前是否是c0。如果不是,則說明確實是完整的數據,無需保存數據;如果是,則說明恰好收到了下一幀數據的開頭,需要把這個c0保存起來,在下一周期拼接在收到數據之前。經過以上分情況處理,就可以保證不會漏掉一幀不完整的數據。程序控制流如圖2所示。

圖1 數據解析過程
該模塊負責判斷何時有新數據傳來,并從硬件緩存中取出新數據。基本思路是設計兩個全局變量,一個用來存放當前周期的ReciveCοunt值(num),一個用來存放上一個周期的ReciveCοunt值(numοld)。如果當前值等于上周期值,則表明無新數據傳來,如果當前值大于上周期值,則表明有新數據,需要從硬件內存中取出數據。新數據為從DataRev[numοld]開始到 DataRev[numοld+(num-numοld-1)]的數據。上述規律在ReciveCοunt小于2 048個數據時成立;當ReciveCοunt大于2 048個數據時,新數據將保存在緩存區的開頭,這時情況稍顯復雜。因為物理內存只有2 048 Byte,首先應將ReciveCοunt值對2 048取余,這樣就將ReciveCοunt換為2 048以內的數值。則一旦數據超過2 048從頭開始時,前述取數規律又可以使用。需要分情況討論的就只剩上一周期數據(numοld)小于2 048,而當前周期數據大于2 048的情況了。由于取余處理,當前周期的ReciveCοunt實際值為余數(num),如果一個周期數據量不超過2 048個,則一定有num小于numοld。此條件可作為特殊情況的判斷條件,這時新數據應該是內存尾部DataRev[numοld]開始到DataRev[numοld+(2048-numοld-1)]的數據與內存頭部DataRev[0]開始到DataRev[num-1]的數據拼接而成。程序控制流程如圖3所示。

圖2 不完整數據保存模塊控制流程圖
在SLIP協議解析過程中,需要根據規則,在數據中指定位置插入特定的數據。例如“如果幀頭幀尾間出現0xdb,那么就用連續兩個字符0xdb和0xdd替代”這一條規則其實就是在0xdb之后插入一個0xdd。為滿足這個需求,設計了一種鏈表類型結構體,結構體設計如下:
typedef struct
{
UINT8RevBuf[MAXSIZE];
int charnum;
}SqList;
該結構體由一個字符數組和一個整型變量組成,字符數組用來存放消息數據,整型變量用來存放消息包含的字符個數,MAXSIZE為數據最大長度,由實際傳輸數據最大長度確定。
鏈表數據插入模塊的操作對象就是這種鏈表數據,輸入一個待操作的鏈表,待插入的數據,以及插入的位置,鏈表數據插入模塊輸出插入數據之后的鏈表,并自動更新數據長度。程序控制流如圖4所示。

圖3 底層收數模塊程序流程
在SLIP協議解析過程中,需要根據規則,刪除數據中指定位置的數據。例如“幀頭幀尾間如果出現0xc0,則以連續兩個字符0xdb和0xdc取代”這條規則需要刪掉不是幀頭幀尾的0xc0,然后利用上一小節設計的鏈表數據插入模塊在刪掉的位置依次插入0xdc,0xdb。刪除指定位置數據之后,其后數據依次向前移動一位,并讓數據長度減1。程序控制流如圖5所示。
協議解碼模塊的輸入為兩個,一是待解碼的數據長度,二是待解碼的數據緩存區;輸出為消息幀結構體數組,返回值為解析出的消息個數。簡單介紹輸出的消息幀結構體為:
typedef struct
{
UINT16 DataID;
UINT16 DataLen;
UINT8 DataItem[80];
UINT16 CheckSum;
}MessageRev;
該結構體與表1所述數據幀結構一致,DataID代表數據ID,DataLen代表數據長度,DataItem[80]代表有物理意義的數據內容(假設數據內容不會超過80個字符),CheckSum表示校驗和。假設一個周期內傳來的消息不會超過10幀,則需創建一個可存儲10幀消息的結構體數組MessageRev Message[10]。

圖4 鏈表數據插入模塊

圖5 鏈表數據刪除模塊
解碼主要分為三個步驟:①從待解碼的數據緩存區中把兩個c0之間的數據取出,存放在上述鏈表結構中,同時記錄好數據長度,并記錄下解析出的消息幀條數;②如果解析出消息幀條數大于0,就按照SLIP協議的規則,把每一條消息幀中該刪除的刪掉,該插入的插入,得到解析后的數據幀;③從解析好的數據中提取出MessageRev結構體所需的各變量,按順序存放在Message[10]結構體數組中,有幾幀消息就占用數組中的前幾個;返回消息幀條數,這樣就把原始數據解析為可以直接用來判斷處理的上層協議幀格式。
程序流程如圖6所示。

圖6 協議解碼模塊
待發送數據需要根據SLIP協議要求進行編碼,調用ListDelete模塊將需要替換的字符刪除,調用ListInsert模塊插入規定的字符。全部替換完畢后,調用ListInsert模塊在待發送數據開始和結尾分別插入0xc0,完成數據的SLIP協議編碼。具體流程如圖7所示。

圖7 協議編碼模塊
首先測試底層接收數據功能是否正確,測試設備通過232串口向本設備發送數據,波特率460.8 Kbps,測試設備向本設備發送1 247 030個字節,在本設備讀取記錄的接收字節數也是1 247 030,說明基本的接收數據功能正常。
測試設備接著向本設備發送不同類型的數據,包括周期性數據與事件性數據,不同消息幀發送與接收到的幀計數如表2所示。

表2 消息幀解析實驗結果
根據實驗結果可以看到,周期性消息不同ID的數據幀接收到的個數是一致的,且發送與接收到的幀計數相同,說明周期性數據幀接收與解析功能正確。在周期性數據發送過程中中隨機的發送事件性消息,可以看到發送幀計數與接收幀計數相同,說明隨機的事件性數據幀也可以完整接收并解析出來。具體的幀內容,如果消息ID解析正確,則容易根據項目協議解析出來,不再贅述。
本文給出了一種基于SLIP協議的串行數據解析方法,對解析過程中的各個模塊進行了詳細描述。特別描述了數據幀不完整情況下數據的存儲與拼接方法。將各個模塊集成為完整的數據解析軟件并經過大量數據測試,結果表明數據接收無丟數現象,數據幀解析完整,不論周期性數據和事件性數據均無丟幀現象。本方法可適用于大多數串行通訊過程,具有適用面廣、解析方法簡單可靠之優點。