一、為什么串口接收的數(shù)據(jù)需要處理
我們在做項目的時候經(jīng)常會用到串口,當(dāng)我們用串口和別的設(shè)備通訊的時候就需要嚴(yán)格遵循通訊協(xié)議,然而,僅僅是遵循通訊協(xié)議是不夠的,因為單片機串口受到別的信號干擾的時候,容易出現(xiàn)數(shù)據(jù)錯誤,特別是串口發(fā)送的第一個字節(jié)和最后一個字節(jié)。一旦出現(xiàn)這種情況,設(shè)備之間的通訊可能會受到影響,甚至?xí)?dǎo)致系統(tǒng)癱瘓。另外,串口收到數(shù)據(jù)的時候,我們也需要判斷一幀數(shù)據(jù)的長度,特別是指令發(fā)送比較頻繁的時候。因此,串口在接收到數(shù)據(jù)之后應(yīng)該先進行數(shù)據(jù)處理,再執(zhí)行命令,這樣能夠增強產(chǎn)品的穩(wěn)定性。
二、串口接收重點關(guān)注的幾個標(biāo)志
為了保證通訊的穩(wěn)定性,一般的通訊協(xié)議會加入幀頭、幀尾、數(shù)據(jù)長度、校驗這四個標(biāo)志中的一個或多個。它們的作用如下:
1、幀頭:串口發(fā)送數(shù)據(jù)的第一個字節(jié)是最容易出錯的,如果你把重要的指令放在第一個字節(jié),一旦出現(xiàn)錯誤,可能會使從機執(zhí)行錯誤的操作。而幀頭能夠有效規(guī)避這個問題。
2、幀尾:和幀頭類似,幀尾也能避免最后一個字節(jié)出錯,同時,它也可以作為接收端接收完成的標(biāo)志。
3、數(shù)據(jù)長度:它可以作為接收端接收完成的標(biāo)志。有時也能作為判斷數(shù)據(jù)是否正確的標(biāo)志。
4、校驗:能夠有效避免校驗以外的所有數(shù)據(jù)的錯誤,但是校驗正確不代表數(shù)據(jù)一定沒有出錯,每種校驗方式都有一定的缺陷。
幀頭、幀尾、數(shù)據(jù)長度和校驗,這四種標(biāo)志加起來之后能夠大大的增強數(shù)據(jù)傳輸的穩(wěn)定性,但不是每個通訊協(xié)議都會包含以上四個標(biāo)志,可能只會用到其中的一兩個。因為如果要發(fā)送的主要數(shù)據(jù)本身就比較長,加上這個幾個標(biāo)志之后會更長,這對于那種傳輸速度慢、傳輸數(shù)據(jù)時間長、傳輸指令頻繁處理速度慢的設(shè)備來說,較長的指令可能會影響工作效率。具體我就不多說了,我今天主要講的是接收數(shù)據(jù)的處理方法,大家根據(jù)自己的協(xié)議選擇合適的處理方法就行了。
三、常用的幾種數(shù)據(jù)處理方法
1、判斷幀頭:串口接收到第一個數(shù)據(jù)之后先判斷是否是幀頭,如果幀頭正確就存起來繼續(xù)收,反之則丟掉繼續(xù)等幀頭。示例代碼片段如下:
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷
{
Res = USART_ReceiveData(USART1);//讀取接收到的數(shù)據(jù),同時也清除了中斷標(biāo)志位
USART_RX_BUF[USART_RX_STA ++] = Res;
if(USART_RX_BUF[0] != 0xA5 && USART_RX_STA == 1)
{//幀頭錯誤
USART_RX_STA = 0;//重新接收
}
if(USART_RX_STA >= USART_RX_Len)
{//接收完成
USART_RX_STA = 0;
USART_RXHANDLE_FLAG = 1;
}
}
這種處理方法能夠有效避免第一個字節(jié)出錯的問題,我就試過一次主設(shè)備那邊傳過來的數(shù)據(jù)幀頭前面多了一個字節(jié)(可能是剛開始傳輸?shù)臅r候電壓不穩(wěn)定產(chǎn)生的紋波),用這種方法就能夠之間把第一個錯誤的數(shù)據(jù)丟掉,從正確的幀頭開始接收。但是這種方法不能夠檢查幀頭后面的數(shù)據(jù)是否正確。
2、判斷幀尾:可以把幀尾作為一幀數(shù)據(jù)接收完成的標(biāo)志。另外,當(dāng)接收緩存存了多個指令的時候,幀尾能夠幫助我們在一堆數(shù)據(jù)中區(qū)分出哪些數(shù)據(jù)是同一個指令的。當(dāng)然,如果僅僅是區(qū)分?jǐn)?shù)據(jù)用幀頭也可以。不過這種辦法必須保證幀尾和其他數(shù)據(jù)不一樣,不然就會出現(xiàn)錯誤的判斷。所以有些人為了避免這個問題會用兩個字節(jié)作為幀尾,不過這樣一來,數(shù)據(jù)長度就更大了,影響通訊效率。
3、根據(jù)數(shù)據(jù)長度判斷是否完成接收:可以通過數(shù)據(jù)長度判斷接收是否完成。如果協(xié)議里面的指令長度不是統(tǒng)一的,我們就不能根據(jù)固定的長度來接收數(shù)據(jù)。這個時候在一幀數(shù)據(jù)里面加入數(shù)據(jù)長度這個標(biāo)志,就能夠給單片機一個判斷的準(zhǔn)則,單片機接收到數(shù)據(jù)長度這個標(biāo)志之后,根據(jù)這個長度來接收剩下的數(shù)據(jù)。示例代碼片段如下:
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷
{
Res = USART_ReceiveData(USART1);//讀取接收到的數(shù)據(jù),同時也清除了中斷標(biāo)志位
USART_RX_BUF[USART_RX_STA ++] = Res;
if(USART_RX_BUF[0] != 0xA5 && USART_RX_STA == 1)
{//幀頭錯誤
USART_RX_STA = 0;//重新接收
}
If(USART_RX_STA == 4)
{
USART_RX_Len = USART_RX_BUF[3];//數(shù)據(jù)長度
}
if(USART_RX_STA >= (USART_RX_Len + 4))
{//接收完成
USART_RX_STA = 0;
USART_RXHANDLE_FLAG = 1;
}
}
4、根據(jù)接收時間判斷一幀數(shù)據(jù)的長度。根據(jù)波特率計算出兩個字節(jié)傳輸?shù)臅r間間隔,接收到數(shù)據(jù)之后定時器開始計時,在定時器中斷觸發(fā)之前收到數(shù)據(jù)就清空,重新計時,超過兩個字節(jié)的間隔時間,就認(rèn)為是一幀數(shù)據(jù)接收完成。具體的程序我就不寫了,這個網(wǎng)上能找到很多例程。這種方法適合接收長度不定的情況,在這個方法的基礎(chǔ)上還可以加上幀頭幀尾等標(biāo)志,增強穩(wěn)定性。
5、校驗處理:校驗一般是在接收完成之后進行,校驗是很必要的,因為它包含一幀數(shù)據(jù)的所有字節(jié),通過校驗?zāi)軌虼蟠蟮臏p少出錯的概率。
四、總結(jié)
其實串口接收數(shù)據(jù)處理主要要注意兩點,第一點是單片機如何確定一幀數(shù)據(jù)接收完成,第二點是單片機如果判斷接收到的數(shù)據(jù)是正確的指令。第一點可以通過幀尾,數(shù)據(jù)長度等標(biāo)志確定接收完成。第二點可以先通過幀頭初步判斷指令的正確性,再通過校驗二次處理,判斷指令是否正確接收。
關(guān)于串口接收數(shù)據(jù)處理的相關(guān)內(nèi)容就介紹到這里,如果還有什么問題,可以留言,如果文章有哪里寫的不對,歡迎指正,謝謝!