找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 8091|回復(fù): 7
打印 上一主題 下一主題
收起左側(cè)

我寫的一個(gè)實(shí)現(xiàn)單片機(jī)與PC機(jī)多機(jī)通訊的程序 串口通信

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:102668 發(fā)表于 2016-1-14 00:10 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
   下面是我寫的一個(gè)實(shí)現(xiàn)多個(gè)下位機(jī)(單片機(jī))與一個(gè)上位機(jī)(PC機(jī))的一主多從串口通訊程序,用的STC89C52RC,定時(shí)器2做串口通信波特率發(fā)生器。
     實(shí)現(xiàn)功能是這樣的:
     用調(diào)試助手向單片機(jī)發(fā)送一個(gè)數(shù)據(jù)包。
     通訊協(xié)議是這樣的:
      數(shù)據(jù)包的格式如下所示(共10個(gè)字節(jié)組成):
0x2A,0xEB,0x8D,地址碼,指令碼,數(shù)據(jù)長(zhǎng)度碼,數(shù)據(jù)碼,數(shù)據(jù)碼,校驗(yàn)碼,0xAD  
前面三個(gè)字節(jié)為幀頭,即開始符。
地址碼: 欲傳送的目的地址,即選定哪一個(gè)單片機(jī)。
指令碼:向單片機(jī)發(fā)送的指令
數(shù)據(jù)長(zhǎng)度碼: 用于指示后面有效數(shù)據(jù)的個(gè)數(shù)
數(shù)據(jù)碼:傳送的數(shù)據(jù),配合指令碼的純數(shù)據(jù)。
校驗(yàn)碼: 累加和校驗(yàn),對(duì)地址碼,指令碼,數(shù)據(jù)長(zhǎng)度碼,數(shù)據(jù)碼進(jìn)行累加,用來檢驗(yàn)數(shù)據(jù)的完整性和正確性。
0xAD : 幀尾,即結(jié)束符。

    本程序?qū)崿F(xiàn)功能是這樣的:
    用調(diào)試助手向單片機(jī)發(fā)送一個(gè)數(shù)據(jù)包,單片機(jī)收到后對(duì)數(shù)據(jù)解析,再回傳指定的數(shù)據(jù)。
    例如發(fā)送:2a eb 8d 01 03 01 01 06 ad
指令碼為01,單片機(jī)接收到后解析,回傳0xce 0x7b 0x11 0xed。其中前兩個(gè)字節(jié)為開始符,最后一個(gè)字節(jié)為結(jié)束符。同理,若收到的指令碼為02,回傳0xce 0x7b 0x12 0xed。以此模擬控制單片機(jī)操作。
若接收錯(cuò)誤,即累加校驗(yàn)碼不等于單片機(jī)實(shí)際計(jì)算的累加和,回傳0xce 0x7b 0x02 0xed,提示接收錯(cuò)誤,要求PC重發(fā)數(shù)據(jù)(模擬,需要上位機(jī)軟件配合才行)。
單片機(jī)開機(jī)初始化后即向PC發(fā)送一個(gè)數(shù)據(jù)0xce 0x7b 0x00 0xed,用于指示單片機(jī)與PC通信已連接。

下面是程序:
#define ID 0x01 //單片機(jī)地址
uint8 rec_data;   //串口通信接收數(shù)據(jù)
uint8 state_flag=0;  //通信協(xié)議解析狀態(tài)標(biāo)志,初始化為0
uint8 retval=0;  //通信協(xié)議解析函數(shù)返回值,初始化為0
uint8 cmd;  //指令碼
uint8 Data[2];  //數(shù)據(jù)碼
uint8 data_count;  //數(shù)據(jù)長(zhǎng)度碼

程序大體思想是:
    首先定義了幾個(gè)全局變量,接收到數(shù)據(jù)后,串口中斷子程序中用變量rec_data存儲(chǔ)一個(gè)字節(jié)的數(shù)據(jù),隨后對(duì)數(shù)據(jù)進(jìn)行解析:首先判斷數(shù)據(jù)包的完整性,正確性,然后提取指令碼,數(shù)據(jù)碼等數(shù)據(jù),存放起來用于主程序處理。
    協(xié)議解析過程中,使用一個(gè)變量state_flag的全局變量作為協(xié)議解析狀態(tài)標(biāo)志,用于確定當(dāng)前字節(jié)處于一幀數(shù)據(jù)中的那個(gè)部位,同時(shí)在接收過程中自動(dòng)對(duì)接收數(shù)據(jù)進(jìn)行校驗(yàn)和處理,在數(shù)據(jù)包接收完的同時(shí)也進(jìn)行了校驗(yàn)的比較。因此當(dāng)幀尾結(jié)束符接收到的時(shí)候,則表示一幀數(shù)據(jù)已經(jīng)接收完畢,并且也通過了校驗(yàn),關(guān)鍵數(shù)據(jù)也保存到了緩沖區(qū)(cmd和Data[])中。主程序即可通過查詢r(jià)etval的標(biāo)志位來進(jìn)行協(xié)議的解析處理。如果retval=1;   //錯(cuò)誤標(biāo)志,數(shù)據(jù)包傳送不正確。如果retval=2;   //接收成功標(biāo)志,數(shù)據(jù)包傳送成功。
    接收過程中,只要哪一步收到的數(shù)據(jù)不是預(yù)期值,則直接將狀態(tài)標(biāo)志復(fù)位,用于下一幀數(shù)據(jù)的判斷,避免狀態(tài)自鎖。
    以下是程序:
void PortInit();                //各端口初始化
void TimerInit();        //定時(shí)器初始化
void UsartInit();        //串口初始化
void usart_cmd_scan();        //串口命令掃描
void Data_analysis();   //通信協(xié)議解析函數(shù)
void Send(uint8 sendcmd);  //數(shù)據(jù)發(fā)送函數(shù)


/*--------------------------------        串口中斷服務(wù)子程序 ------------------------------------*/
void ser() interrupt 4
{
   RI=0;
   rec_data=SBUF;   //讀取接收到的數(shù)據(jù)
   Data_analysis();//數(shù)據(jù)解析  
}

/*
* 函數(shù)名:Data_analysis
* 描  述:通信協(xié)議解析函數(shù)
* 輸  入:無
* 輸  出:無
* 備  注:解析串口接收到的數(shù)據(jù)
/*--------------------------------        多機(jī)通信協(xié)議格式 ------------------------------------*/
/*  數(shù)據(jù)包的格式如下所示(共10個(gè)字節(jié)組成): */
/*  0x2A,0xEB,0x8D,地址碼,指令碼,數(shù)據(jù)長(zhǎng)度碼,數(shù)據(jù)碼,數(shù)據(jù)碼,校驗(yàn)碼,0xAD  */
void Data_analysis()
{
   static uchar recdata_sum=0;  //存放累加和
   static uchar lencnt=0;  //數(shù)據(jù)長(zhǎng)度計(jì)數(shù)器
   switch (state_flag)
     {
        case 0:
          {
             if(rec_data == 0x2A)     // 是否幀頭第一個(gè)數(shù)據(jù)
               state_flag = 1;
             else
               state_flag = 0;    // 標(biāo)志復(fù)位
             break;      
          }
        case 1:
          {
             if(rec_data == 0xEB)     // 是否幀頭第二個(gè)數(shù)據(jù)
               state_flag = 2;
             else
               state_flag = 0;    // 標(biāo)志復(fù)位
             break;
          }
        case 2:
          {
             if(rec_data == 0x8D)     // 是否幀頭第三個(gè)數(shù)據(jù)
               state_flag = 3;
             else
               state_flag = 0;    // 標(biāo)志復(fù)位
             break;
          }
        case 3:
          {
             if(rec_data == ID)    // 判斷目的地址是否正確
               {
                  state_flag = 4;
                  recdata_sum=rec_data;   //開始累加
               }   
             else
               state_flag = 0;   // 標(biāo)志復(fù)位
             break;
          }
        case 4:
          {
             state_flag = 5;
             cmd=rec_data;  //指令碼存儲(chǔ)
             recdata_sum+=rec_data;  //累加
             break;
          }        
        case 5:
          {
             lencnt = 0;  //數(shù)據(jù)長(zhǎng)度計(jì)數(shù)器清零
             data_count=rec_data;  //數(shù)據(jù)長(zhǎng)度碼存儲(chǔ)
             recdata_sum+=rec_data;  //累加
             if (data_count!=0)  //后面有數(shù)據(jù)碼
               state_flag=6;
             else
               state_flag=8;
             break;
          }
        case 6:
        case 7:
          {
              Data[lencnt++]=rec_data;  //數(shù)據(jù)碼保存
              recdata_sum+=rec_data;   //累加
              if(lencnt==data_count)
              {
                                          state_flag=8;
                                        lencnt = 0;        
                          }  

              else
                state_flag=7;
              break;
          }
        case 8:
          {
             if(recdata_sum==rec_data)   //數(shù)據(jù)校驗(yàn),判斷累加和是否相等
               state_flag=9;
             else
               {
                  retval=1;   //置錯(cuò)誤標(biāo)志,數(shù)據(jù)包傳送不正確。
                  state_flag=0;   
               }
                         recdata_sum=0;//累加和清零
             break;
          }
        case 9:
          {
             if (rec_data==0xAD)
               {
                                           retval=2;   //置接收成功標(biāo)志,數(shù)據(jù)包傳送成功。
                                        state_flag=0;
                           }
             else
               state_flag=0;
             break;
          }

     }
}

//主程序 , 不斷掃描串口接收到的命令
void main()
{
        PortInit();                //各端口初始化
        TimerInit();        //定時(shí)器初始化
        UsartInit();        //串口初始化                                   
        Send(0xce);
        Send(0x7b);
        Send(0x00);
        Send(0xed);
        while(1)
        {
                usart_cmd_scan();        //串口命令掃描
        }        
}


/*
* 函數(shù)名:usart_cmd_scan
* 描  述:串口命令掃描
* 輸  入:無
* 輸  出:無
* 備  注:掃描PC通過串口發(fā)送的命令
*/
void usart_cmd_scan()
{
        uchar sendcmd;   //下位機(jī)向PC發(fā)送的命令碼
           switch (retval)
    {
        case 1:      //數(shù)據(jù)發(fā)送錯(cuò)誤,請(qǐng)求PC重發(fā)
          {
             sendcmd=2;  //向PC發(fā)送的重發(fā)數(shù)據(jù)命令,PC識(shí)別后向下位機(jī)重發(fā)數(shù)據(jù)包。
             Send(0xce);
                         Send(0x7b);
                         Send(sendcmd);
                         Send(0xed);  //向PC發(fā)送命令

                         retval=0;   //標(biāo)志清零,防止重復(fù)掃描,重復(fù)執(zhí)行。  2013/9/24
                         break;

          }
        case 2:      //數(shù)據(jù)發(fā)送成功,執(zhí)行命令
          {
             switch (cmd)    //命令解碼
                         {
                                 case 0x01:
                                {
                                        Send(0xce);
                                        Send(0x7b);
                                        Send(0x11);
                                        Send(0xed);
                                        cmd=0x00;
                                        break;
                                }
                                case 0x02:
                                {
                                        Send(0xce);
                                        Send(0x7b);
                                        Send(0x12);
                                        Send(0xed);
                                        cmd=0x00;
                                        break;
                                }
                                case 0x03:
                                {
                                        Send(0xce);
                                        Send(0x7b);
                                        Send(0x13);
                                        Send(0xed);
                                        cmd=0x00;
                                        break;
                                }                 
                         }
            }
                  retval=0;   //標(biāo)志清零,防止重復(fù)掃描,重復(fù)執(zhí)行。
     }
}


/*
* 函數(shù)名:Send
* 描  述:串口數(shù)據(jù)發(fā)送函數(shù)
* 輸  入:sendcmd - 待發(fā)送的數(shù)據(jù)
* 輸  出:無
* 備  注:
*/
void Send(uint8 sendcmd)
{
   ES=0;  //關(guān)閉串口
   SBUF=sendcmd;  //發(fā)送數(shù)據(jù),向PC發(fā)送。
   while(!TI);
   TI=0;  //發(fā)送完成,TI清零
   ES=1;  //開串口
}

以上是我寫的這個(gè)程序,希望大家指點(diǎn)一下。
程序運(yùn)行整體可以,但是有個(gè)問題,也希望大神們能幫忙看一下什么問題
每次在單片機(jī)關(guān)機(jī)后,再重新上電后,發(fā)送都沒反應(yīng),只有手動(dòng)按下開發(fā)板的復(fù)位鍵后才能正常通信,當(dāng)再次斷電上電后,又不行了,又得按復(fù)位鍵才正常。按說開發(fā)板上電就復(fù)位了呀,為什么還要手動(dòng)復(fù)位才行?什么的問題?你們?cè)囈幌逻@個(gè)程序有這個(gè)問題嗎?
    還望大神們幫忙指點(diǎn)。

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏5 分享淘帖 頂 踩

相關(guān)帖子

回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:102611 發(fā)表于 2016-1-15 09:21 | 只看該作者
復(fù)位的時(shí)間不同步    時(shí)序問題吧      我剛學(xué)單片機(jī)   這是我自己的理解
回復(fù)

使用道具 舉報(bào)

板凳
ID:19715 發(fā)表于 2016-1-30 11:59 | 只看該作者
每一個(gè)數(shù)據(jù)包在接收的時(shí)候,要加上超時(shí)約定,超過這個(gè)約定時(shí)間就要重新來
回復(fù)

使用道具 舉報(bào)

地板
ID:169871 發(fā)表于 2017-12-13 16:58 | 只看該作者
發(fā)送數(shù)據(jù)后延時(shí)
回復(fù)

使用道具 舉報(bào)

5#
ID:156540 發(fā)表于 2018-12-12 22:20 | 只看該作者
明白 發(fā)表于 2016-1-30 11:59
每一個(gè)數(shù)據(jù)包在接收的時(shí)候,要加上超時(shí)約定,超過這個(gè)約定時(shí)間就要重新來

你好,請(qǐng)問這個(gè)超時(shí)約定應(yīng)該怎么寫呢?要用到定時(shí)器中斷嗎
回復(fù)

使用道具 舉報(bào)

6#
ID:556672 發(fā)表于 2019-6-6 16:56 | 只看該作者
寫得不錯(cuò),樓主有crc16校驗(yàn)的例程么。
回復(fù)

使用道具 舉報(bào)

7#
ID:43342 發(fā)表于 2019-6-7 09:12 | 只看該作者
開機(jī)收不到連接信息,再次發(fā)送連接
回復(fù)

使用道具 舉報(bào)

8#
ID:106272 發(fā)表于 2021-4-1 20:33 | 只看該作者
mark一下,有時(shí)間仔細(xì)學(xué)學(xué)
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

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

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表