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