串口數據包解析代(dài)碼分析
2019/1/10 點擊:
這裏以串口作為傳輸媒介,介紹下怎樣來發送接收一個完整的數據包。過程涉及到封包與解包。設計一個良(liáng)好(hǎo)的(de)包(bāo)傳輸機製很有利於數據(jù)傳輸的(de)穩定(dìng)性以及正確性。串口隻(zhī)是一種傳(chuán)輸媒介,這種包機製同時也可以用於SPI,I2C的總線下(xià)的數據傳輸。在單片機通信係統(多機通信(xìn)以(yǐ)及PC與單片機通信)中,是(shì)很常(cháng)見的問題。
一、根據幀頭幀(zhēn)尾或(huò)者幀長檢測一個數據(jù)幀
1、幀頭(tóu)+數據+校驗(yàn)+幀尾
這是一個典型的方案,但(dàn)是對幀頭與幀尾在設計的時候都要注意,也就是說幀(zhēn)頭、幀尾(wěi)不能在所傳輸的數據域中出現,一旦出現可能就被誤判。如果用中斷來接收的話,程(chéng)序(xù)基本可以這麽實現:
unsigned char recstatu;//表示是否處於一(yī)個正(zhèng)在接收數據包的狀(zhuàng)態
unsigned char ccnt; //計數
unsigned char packerflag;//是否接收到(dào)一個完整的數據包標誌
unsigned char rxbuf[100];//接收數據的緩衝區
void UartHandler()
{
unsigned char tmpch;
tmpch = UARTRBR;
if(tmpch 是包(bāo)頭) //檢測是否是包頭
{
recstatu = 1;
ccnt = 0 ;
packerflag = 0;
return ;
}
if(tmpch是包(bāo)尾) //檢測是否是包尾
{
recstatu = 0;
packerflag = 1; //用於告知係統已經接收到一個完整的數據包
return ;
}
if(recstatu ==1) //是否處於接收數據包狀態
{
rxbuf[ccnt++] = tmpch;
}
}
上麵也就是接收一(yī)個數據包,但是再次(cì)提醒,包頭和包尾不能在數據域中出現,一旦出(chū)現將會出現誤判。另外一個。數據的校驗算(suàn)法是很必要(yào)的(de),在數據傳輸中,由於受(shòu)到幹擾,很難(nán)免有時出現數據錯誤,加上校驗碼可在發現數據傳輸錯誤時,可(kě)以要求數據的另一方重新發送,或是進行簡單的丟棄處理。校驗算(suàn)法不一定要很複雜,普通的加和,異或,以及循環冗餘都是可以(yǐ)的。我上麵的接收程序在接收(shōu)數據時,已經將包頭和包尾去掉,這些可以根據自己的需求加上,關鍵是要理解原理。
上述包協議出現了以下的(de)幾種變種:
1.1 幀頭+數據長度+數(shù)據+校(xiào)驗(yàn)值
1.2包(bāo)長+校驗值
上麵兩(liǎng)種其實都是知道了數據包的長度(dù),然後根據接收字節的長度來判斷一個(gè)完整的數據包。例如,定義一個數據包的長度為256字節(jiē),那我們就可以一直接收,直到接收到256個字節,就認為是一個數據包。但是,會不會存在問題呢?比如(rú)說從機向主機發送數據,發送(sòng)了一半,掉電,重啟,開機後繼續發送,這(zhè)很明顯接收到的數據就不對了,所以此時(shí)很有必要定義一(yī)個超限時間,比如我們可以(yǐ)維護(hù)下麵這樣的一個結構體(tǐ)。
struct uartrd{
char rd[ 256];
unsigned int timeout;
}
成員變(biàn)量rd用來存放(fàng)接收到的數據字節;成員變量timeout用(yòng)來維護超時值(zhí),這(zhè)裏主要討(tǎo)論這個。這個數值怎麽維護呢,可以用一個定時器來維護,以可以放在普通的滴答中斷裏麵來維(wéi)護,也可以根據係統運行一(yī)條(tiáo)指令的周期,在自己的循(xún)環中來(lái)維護,給其設置(zhì)個初值,比如說100,當有第一個數據到來以後,timeout在(zài)指定(dìng)的時間就會減(jiǎn)少1,減少到0時,就認(rèn)為超時,不(bú)論是否接收到足(zú)夠的數據,都應該拋棄。
二、根據接收超時來判斷一個數據包
2.1 數據+校(xiào)驗
核心思想是如果在達(dá)到一定的時間沒有接受到數據,就認為數據包接收完成。modbus協議裏就有通過時間間隔來判斷幀結束的。具體實現是要使用一個定時器,在接收到第一個數據時候,開啟定時器,在接收(shōu)到一個數據(jù)時(shí)候,就(jiù)將定時器清(qīng)零,讓定時器重新開始計時,如果設定的超時時(shí)間到(超時時間長度可以設置為5個正常接收的周期),則認為(wéi)在這一段時間內沒有接(jiē)受到新的數(shù)據,就認為接收到一個完(wán)整的數據包了。
一、根據幀頭幀(zhēn)尾或(huò)者幀長檢測一個數據(jù)幀
1、幀頭(tóu)+數據+校驗(yàn)+幀尾
這是一個典型的方案,但(dàn)是對幀頭與幀尾在設計的時候都要注意,也就是說幀(zhēn)頭、幀尾(wěi)不能在所傳輸的數據域中出現,一旦出現可能就被誤判。如果用中斷來接收的話,程(chéng)序(xù)基本可以這麽實現:
unsigned char recstatu;//表示是否處於一(yī)個正(zhèng)在接收數據包的狀(zhuàng)態
unsigned char ccnt; //計數
unsigned char packerflag;//是否接收到(dào)一個完整的數據包標誌
unsigned char rxbuf[100];//接收數據的緩衝區
void UartHandler()
{
unsigned char tmpch;
tmpch = UARTRBR;
if(tmpch 是包(bāo)頭) //檢測是否是包頭
{
recstatu = 1;
ccnt = 0 ;
packerflag = 0;
return ;
}
if(tmpch是包(bāo)尾) //檢測是否是包尾
{
recstatu = 0;
packerflag = 1; //用於告知係統已經接收到一個完整的數據包
return ;
}
if(recstatu ==1) //是否處於接收數據包狀態
{
rxbuf[ccnt++] = tmpch;
}
}
上麵也就是接收一(yī)個數據包,但是再次(cì)提醒,包頭和包尾不能在數據域中出現,一旦出(chū)現將會出現誤判。另外一個。數據的校驗算(suàn)法是很必要(yào)的(de),在數據傳輸中,由於受(shòu)到幹擾,很難(nán)免有時出現數據錯誤,加上校驗碼可在發現數據傳輸錯誤時,可(kě)以要求數據的另一方重新發送,或是進行簡單的丟棄處理。校驗算(suàn)法不一定要很複雜,普通的加和,異或,以及循環冗餘都是可以(yǐ)的。我上麵的接收程序在接收(shōu)數據時,已經將包頭和包尾去掉,這些可以根據自己的需求加上,關鍵是要理解原理。
上述包協議出現了以下的(de)幾種變種:
1.1 幀頭+數據長度+數(shù)據+校(xiào)驗(yàn)值
1.2包(bāo)長+校驗值
上麵兩(liǎng)種其實都是知道了數據包的長度(dù),然後根據接收字節的長度來判斷一個(gè)完整的數據包。例如,定義一個數據包的長度為256字節(jiē),那我們就可以一直接收,直到接收到256個字節,就認為是一個數據包。但是,會不會存在問題呢?比如(rú)說從機向主機發送數據,發送(sòng)了一半,掉電,重啟,開機後繼續發送,這(zhè)很明顯接收到的數據就不對了,所以此時(shí)很有必要定義一(yī)個超限時間,比如我們可以(yǐ)維護(hù)下麵這樣的一個結構體(tǐ)。
struct uartrd{
char rd[ 256];
unsigned int timeout;
}
成員變(biàn)量rd用來存放(fàng)接收到的數據字節;成員變量timeout用(yòng)來維護超時值(zhí),這(zhè)裏主要討(tǎo)論這個。這個數值怎麽維護呢,可以用一個定時器來維護,以可以放在普通的滴答中斷裏麵來維(wéi)護,也可以根據係統運行一(yī)條(tiáo)指令的周期,在自己的循(xún)環中來(lái)維護,給其設置(zhì)個初值,比如說100,當有第一個數據到來以後,timeout在(zài)指定(dìng)的時間就會減(jiǎn)少1,減少到0時,就認(rèn)為超時,不(bú)論是否接收到足(zú)夠的數據,都應該拋棄。
二、根據接收超時來判斷一個數據包
2.1 數據+校(xiào)驗
核心思想是如果在達(dá)到一定的時間沒有接受到數據,就認為數據包接收完成。modbus協議裏就有通過時間間隔來判斷幀結束的。具體實現是要使用一個定時器,在接收到第一個數據時候,開啟定時器,在接收(shōu)到一個數據(jù)時(shí)候,就(jiù)將定時器清(qīng)零,讓定時器重新開始計時,如果設定的超時時(shí)間到(超時時間長度可以設置為5個正常接收的周期),則認為(wéi)在這一段時間內沒有接(jiē)受到新的數(shù)據,就認為接收到一個完(wán)整的數據包了。
簡單的小的總結,上述幾(jǐ)種方法(fǎ)都還是較為常用的,在具體的實現上,可以根據具體的實(shí)際情況,設(shè)計出具體的通訊協議。數據校驗位,有時候(hòu)感覺不出(chū)來其重要性,但是一定要加上,對數據進行一個相關的驗證還是必要的。現在很在(zài)MCU都帶有FIFO,DMA等功能,所以有時候利(lì)用上這些特性,可以設計出更好的通訊方(fāng)式。有的人問在(zài)接受串口數據時候是應該中斷一次接收一個,還是進入中斷後(hòu)接收一段數據(jù)呢,我認為應該中斷接收一個,因為CPU是很快的,至少對於串(chuàn)口是這樣,在(zài)接(jiē)受每個數據(jù)的間隔期間,處理器還是可以做些其他工作的。這是在裸機下的模型。在多線程中,那就可以(yǐ)直接建立一個數據(jù)接收線程。
- 上一篇:Unity3d 動態(tài)加載模型文件的方法 2019/1/22
- 下一篇:unity3d中協程Coroutine的的原理及使用 2019/1/9