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