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