久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4425|回復: 2
收起左側

單片機解析協議基礎教程(基于51單片機)

[復制鏈接]
ID:262 發表于 2014-10-21 01:12 | 顯示全部樓層 |閱讀模式
        以前喜歡寫一些技術性的文章放到空間讓大家學習一下,這本是好意,不料這段時間翻看一下自己空間的日志,關于技術方面的文章卻是屈指可數,仔細一看內容,卻有一種誤人子弟的感覺,實在慚愧至極。工作快半年了,多多少少也積累了一些經驗,只不過很多東西都是放在腦子里,沒有用文字等記錄下來。最近不停地被坑,心中郁悶至極,今天干活也沒有什么積極性了,放下手下的事情,寫一些文字,就當放松了,轉入正題,下面講解的知識適合于單片機初學者,當然我也算是初學者。
        其實,使用單片機解析協議很簡單,我們需要分析協議的格式,將其分開處理即可。例如某一串口指令模塊的指令格式為:幀頭(2字節)+數據長度(1字節)+數據(N字節,其中N<256),那么我們就按照幀頭、數據長度、數據分別處理。寫單片機程序要有一種思路就是從整體到局部。那么我們如何判斷一個數據幀被完整接收呢?這里就要用到一些標志位來處理了。先給出程序流程圖吧



圖1 程序流程圖





在寫程序之前,首先對幾個變量及標志位進行說明:
    1.幀頭高字節和幀頭低字節分別使用宏定義指定,這樣方便修改,如:
            #define FRM_H  0XAA
            #define FRM_L   0XBB
    2.用于指示接收數據的位置,定義一個變量Rx_POS,定義一個變量Rx_Num用于指示要接收數據的長度。
            unsigned char Rx_POS;
            unsigned char Rx_Num;
    3.接收到完整幀標志位、接收到幀頭標志位、
            bit RXFRMOK;     //接收到完整幀標志位
            bit RXFHOK;        //接收到完整幀頭標志位
    4.接收的數據緩沖區
            unsinged char RXFH[3];
            unsigned char
RX_BUF[32];
         
現在我們使用RXFRMOK來標識接收到完整數據幀,首先要寫的肯定是沒有接收到完整幀的情形。那么我們可以編寫程序如下:
                代碼1:

                void UART1_ISR() interrupt 4
                {
                        unsigned char Rx_Data;
                        if(RI)
                        {
                                Rx_Data = SBUF;//讀取串口緩沖區數據
                                RI = 0;                //清除串口中斷請求
                        }
                        if(!RXFRMOK)//如果沒有接收到完整幀
                        {
                                
                        }
                        if(TI)
                        {
                               TI = 0;
                        }
                 }
        代碼寫到這里,可能就有人急了,怎么才寫這么一點?別急,這是為了讓新手更容易程序是怎么一步步的寫出來的,我將代碼一點點地寫完整,讓新手更加輕松地入門。
        根據從整體到局部的思想,我們把整體框架已完成,那么現在開始處理局部的問題了,仔細一看數據幀格式,開頭是兩個字節的幀頭,那么,開辟兩個數組,其中一個用于緩存幀頭和數據長度,另一個用于緩存串口接收到的數據,緩存幀頭和數據長度的數組定義為RXFH[3],我們假設一次接收的數據不超過32字節,則開辟的數組為RX_BUF[32],設置一個unsigned char型的變量Rx_POS用以指示接收數據的位置,這個位置僅用于指示接收到數據的位置,注意:不包含幀頭和數據。
         準備工作做好后,此刻當然是先處理沒有接收到幀頭的程序了,每接收到一個字節的數據,我們都把緩沖區的數據依次往前挪一個位置,然后判斷第一和第二字節數據是否為幀頭,如果是幀頭,那么第三字節就表示數據長度了,我們把上面的代碼1拷貝過來,繼續添加代碼(添加部分用淺藍色標識):
           代碼2:
                void UART1_ISR() interrupt 4
                {
                        unsigned char Rx_Data;
                        if(RI)
                        {
                                Rx_Data = SBUF;//讀取串口緩沖區數據
                                RI = 0;                //清除串口中斷請求
                        }
                        if(!RXFRMOK)        //如果沒有接收到完整幀
                        {
                                if(!RXFHOK)       //如果沒有接收到幀頭
                                {
                                       
//先移位操作再將緩沖區內容與幀頭進行比較
                                        RXFH[0] = RXFH[1];
                                        RXFH[1] = RXFH[2];
                                        RXFH[2] = Rx_Data;
                                        if((RXFH[0]==FRM_H)&&(RXFH[1]==FRM_L))
                                        {
                                                RXFHOK = 1;            //正常接收到幀頭標志位置1
                                                Rx_Num = RXFH[2];
                                        }        
                                }                           
                        }
                        if(TI)
                        {
                               TI = 0;
                        }
                 }

        看到這里你是不是感覺這種做法有點巧妙呢?判斷幀頭就像把接收到的數據放到一個三字節的窗口中,自己盯著窗口找幀頭,如果發現幀頭匹配,那么我就要處理接收到幀頭的程序了。接收到幀頭處理方法為:每接收到一個字節,則接收到的數據位置Rx_POS加1,如果接收的數據長度等于Rx_Num,則對表示一幀數據接收完成,則應給接收一幀完整數據標志位置位,同時將接收數據位置Rx_P清零,并將接收到幀頭標識為清零。詳見下面的代碼(增加的部分用淺紅色標識):
                 代碼3:

                void UART1_ISR() interrupt 4
                {
                        unsigned char Rx_Data;
                        if(RI)
                        {
                                Rx_Data = SBUF;//讀取串口緩沖區數據
                                RI = 0;                //清除串口中斷請求
                        }
                        if(!RXFRMOK)        //如果沒有接收到完整幀
                        {
                                if(!RXFHOK)       //如果沒有接收到幀頭
                                {
                                       
//先移位操作再將緩沖區內容與幀頭進行比較
                                        RXFH[0] = RXFH[1];
                                        RXFH[1] = RXFH[2];
                                        RXFH[2] = Rx_Data;
                                        if((RXFH[0]==FRM_H)&&(RXFH[1]==FRM_L))
                                        {
                                                RXFHOK = 1;            //正常接收到幀頭標志位置1
                                                Rx_Num = RXFH[2];
                                                goto    TX:                 //接收完數據當然要跳出去了,不能把數據長度存放到數據中了

                                        }        
                                }        
                              
  if(RXFHOK)
                                {
                                       
RXBUF[Rx_POS] = Rx_Data;                                         Rx_POS ++;
                                        if(Rx_POS > Rx_Num-1)
                                        {
                                                RXFRMOK = 1;
                                                RXFHOK = 0;
                                                Rx_POS = 0;
                                         }

                                }                  
                        }
TX:                   if(TI)
                        {
                               TI = 0;
                        }
                 }

     以上代碼3基本上對本文開始的那段協議完成了解析。是不是很簡單?細心看代碼3的人可能會一眼看出來,你這代碼接收到一幀完整數據后,RXFRMOK就為1了,后續該怎么處理呢?應該有個歸零的過程啊,不然怎么再次接收數據呢?這個問題問得很好,這里我沒有把主程序寫出來,因為解析基本上在串口中斷里完成了,當接收到一幀完整數據,其中某一個數據必定有著特定的用途,假設RX_BUF[0]的數據代表接受的數據代表的命令,那么,我在主程序中判斷RX_BUF[0]的值即可,處理完相關任務,我們必須將RXFRMOK清零,那么程序就可以再次接收數據幀了。
        其中有一個特別需要注意的是,有些人沒有注意分析這些代碼,判斷錯誤的數據,導致不能解析該協議,這里特別說明一下:假設單片機接收一幀數據為 0xaa 0xbb 0x03 0x40 0x01 0x06,那么RX_BUF中數據是什么呢?告訴你,只有后面三個字節,即0x40 0x01 0x06。解析協議的時候已經將幀頭和數據長度這三個字節去掉了。  
         有了通信協議,就可以做一個組網的系統,例如醫院里的醫護系統,工廠的工業控制系統,應用范圍廣,安全高效!值得學習!如有興趣,可發郵件至huzhiqianglz@163.com與我交流。本文轉載請注明出處,文中若有錯別字,在所難免,請告之本人修正,謝謝!
        其他類型的協議解析暫未整理,如有空再寫,敬請關注!
回復

使用道具 舉報

ID:28571 發表于 2015-4-18 08:07 | 顯示全部樓層
好東西,學習一下,謝謝分享!
回復

使用道具 舉報

無效樓層,該帖已經被刪除
您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 成人三级av | 成人免费视频在线观看 | 欧美xxxx性 | 一区二区三区国产好 | 国产一区二区精品在线观看 | 亚洲精品一 | av中文字幕在线观看 | 午夜影院普通用户体验区 | 中文字幕影院 | 亚洲欧洲成人 | 天天夜碰日日摸日日澡 | 在线成人www免费观看视频 | 国产视频久久 | 午夜私人影院 | 无码一区二区三区视频 | 日韩欧美三区 | 亚洲精品九九 | 久久毛片 | 欧美一区免费 | 国产视频福利一区 | 亚洲精品国产精品国自产在线 | 中文字幕成人 | 欧美日韩精品久久久免费观看 | 国产综合久久 | 天天色天天射天天干 | 亚洲福利网站 | 国产一区二区精品在线观看 | 久久亚洲精品久久国产一区二区 | 婷婷91| 亚洲免费大片 | 久久国产精品视频 | 国产精品爱久久久久久久 | 免费视频一区 | 免费在线视频精品 | 亚洲电影一区二区三区 | www.天天操| 国产精品视频97 | 在线观看中文视频 | 久操福利 | 韩国av一区二区 | 午夜精品一区二区三区在线视频 |