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

標(biāo)題: 玩轉(zhuǎn)單片機(jī)之--串口通信,接收數(shù)據(jù) [打印本頁(yè)]

作者: jialinx    時(shí)間: 2015-9-15 20:23
標(biāo)題: 玩轉(zhuǎn)單片機(jī)之--串口通信,接收數(shù)據(jù)
單片機(jī)接收代碼

#include <reg51.h>
#define uchar unsigned char    //byte
#define uint unsigned int    //word
sbit led1=P0^0;     
sbit fir=P2^4; //fir=0;工作
sbit sec=P2^5; //sec=0;工作
sbit thi=P2^6; //thi=0;工作
sbit fot=P2^7; //fot=0;工作
uchar table[]={0x28,0xeb,0x32,0xa2,0xe1,0xa4,0x24,0xea,0x20,0xa0};
//P1=table[ i];/* 0    1    2   3    4    5    6    7    8    9 */
static uchar dispbuf[5];
//動(dòng)態(tài)顯示數(shù)字的函數(shù)
void scandisp(void)
{
       unsigned int i;
       fir=0;
       P1=table[ dispbuf[0] ];
       for(i=0;i<200;i++);
fir=1;
       sec=0;
       P1=table[ dispbuf[1] ];
       for(i=0;i<200;i++);
sec=1;
       thi=0;
       P1=table[ dispbuf[2] ];
       for(i=0;i<200;i++);
thi=1;
       fot=0;
       P1=table[ dispbuf[3] ];
       for(i=0;i<200;i++);
fot=1;
}   
//十六進(jìn)制轉(zhuǎn)十進(jìn)制存儲(chǔ)
void HEX_TO_BCD(unsigned int n)
{
       dispbuf[3]=n/1000;
       dispbuf[2]=(n/100)%10;
       dispbuf[1]=(n/10)%10;
       dispbuf[0]=n%10;
}
void main(void)
{   
     uchar a;
       uint mydata;
       mydata=0x00;
       TMOD=0x20;
       PCON=0x00;
       SCON=0x50;
       TL1=0xfd;
       TH1=0xfd;
       TR1=1;
       while(1)  //動(dòng)態(tài)現(xiàn)實(shí)是接收的數(shù)據(jù)
       {     //如果沒(méi)有接收到數(shù)據(jù),RI=0,一直循環(huán)顯示原值
              //如果有接收到數(shù)據(jù),RI=1,跳出循環(huán)重新計(jì)算并再次進(jìn)入循環(huán)
              HEX_TO_BCD(mydata);
              while(RI==0)  scandisp();
              RI=0;     //重新置0
              a=SBUF;   //從緩沖區(qū)獲取數(shù)據(jù)
           mydata=a;
                    
              //HEX_TO_BCD(mydata);
              //scandisp();
              //SBUF=a;
              //while(TI==0)
              //TI=0;
       }
}
1. 自定義數(shù)據(jù)通信協(xié)議
    這里所說(shuō)的數(shù)據(jù)協(xié)議是建立在物理層之上的通信數(shù)據(jù)包格式。所謂通信的物理層就是指我們通常所用到的RS232、RS485、紅外、光纖、無(wú)線等等通信方式。在這個(gè)層面上,底層軟件提供兩個(gè)基本的操作函數(shù):發(fā)送一個(gè)字節(jié)數(shù)據(jù)、接收一個(gè)字節(jié)數(shù)據(jù)。所有的數(shù)據(jù)協(xié)議全部建立在這兩個(gè)操作方法之上。
通信中的數(shù)據(jù)往往以數(shù)據(jù)包的形式進(jìn)行傳送的,我們把這樣的一個(gè)數(shù)據(jù)包稱作為一幀數(shù)據(jù)。類似于網(wǎng)絡(luò)通信中的TCPIP協(xié)議一般,比較可靠的通信協(xié)議往往包含有以下幾個(gè)組成部分:幀頭、地址信息、數(shù)據(jù)類型、數(shù)據(jù)長(zhǎng)度、數(shù)據(jù)塊、校驗(yàn)碼、幀尾。
    幀頭和幀尾用于數(shù)據(jù)包完整性的判別,通常選擇一定長(zhǎng)度的固定字節(jié)組成,要求是在整個(gè)數(shù)據(jù)鏈中判別數(shù)據(jù)包的誤碼率越低越好。減小固定字節(jié)數(shù)據(jù)的匹配機(jī)會(huì),也就是說(shuō)使幀頭和幀尾的特征字節(jié)在整個(gè)數(shù)據(jù)鏈中能夠匹配的機(jī)會(huì)最小。通常有兩種做法,一、減小特征字節(jié)的匹配幾率。二、增加特征字節(jié)的長(zhǎng)度。通常選取第一種方法的情況是整個(gè)數(shù)據(jù)鏈路中的數(shù)據(jù)不具有隨即性,數(shù)據(jù)可預(yù)測(cè),可以通過(guò)人為選擇幀頭和幀尾的特征字來(lái)避開(kāi),從而減小特征字節(jié)的匹配幾率。使用第二種方法的情況更加通用,適合于數(shù)據(jù)隨即的場(chǎng)合。通過(guò)增加特征字節(jié)的長(zhǎng)度減小匹配幾率,雖然不能夠完全的避免匹配的情況,但可以使匹配幾率大大減小,如果碰到匹配的情況也可以由校驗(yàn)碼來(lái)進(jìn)行檢測(cè),因此這種情況在絕大多說(shuō)情況下比較可靠。
    地址信息主要用于多機(jī)通信中,通過(guò)地址信息的不同來(lái)識(shí)別不同的通信終端。在一對(duì)多的通信系統(tǒng)中,可以只包含目的地址信息。同時(shí)包含源地址和目的地址則適用于多對(duì)多的通信系統(tǒng)。
    數(shù)據(jù)類型、數(shù)據(jù)長(zhǎng)度和數(shù)據(jù)塊是主要的數(shù)據(jù)部分。數(shù)據(jù)類型可以標(biāo)識(shí)后面緊接著的是命令還是數(shù)據(jù)。數(shù)據(jù)長(zhǎng)度用于指示有效數(shù)據(jù)的個(gè)數(shù)。
    校驗(yàn)碼則用來(lái)檢驗(yàn)數(shù)據(jù)的完整性和正確性。通常對(duì)數(shù)據(jù)類型、數(shù)據(jù)長(zhǎng)度和數(shù)據(jù)塊三個(gè)部分進(jìn)行相關(guān)的運(yùn)算得到。最簡(jiǎn)單的做法可是對(duì)數(shù)據(jù)段作累加和,復(fù)雜的也可以對(duì)數(shù)據(jù)進(jìn)行CRC運(yùn)算等等,可以根據(jù)運(yùn)算速度、容錯(cuò)度等要求來(lái)選取。
2. 上位機(jī)和下位機(jī)中的數(shù)據(jù)發(fā)送
    物理通信層中提供了兩個(gè)基本的操作函數(shù),發(fā)送一個(gè)字節(jié)數(shù)據(jù)則為數(shù)據(jù)發(fā)送的基礎(chǔ)。數(shù)據(jù)包的發(fā)送即把數(shù)據(jù)包中的左右字節(jié)按照順序一個(gè)一個(gè)的發(fā)送數(shù)據(jù)而已。當(dāng)然發(fā)送的方法也有不同。
    在單片機(jī)系統(tǒng)中,比較常用的方法是直接調(diào)用串口發(fā)送單個(gè)字節(jié)數(shù)據(jù)的函數(shù)。這種方法的缺點(diǎn)是需要處理器在發(fā)送過(guò)程中全程參與,優(yōu)點(diǎn)是所要發(fā)送的數(shù)據(jù)能夠立即的出現(xiàn)在通信線路上,能夠立即被接收端接收到。另外一種方法是采用中斷發(fā)送的方式,所有需要發(fā)送的數(shù)據(jù)被送入一個(gè)緩沖區(qū),利用發(fā)送中斷將緩沖區(qū)中的數(shù)據(jù)發(fā)送出去。這種方法的優(yōu)點(diǎn)是占用處理器資源小,但是可能出現(xiàn)需要發(fā)送的數(shù)據(jù)不能立即被發(fā)送的情況,不過(guò)這種時(shí)延相當(dāng)?shù)男 ?duì)于51系列單片機(jī),比較傾向于采用直接發(fā)送的方式,采用中斷發(fā)送的方式比較占用RAM資源,而且對(duì)比直接發(fā)送來(lái)說(shuō)也沒(méi)有太多的優(yōu)點(diǎn)。以下是51系列單片機(jī)中發(fā)送單個(gè)字節(jié)的函數(shù)。
void SendByte(unsigned char ch)
{
     SBUF = ch;
     while(TI == 0);
     TI = 0;
}
    上位機(jī)中關(guān)于串口通信的方式也有多種,這種方式不是指數(shù)據(jù)有沒(méi)有緩沖的問(wèn)題,而是操作串口的方式不同,因?yàn)镻C上數(shù)據(jù)發(fā)送基本上都會(huì)被緩沖后再發(fā)送。對(duì)于編程來(lái)說(shuō)操作串口有三種方式,一、使用windows系統(tǒng)中自帶的串口通信控件,這種方式使用起來(lái)比較簡(jiǎn)單,需要注意的是接收時(shí)的阻塞處理和線程機(jī)制。二、使用系統(tǒng)的API直接進(jìn)行串口數(shù)據(jù)的讀取,在windows和linux系統(tǒng)中,設(shè)備被虛擬為文件,只需要利用系統(tǒng)提供的API函數(shù)即可進(jìn)行串口數(shù)據(jù)的發(fā)送和讀取。三、使用串口類進(jìn)行串口操作。在此只介紹windows環(huán)境下利用串口類編程的方式。
CSerialPort是比較好用的串口類。它提供如下的串口操作方法:
void WriteToPort(char* string, int len);
    串口初始化成功后,調(diào)用此函數(shù)即可向串口發(fā)送數(shù)據(jù)。為了避免串口緩沖所帶來(lái)的延時(shí),可以開(kāi)啟串口的沖刷機(jī)制。
3. 下位機(jī)中的數(shù)據(jù)接收和協(xié)議解析
    下位機(jī)接收數(shù)據(jù)也有兩種方式,一、等待接收,處理器一直查詢串口狀態(tài),來(lái)判斷是否接收到數(shù)據(jù)。二、中斷接收。兩種方法的優(yōu)缺點(diǎn)在此前的一篇關(guān)于串口通信的文章中詳細(xì)討論過(guò)。得出的結(jié)論是采用中斷接收的方法比較好。
    數(shù)據(jù)包的解析過(guò)程可以設(shè)置到不同的位置。如果協(xié)議比較簡(jiǎn)單,整個(gè)系統(tǒng)只是處理一些簡(jiǎn)單的命令,那么可以直接把數(shù)據(jù)包的解析過(guò)程放入到中斷處理函數(shù)中,當(dāng)收到正確的數(shù)據(jù)包的時(shí)候,置位相應(yīng)的標(biāo)志,在主程序中再對(duì)命令進(jìn)行處理。如果協(xié)議稍微復(fù)雜,比較好的方式是將接收的數(shù)據(jù)存放于緩沖區(qū)中,主程序讀取數(shù)據(jù)后進(jìn)行解析。也有兩種方式交叉使用的,比如一對(duì)多的系統(tǒng)中,首先在接收中斷中解析“連接”命令,連接命令接收到后主程序進(jìn)入設(shè)置狀態(tài),采用查詢的方式來(lái)解析其余的協(xié)議。
    以下給出具體的實(shí)例。在這個(gè)系統(tǒng)中,串口的命令非常簡(jiǎn)單。所有的協(xié)議全部在串口中斷中進(jìn)行。數(shù)據(jù)包的格式如下:
0x55, 0xAA, 0x7E, 0x12, 0xF0, 0x02, 0x23, 0x45, SUM, XOR, 0x0D
    其中0x55, 0xAA, 0x7E為數(shù)據(jù)幀的幀頭,0x0D為幀尾,0x12為設(shè)備的目的地址,0xF0為源地址,0x02為數(shù)據(jù)長(zhǎng)度,后面接著兩個(gè)數(shù)據(jù)0x23, 0x45,從目的地址開(kāi)始結(jié)算累加、異或校驗(yàn)和,到數(shù)據(jù)的最后一位結(jié)束。
    協(xié)議解析的目的,首先判斷數(shù)據(jù)包的完整性,正確性,然后提取數(shù)據(jù)類型,數(shù)據(jù)等數(shù)據(jù),存放起來(lái)用于主程序處理。代碼如下:
if(state_machine == 0)       // 協(xié)議解析狀態(tài)機(jī)
{
      if(rcvdat == 0x55)       // 接收到幀頭第一個(gè)數(shù)據(jù)
          state_machine = 1;
      else
          state_machine = 0;      // 狀態(tài)機(jī)復(fù)位
}
else if(state_machine == 1)
{
      if(rcvdat == 0xAA)       // 接收到幀頭第二個(gè)數(shù)據(jù)
          state_machine = 2;
      else
          state_machine = 0;      // 狀態(tài)機(jī)復(fù)位
}
else if(state_machine == 2)
{
      if(rcvdat == 0x7E)       // 接收到幀頭第三個(gè)數(shù)據(jù)
          state_machine = 3;
     else
          state_machine = 0;      // 狀態(tài)機(jī)復(fù)位
}
else if(state_machine == 3)
{
      sumchkm = rcvdat;       // 開(kāi)始計(jì)算累加、異或校驗(yàn)和
      xorchkm = rcvdat;
      if(rcvdat == m_SrcAdr)      // 判斷目的地址是否正確
          state_machine = 4;
      else
          state_machine = 0;
}
else if(state_machine == 4)
{
      sumchkm += rcvdat;
      xorchkm ^= rcvdat;
      if(rcvdat == m_DstAdr)      // 判斷源地址是否正確
          state_machine = 5;
      else
          state_machine = 0;
   }
else if(state_machine == 5)
{
      lencnt = 0;            // 接收數(shù)據(jù)計(jì)數(shù)器
      rcvcount = rcvdat;        // 接收數(shù)據(jù)長(zhǎng)度
      sumchkm += rcvdat;
      xorchkm ^= rcvdat;
      state_machine = 6;
}
else if(state _machine == 6 || state _machine == 7)
{
      m_ucData[lencnt++] = rcvdat;     // 數(shù)據(jù)保存
      sumchkm += rcvdat;
      xorchkm ^= rcvdat;
      if(lencnt == rcvcount)      // 判斷數(shù)據(jù)是否接收完畢
          state_machine = 8;
      else
          state_machine = 7;
}
else if(state_machine == 8)
{
      if(sumchkm == rcvdat)      // 判斷累加和是否相等
          state_machine = 9;
      else
          state_machine = 0;
}
else if(state_machine == 9)
{
      if(xorchkm == rcvdat)      // 判斷異或校驗(yàn)和是否相等
          state_machine = 10;
      else
          state_machine = 0;
}
else if(state_machine == 10)
{
      if(0x0D == rcvdat)       // 判斷是否接收到幀尾結(jié)束符
      {
          retval = 0xaa;    // 置標(biāo)志,表示一個(gè)數(shù)據(jù)包接收到
      }
      state_machine = 0;     // 復(fù)位狀態(tài)機(jī)
}

    此過(guò)程中,使用了一個(gè)變量state_machine作為協(xié)議狀態(tài)機(jī)的轉(zhuǎn)換狀態(tài),用于確定當(dāng)前字節(jié)處于一幀數(shù)據(jù)中的那個(gè)部位,同時(shí)在接收過(guò)程中自動(dòng)對(duì)接收數(shù)據(jù)進(jìn)行校驗(yàn)和處理,在數(shù)據(jù)包接收完的同時(shí)也進(jìn)行了校驗(yàn)的比較。因此當(dāng)幀尾結(jié)束符接收到的時(shí)候,則表示一幀數(shù)據(jù)已經(jīng)接收完畢,并且通過(guò)了校驗(yàn),關(guān)鍵數(shù)據(jù)也保存到了緩沖去中。主程序即可通過(guò)retval的標(biāo)志位來(lái)進(jìn)行協(xié)議的解析處理。
    接收過(guò)程中,只要哪一步收到的數(shù)據(jù)不是預(yù)期值,則直接將狀態(tài)機(jī)復(fù)位,用于下一幀數(shù)據(jù)的判斷,因此系統(tǒng)出現(xiàn)狀態(tài)死鎖的情況非常少,系統(tǒng)比較穩(wěn)定,如果出現(xiàn)丟失數(shù)據(jù)包的情況也可由上位機(jī)進(jìn)行命令的補(bǔ)發(fā),不過(guò)這種情況筆者還沒(méi)有碰到。
    對(duì)于主程序中進(jìn)行協(xié)議處理的過(guò)程與此類似,主程序循環(huán)中不斷的讀取串口緩沖區(qū)的數(shù)據(jù),此數(shù)據(jù)即參與到主循環(huán)中的協(xié)議處理過(guò)程中,代碼與上面所述完全一樣。


作者: GG276192978    時(shí)間: 2017-10-25 11:07
假如我接收到的數(shù)據(jù)有四組(1,2,3,4)分別對(duì)應(yīng)要發(fā)送的四組數(shù)據(jù)(5,6,7,8),這個(gè)有實(shí)現(xiàn)的算法么
作者: Y!NG    時(shí)間: 2017-11-3 08:27
state_machine 是在什么時(shí)候進(jìn)行改變 的?
作者: dadiao    時(shí)間: 2018-10-2 15:46
好貼
作者: kww6g55t    時(shí)間: 2021-3-29 19:19
領(lǐng)教了,很受啟發(fā)




歡迎光臨 (http://www.zg4o1577.cn/bbs/) Powered by Discuz! X3.1
主站蜘蛛池模板: 国产精品精品久久久 | 亚洲xxxxx | 99精品国产一区二区三区 | 岛国av在线免费观看 | 欧美高清视频 | 亚洲 中文 欧美 日韩 在线观看 | 一区二区三区视频 | 午夜精品久久久久久久星辰影院 | 午夜一区二区三区在线观看 | 国产精品伦一区二区三级视频 | 羞羞视频网站 | 亚洲精品二区 | 黄色在线免费观看视频 | 欧美亚洲第一区 | 欧美区日韩区 | 九九亚洲 | 狠狠干五月天 | 亚洲免费人成在线视频观看 | 性色综合 | 中文字幕日本一区二区 | 欧美一级大片免费观看 | 日韩高清黄色 | 毛片a级| 久久久国产精品视频 | 亚洲一区欧美一区 | 日韩中出| 国产成人精品一区二区三区网站观看 | 国产在线播 | 精品久久久久久久久久 | 福利视频网 | 草久在线视频 | 一级欧美| 成人激情视频免费观看 | 九九精品在线 | www..99re| 男人久久天堂 | 成人免费精品视频 | 一级片网站视频 | 国内精品成人 | 国产精品欧美一区二区 | 国产精品一区在线观看 |