這裏以串口做爲傳輸媒介,介紹下怎樣來發送接收一個完整的數據包。過程涉及到封包與解包。設計一個良好的包傳輸機制頗有利於數據傳輸的穩定性以及正確性。串口只是一種傳輸媒介,這種包機制同時也能夠用於SPI,I2C的總線下的數據傳輸。在單片機通訊系統(多機通訊以及PC與單片機通訊)中,是很常見的問題。 算法
1、根據幀頭幀尾或者幀長檢測一個數據幀 多線程
一、幀頭+數據+校驗+幀尾 線程
這是一個典型的方案,可是對幀頭與幀尾在設計的時候都要注意,也就是說幀頭、幀尾不能在所傳輸的數據域中出現,一旦出現可能就被誤判。若是用中斷來接收的話,程序基本能夠這麼實現: 設計
unsigned char recstatu;//表示是否處於一個正在接收數據包的狀態 3d
unsigned char ccnt; //計數 blog
unsigned char packerflag;//是否接收到一個完整的數據包標誌 get
unsigned char rxbuf[100];//接收數據的緩衝區 it
void UartHandler() 變量
{ 原理
unsigned char tmpch;
tmpch = UARTRBR;
if(tmpch 是包頭) //檢測是不是包頭
{
recstatu = 1;
ccnt = 0 ;
packerflag = 0;
return ;
}
if(tmpch是包尾) //檢測是不是包尾
{
recstatu = 0;
packerflag = 1; //用於告知系統已經接收到一個完整的數據包
return ;
}
if(recstatu ==1) //是否處於接收數據包狀態
{
rxbuf[ccnt++] = tmpch;
}
}
上面也就是接收一個數據包,可是再次提醒,包頭和包尾不能在數據域中出現,一旦出現將會出現誤判。另一個。數據的校驗算法是很必要的,在數據傳輸中,因爲受到干擾,很不免有時出現數據錯誤,加上校驗碼可在發現數據傳輸錯誤時,能夠要求數據的另外一方從新發送,或是進行簡單的丟棄處理。校驗算法不必定要很複雜,普通的加和,異或,以及循環冗餘都是能夠的。我上面的接收程序在接收數據時,已經將包頭和包尾去掉,這些能夠根據本身的需求加上,關鍵是要理解原理。
上述包協議出現瞭如下的幾種變種:
1.1 幀頭+數據長度+數據+校驗值
1.2包長+校驗值
上面兩種其實都是知道了數據包的長度,而後根據接收字節的長度來判斷一個完整的數據包。例如,定義一個數據包的長度爲256字節,那咱們就能夠一直接收,直到接收到256個字節,就認爲是一個數據包。可是,會不會存在問題呢?好比說從機向主機發送數據,發送了一半,掉電,重啓,開機後繼續發送,這很明顯接收到的數據就不對了,因此此時頗有必要定義一個超限時間,好比咱們能夠維護下面這樣的一個結構體。
struct uartrd{
char rd[ 256];
unsigned int timeout;
}
成員變量rd用來存放接收到的數據字節;成員變量timeout用來維護超時值,這裏主要討論這個。這個數值怎麼維護呢,能夠用一個定時器來維護,也能夠放在普通的滴答中斷裏面來維護,也能夠根據系統運行一條指令的週期,在本身的循環中來維護,給其設置個初值,好比說100,當有第一個數據到來之後,timeout在指定的時間就會減小1,減小到0時,就認爲超時,不管是否接收到足夠的數據,都應該拋棄。
2、根據接收超時來判斷一個數據包
2.1 數據+校驗
核心思想是若是在達到必定的時間沒有接受到數據,就認爲數據包接收完成。modbus協議裏就有經過時間間隔來判斷幀結束的。具體實現是要使用一個定時器,在接收到第一個數據時候,開啓定時器,在接收到一個數據時候,就將定時器清零,讓定時器從新開始計時,若是設定的超時時間到(超時時間長度能夠設置爲5個正常接收的週期),則認爲在這一段時間內沒有接受到新的數據,就認爲接收到一個完整的數據包了。流程大致以下圖所示:
進行一個簡單的小的總結,上述幾種方法都仍是較爲經常使用的,在具體的實現上,能夠根據具體的實際狀況,設計出具體的通信協議。數據校驗位,有時候感受不出來其重要性,可是必定要加上,對數據進行一個相關的驗證仍是必要的。如今不少MCU都帶有FIFO,DMA等功能,因此有時候利用上這些特性,能夠設計出更好的通信方式。有的人問在接受串口數據時候是應該中斷一次接收一個,仍是進入中斷後接收一段數據呢,我認爲應該中斷接收一個,由於CPU是很快的,至少對於串口是這樣,在接受每一個數據的間隔期間,處理器仍是能夠作些其餘工做的。這是在裸機下的模型。在多線程中,那就能夠直接創建一個數據接收線程。