新聞中心

        EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > 串口中怎樣接收一個完整數(shù)據(jù)包的解析

        串口中怎樣接收一個完整數(shù)據(jù)包的解析

        作者: 時間:2016-11-09 來源:網(wǎng)絡 收藏
        這里以串口作為傳輸媒介,介紹下怎樣來發(fā)送接收一個完整的數(shù)據(jù)包。過程涉及到封包與解包。設計一個良好的包傳輸機制很有利于數(shù)據(jù)傳輸?shù)姆€(wěn)定性以及正確性。串口只是一種傳輸媒介,這種包機制同時也可以用于SPI,I2C的總線下的數(shù)據(jù)傳輸。在單片機通信系統(tǒng)(多機通信以及PC與單片機通信)中,是很常見的問題。

        一、根據(jù)幀頭幀尾或者幀長檢測一個數(shù)據(jù)幀

        本文引用地址:http://www.104case.com/article/201611/318039.htm

        1、幀頭+數(shù)據(jù)+校驗+幀尾

        這是一個典型的方案,但是對幀頭與幀尾在設計的時候都要注意,也就是說幀頭、幀尾不能在所傳輸?shù)臄?shù)據(jù)域中出現(xiàn),一旦出現(xiàn)可能就被誤判。如果用中斷來接收的話,程序基本可以這么實現(xiàn):

        unsigned char recstatu;//表示是否處于一個正在接收數(shù)據(jù)包的狀態(tài)

        unsigned charccnt; //計數(shù)

        unsigned char packerflag;//是否接收到一個完整的數(shù)據(jù)包標志

        unsigned charrxbuf[100];//接收數(shù)據(jù)的緩沖區(qū)

        void UartHandler()

        {

        unsigned char tmpch;

        tmpch = UARTRBR;

        if(tmpch 是包頭) //檢測是否是包頭

        {

        recstatu = 1;

        ccnt = 0 ;

        packerflag = 0;

        return ;

        }

        if(tmpch是包尾) //檢測是否是包尾

        {

        recstatu = 0;

        packerflag = 1; //用于告知系統(tǒng)已經(jīng)接收到一個完整的數(shù)據(jù)包

        return ;

        }

        if(recstatu ==1) //是否處于接收數(shù)據(jù)包狀態(tài)

        {

        rxbuf[ccnt++] = tmpch;

        }

        }

        上面也就是接收一個數(shù)據(jù)包,但是再次提醒,包頭和包尾不能在數(shù)據(jù)域中出現(xiàn),一旦出現(xiàn)將會出現(xiàn)誤判。另外一個。數(shù)據(jù)的校驗算法是很必要的,在數(shù)據(jù)傳輸中,由于受到干擾,很難免有時出現(xiàn)數(shù)據(jù)錯誤,加上校驗碼可在發(fā)現(xiàn)數(shù)據(jù)傳輸錯誤時,可以要求數(shù)據(jù)的另一方重新發(fā)送,或是進行簡單的丟棄處理。校驗算法不一定要很復雜,普通的加和,異或,以及循環(huán)冗余都是可以的。我上面的接收程序在接收數(shù)據(jù)時,已經(jīng)將包頭和包尾去掉,這些可以根據(jù)自己的需求加上,關鍵是要理解原理。

        上述包協(xié)議出現(xiàn)了以下的幾種變種:

        1.1 幀頭+數(shù)據(jù)長度+數(shù)據(jù)+校驗值

        1.2包長+校驗值

        上面兩種其實都是知道了數(shù)據(jù)包的長度,然后根據(jù)接收字節(jié)的長度來判斷一個完整的數(shù)據(jù)包。例如,定義一個數(shù)據(jù)包的長度為256字節(jié),那我們就可以一直接收,直到接收到256個字節(jié),就認為是一個數(shù)據(jù)包。但是,會不會存在問題呢?比如說從機向主機發(fā)送數(shù)據(jù),發(fā)送了一半,掉電,重啟,開機后繼續(xù)發(fā)送,這很明顯接收到的數(shù)據(jù)就不對了,所以此時很有必要定義一個超限時間,比如我們可以維護下面這樣的一個結(jié)構體。

        struct uartrd{

        char rd[ 256];

        unsigned int timeout;

        }

        成員變量rd用來存放接收到的數(shù)據(jù)字節(jié);成員變量timeout用來維護超時值,這里主要討論這個。這個數(shù)值怎么維護呢,可以用一個定時器來維護,以可以放在普通的滴答中斷里面來維護,也可以根據(jù)系統(tǒng)運行一條指令的周期,在自己的循環(huán)中來維護,給其設置個初值,比如說100,當有第一個數(shù)據(jù)到來以后,timeout在指定的時間就會減少1,減少到0時,就認為超時,不論是否接收到足夠的數(shù)據(jù),都應該拋棄。

        二、根據(jù)接收超時來判斷一個數(shù)據(jù)包

        2.1 數(shù)據(jù)+校驗

        核心思想是如果在達到一定的時間沒有接受到數(shù)據(jù),就認為數(shù)據(jù)包接收完成。modbus協(xié)議里就有通過時間間隔來判斷幀結(jié)束的。具體實現(xiàn)是要使用一個定時器,在接收到第一個數(shù)據(jù)時候,開啟定時器,在接收到一個數(shù)據(jù)時候,就將定時器清零,讓定時器重新開始計時,如果設定的超時時間到(超時時間長度可以設置為5個正常接收的周期),則認為在這一段時間內(nèi)沒有接受到新的數(shù)據(jù),就認為接收到一個完整的數(shù)據(jù)包了。流程大體如下圖所示:

        進行一個簡單的小的總結(jié),上述幾種方法都還是較為常用的,在具體的實現(xiàn)上,可以根據(jù)具體的實際情況,設計出具體的通訊協(xié)議。數(shù)據(jù)校驗位,有時候感覺不出來其重要性,但是一定要加上,對數(shù)據(jù)進行一個相關的驗證還是必要的。現(xiàn)在很在MCU都帶有FIFO,DMA等功能,所以有時候利用上這些特性,可以設計出更好的通訊方式。有的人問在接受串口數(shù)據(jù)時候是應該中斷一次接收一個,還是進入中斷后接收一段數(shù)據(jù)呢,我認為應該中斷接收一個,因為CPU是很快的,至少對于串口是這樣,在接受每個數(shù)據(jù)的間隔期間,處理器還是可以做些其他工作的。這是在裸機下的模型。在多線程中,那就可以直接建立一個數(shù)據(jù)接收線程。



        評論


        技術專區(qū)

        關閉
        主站蜘蛛池模板: 镇江市| 珲春市| 灵寿县| 额敏县| 江孜县| 山阳县| 当阳市| 辛集市| 隆回县| 黄大仙区| 孙吴县| 颍上县| 金昌市| 龙胜| 滦平县| 正定县| 周口市| 洪江市| 泰安市| 无为县| 铁岭市| 黑龙江省| 焉耆| 闽侯县| 临武县| 桓台县| 五峰| 商水县| 兴隆县| 张掖市| 沈丘县| 大埔区| 汝城县| 察哈| 苍山县| 兴义市| 岐山县| 平定县| 佳木斯市| 美姑县| 共和县|