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