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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 17269|回復: 26
打印 上一主題 下一主題
收起左側

51單片機RS485通訊程序仿真與MODBUS RTU靜止時間詳解

  [復制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:283702 發(fā)表于 2018-2-11 17:02 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
單片機RS485通訊的程序,工作中的成熟應用案例。分享給大家。

仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)



由于MODBUS-RTU 要求每幀信息中的數(shù)據(jù)間隔時間不得超過1.5字符的靜止時間。
  因此:
   (1)、當串口產(chǎn)生接收中斷后,立即重裝“超時定時器”的初始值。(注:超時定時器的初始值和波特率有關)
  問題如下: 
   (a)、由于要求在串口接收中斷中重裝超時定時器初始值,那么,該超時定時器就只能采用“溢出模式”,而不能采用“CTC模式”
   (b)、超時定時器必須在程序初始化時就一直開啟。
  我的理解對嗎?
(2)兩幀之間的3.5字符的靜止時間該如何實現(xiàn)呢?
接收到每個字節(jié)的時候,初始化定時器就行,最后一個字節(jié)后,定時器就溢出了
利用單獨的軟件定時器,來判斷一幀接收報文結束,可以防止若報文接收不完整,該幀通信任務無法結束而影響下一幀的接收。
  由于一幀報文中字節(jié)與字節(jié)之間的時間間隔和幀與幀之間的時間間隔相比要小得多,因此每當接收一個新字節(jié),就啟動軟件定時器開始計時,定時器的時間設定為幀與幀的最小時間間隔。波特率不同,該時間間隔也不同。若不到預定的時間內(nèi)又接收到下一個字節(jié),則說明一幀報文未結束,定時器重新計時;若定時器順利計數(shù)到預定時間,就會觸發(fā)相應的中斷號,在該定時器中斷子程序中設定幀結束標志字節(jié),表明一幀報文接收完畢。當主程序內(nèi)檢測到一幀報文接收完畢后,會通過核查從方地址及循環(huán)冗余校驗字節(jié)是否正確來判斷該幀的有效性。若確定接收到的是一幀發(fā)送給已方的正確報文,則會根據(jù)報文內(nèi)的功能碼對該幀命令進行相應的處理,并準備發(fā)送幀。
上面就是解決以下兩個問題的方法
  (1)、當前幀兩個字節(jié)之間的1.5字符靜止時間
  (2)、兩幀之間3.5字符的靜止時間
這里有個SPI串口擴展芯片uCSU122P,內(nèi)置MODBUS引擎,DIP28,或許對你有用點擊此處下載  (原文件名:ucmu2_dat_v122.pdf)
其實這個時間不用這么準確啦,因為是問答式的協(xié)議,你可以以某個定時時間查詢串口緩沖區(qū)字符的長度,如果兩次讀入的長度一樣就認為一幀結束了,這個查詢間隔根據(jù)波特率微調(diào),就是3.5個字符時間。
7樓的做法不嚴格,如果第一次定時查詢的時候正在收最后一個字節(jié),第二次查顯然收完了,第三次查數(shù)據(jù)不變,那么就導致了7個字符的間隔,如果對方在3.5~7字符之間又來了數(shù)據(jù),就麻煩了;

T1.5和T3.5最嚴格的方法還是開定時器,但是可以靈活一點;低波特率(<19200)的時候嚴格定時,和波特率相關;高波特率(>19200)的時候就固定定時(T1.5=750us,T3.5=1750us),這樣降低了CPU中斷響應的負擔。
給你這個程序片段應該可以解決你的問題,我的程序經(jīng)過嚴格的測試,高掃描周期、波特率19200下連續(xù)運行了一個星期,沒出一個錯誤


#pragma interrupt_handler Timer1:iv_TIMER1_OVF  
void Timer1(void)
{
  unsigned short CRC;
  TCNT1=65525-51*11;//65535-(11*(ubbr+1)) 波特率9600   
  if(CNT<8)
  {
     CNT++;        
     if(CNT==4)
     {
    ModBusQueryDataLong=IsrCount;
    IsrCount=0;
     }
     else if(CNT==8)
     {   
    if(ModBusQueryDataLong>2)
{  
   
       CRC=CRC16((unsigned char *)&ModbusFunctionUnion,ModBusQueryDataLong-2);
   if((ModbusFunctionUnion.Data[ModBusQueryDataLong-2]==MSB(CRC))&&
          (ModbusFunctionUnion.Data[ModBusQueryDataLong-1]==LSB(CRC)))
{   
              FrameStatu=1;
    }   
    }   
     }
  }   
}
#pragma interrupt_handler UART_isr:iv_USART0_RX
void UART_isr(void)
{  
  CNT=0;  
  while(!(UCSR0A&(1<<RXC0)));
  ModbusFunctionUnion.Data[IsrCount++]=UDR0;
}



單片機源程序如下:
  1. /*------------------------------------------------------------------------------
  2. MEASURE.C:  Remote Measurement Recorder using the C51 COMPILER

  3. Copyright 1990-2005 Keil Software, Inc.
  4. ------------------------------------------------------------------------------*/
  5. #include "includes.h"

  6. sbit       CS=P1^0;                                  //看門狗端口
  7. sbit       ctrl_485=P1^5;         //RS485控制端口

  8. unsigned char RxBuf[LenRxBuf];           //接收緩沖區(qū)
  9. unsigned char *inRxBuf,*outRxBuf; //接收緩沖區(qū)指針
  10. /***************判斷靜止字符**************/
  11. unsigned char  time_3;
  12. unsigned char  time_2;
  13. bit                  rt_rxbuf;
  14. bit                   rt_ctrl;
  15. /***************接收字符**************/
  16. unsigned char  ComBuf[MaxLenComBuf+1] ;
  17. unsigned char  ch;
  18. unsigned char  k1;
  19. unsigned char  n1;
  20. int          i=-1;
  21. int State=StatInputCom;


  22. /*
  23. *********************************************************************************************************
  24. *                                                                                 串口初始化函數(shù)
  25. *********************************************************************************************************
  26. */
  27. void InitSerial()
  28. {
  29.         TMOD=TMOD&0x0F;
  30.         TMOD=TMOD|0x20;
  31.         TH1=0xF4;//2400 , 11.0592MHz
  32.         TL1=0xF4,
  33.         SCON=0x50;PCON=0x00;
  34.         TR1=1;
  35. }
  36. /*
  37. *********************************************************************************************************
  38. *                                                             緩沖區(qū)初始化(只定義接收緩沖區(qū))
  39. *********************************************************************************************************
  40. */
  41. void InitSerialBuffer(void)                   //串口緩沖區(qū)初始化
  42. {
  43.         inRxBuf=RxBuf;outRxBuf=RxBuf;
  44.         ctrl_485=0;                              //接收模式
  45.         ES=1;
  46.         EA=1;
  47. }
  48. /*
  49. *********************************************************************************************************
  50. *                                                                  定時器函數(shù)
  51. *********************************************************************************************************
  52. */
  53. void timefunc(void)
  54. {        unsigned char k;
  55.         unsigned char *tt;

  56.         if(!rt_ctrl)  time_2++;          //判斷靜止時間,配合下面添加結束符
  57.     time_3++;

  58.         if(time_2>20&&!rt_ctrl)
  59.         {   //ES=0;
  60.                 for(k=0;k<4;k++)
  61.                 {
  62.                         tt=inRxBuf;tt++;                        //數(shù)據(jù)暫存        5555
  63.                         if(tt==RxBuf+LenRxBuf) tt=RxBuf;        //                5555
  64.                         if(tt==outRxBuf) {break;}          //如果緩沖期滿停止加結束標志  5555
  65.                         else
  66.                         {
  67.                                 if(k==0)*inRxBuf=0x4A ;
  68.                                 if(k==1)*inRxBuf=0x59 ;
  69.                                 if(k==2)*inRxBuf=0x48 ;
  70.                                 if(k==3)*inRxBuf=0x59 ;
  71.                                 inRxBuf=tt;
  72.                         }        //          5555
  73.                 }       
  74.                          rt_ctrl=1;
  75.                                                          //  ES=1;                                                          
  76.         }

  77. //        time_1++;
  78. //        time_4++;//接收數(shù)據(jù)時間判斷

  79. }
  80. /*
  81. *********************************************************************************************************
  82. *                                                                串口接收
  83. *********************************************************************************************************
  84. */
  85. void serial(void) //串口中斷服務子程序
  86. {  
  87.         unsigned char *t;

  88.         if(RI)
  89.         {
  90.                 RI=0;
  91.                 if(time_3>4)   //判斷靜止時間
  92.                 { time_3=0;
  93.                         //         rt_rxbuf=0;
  94.                         rt_rxbuf=1;        // if(SBUF==0x01)rt_rxbuf=1;
  95.                                         // time_3的作用是為判斷靜止字符之后必須為i本機地址,否則不接受數(shù)據(jù),判斷下一個靜止字符
  96.                 }
  97. //                 rt1_ctrl=0;
  98.                  time_3=0;

  99.                 if(rt_rxbuf)
  100.                 {
  101.                         rt_ctrl=0;
  102.                         time_2=0;//如果有中斷,將時間清零          5555
  103.                         RI=0;                        //清接收中斷標志
  104.                         t=inRxBuf;t++;        //緩沖器中,當前尾端指針存入t
  105.                         if(t==RxBuf+LenRxBuf) t=RxBuf;         //判斷緩沖器是否指向最后,如果T為緩沖器最后,重新指向開始
  106.                         if(t==outRxBuf) return;         //RxBuf Full。緩沖器滿,(緩沖器命令并未執(zhí)行,則為滿),停止接受
  107.                         *inRxBuf=SBUF;                                //如果緩沖器未滿,將接受到的數(shù)據(jù)存入緩沖器中,
  108.                         inRxBuf=t;                                        //緩沖器尾端指針移向下一位
  109.                 }
  110.         }
  111. }

  112. //void serial(void) //串口中斷服務子程序
  113. //{
  114. //    unsigned char *t;
  115. //    if(RI)
  116. //    {       
  117. //        RI=0;
  118. //        t=inRxBuf;
  119. //                t++;
  120. //                if(t==RxBuf+LenRxBuf) t=RxBuf;
  121. //                if(t==outRxBuf) return;
  122. //                *inRxBuf=SBUF;
  123. //                inRxBuf=t;
  124. //    }
  125. //}

  126. /*
  127. *********************************************************************************************************
  128. *                                                                    定時器0初始化
  129. *********************************************************************************************************
  130. */void InterruptInitial()
  131. {
  132.         /* set timer. 50ms, THTL = 3caf when cpu at 12MHz */
  133.         TMOD |= 0x01;
  134.         TH0 = 0xEE;
  135.         TL0 = 0x00;
  136.         /* TR0 is set in OSStart() */
  137.         TR0=1;   /*啟動定時器0 */
  138.         ET0 = 1; /*定時器中斷開*/       
  139. }
  140. /*
  141. *********************************************************************************************************
  142. *                                                                     看門狗復位
  143. *********************************************************************************************************
  144. */
  145. void rst_wdog()
  146. {
  147.         CS = 0;
  148.     _nop_();
  149.         _nop_();
  150.     CS = 1;
  151. }
  152. /*
  153. *********************************************************************************************************
  154. *                                                                  從串口緩沖區(qū)讀數(shù)據(jù)
  155. *********************************************************************************************************
  156. */
  157. bit yygetch(unsigned char *ch)                  //從串口緩沖區(qū)讀1字節(jié)數(shù)據(jù)
  158. {
  159.         //ES=0;        
  160.         if(inRxBuf==outRxBuf) {ES=1;return 0;}  //RxBuf Empty
  161.         *ch=*outRxBuf;  outRxBuf++;
  162.         if(outRxBuf==RxBuf+LenRxBuf) outRxBuf=RxBuf;
  163.         //ES=1;        
  164.         return 1;
  165. }
  166. /*
  167. *********************************************************************************************************
  168. *                                                                  從串口緩沖區(qū)讀數(shù)據(jù)
  169. *********************************************************************************************************
  170. */
  171. void getstr(void)
  172. {
  173.         switch(State)
  174.         {
  175.                 case StatInputCom:         //接收數(shù)據(jù)
  176.                 {
  177.                         if(yygetch(&ch))
  178.                         {
  179.                                 if(k1==3){if(ch==0x59)k1=4;else k1=0;}
  180.                                 if(k1==2){if(ch==0x48)k1=3;else k1=0;}
  181.                                 if(k1==1){if(ch==0x59)k1=2;else k1=0;}
  182.                                 if(ch==0x4A)k1=1;

  183.                                 if(k1>=4)        /*Enter return key*/
  184.                                 {
  185.                                         k1=0;
  186.                                         ComBuf[i+1]='Y';        //設                               
  187.                                         if(i+1==0) {;}                 //如果無接收到字符,不處理
  188.                                         else {State=StatExeCom;}
  189.                                 }
  190.                                 else
  191.                                 {
  192.                                         i=i+1;         //保存字符到緩沖器
  193.                                         if(i>=MaxLenComBuf){i=MaxLenComBuf-1;} //如果緩沖器滿了,將數(shù)據(jù)存入最后一個地址
  194.                                         ComBuf[i]=ch;                       
  195.                                 }
  196.                                         break;
  197.                         }
  198.                         else
  199.                         {
  200.                                 break;
  201.                         }
  202.                 }        //case StatInputCom
  203.                 case StatExeCom:         //接收完畢后,解析數(shù)據(jù)
  204.                 {         n1=ComBuf[1];
  205.                  PrintStr1(ComBuf,10);
  206.                          n1=0;
  207.        
  208.                         State=StatInputCom;
  209.                         i=-1;
  210.                         break;                               
  211.    
  212.             }        //case StatExeCom       
  213.         }        //switch
  214. }
  215. /*
  216. *********************************************************************************************************
  217. *                                                                  發(fā)送字符串
  218. *********************************************************************************************************
  219. */
  220. void PrintStr1(unsigned char *str,unsigned char n)//顯示字符串
  221. {
  222.         int i;
  223. ……………………

  224. …………限于本文篇幅 余下代碼請從51黑下載附件…………
復制代碼

所有資料51hei提供下載:
01.02.26通訊.rar (77.34 KB, 下載次數(shù): 479)



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

使用道具 舉報

沙發(fā)
ID:147459 發(fā)表于 2018-4-13 13:09 | 只看該作者
謝謝樓主的帖子,我會仔細研究的,努力學習中。
回復

使用道具 舉報

板凳
ID:285352 發(fā)表于 2018-5-12 20:58 | 只看該作者
剛好正在學習這個,真的很感謝
回復

使用道具 舉報

地板
ID:228452 發(fā)表于 2018-5-16 05:13 | 只看該作者
Just wonder
Is it faster to use lookup table instead of calculating CRC
Thanks
回復

使用道具 舉報

5#
ID:262858 發(fā)表于 2018-12-8 10:00 | 只看該作者
學習學習先,感謝樓主分享
回復

使用道具 舉報

6#
ID:262858 發(fā)表于 2018-12-8 10:01 | 只看該作者
只言片語,不能窺其全貌,遺憾
回復

使用道具 舉報

7#
ID:544650 發(fā)表于 2019-5-22 13:53 | 只看該作者
謝謝樓主
回復

使用道具 舉報

8#
ID:92827 發(fā)表于 2019-6-1 08:26 | 只看該作者
正在學習modbus,感謝樓主無私奉獻!
回復

使用道具 舉報

9#
ID:79544 發(fā)表于 2019-6-3 16:28 | 只看該作者
感謝樓主分享,學習啦。謝謝!
回復

使用道具 舉報

10#
ID:92827 發(fā)表于 2019-6-15 06:22 | 只看該作者
proteus仿真的,好,方便測試
回復

使用道具 舉報

11#
ID:92827 發(fā)表于 2019-6-15 06:23 | 只看該作者
mick32 發(fā)表于 2018-5-16 05:13
Just wonder
Is it faster to use lookup table instead of calculating CRC
Thanks

查表是要比計算快,但表格是不太占地方了
回復

使用道具 舉報

12#
ID:92827 發(fā)表于 2019-6-15 06:55 | 只看該作者
是要同過串口連接modbus主機軟件來仿真么?下載后發(fā)現(xiàn)電路好多標號都是獨立的,根本就沒有連接的,LED0--LED7等,感覺是幾個電路拼接到一起的,運行后無法測試
回復

使用道具 舉報

13#
ID:90212 發(fā)表于 2019-7-11 10:54 | 只看該作者
以前沒在意modbus幀間隔時間,現(xiàn)在正為它頭疼
回復

使用道具 舉報

14#
ID:49198 發(fā)表于 2019-7-17 17:13 | 只看該作者
這個應該是最全的485通訊了,正好學習下,感謝樓主分享
回復

使用道具 舉報

15#
ID:649789 發(fā)表于 2019-12-16 08:35 來自手機 | 只看該作者
littlejia 發(fā)表于 2019-6-15 06:22
proteus仿真的,好,方便測試

proteus是很棒,就是不閃退就好了
回復

使用道具 舉報

16#
ID:477505 發(fā)表于 2020-1-3 20:28 | 只看該作者
剛好正在學習這個
回復

使用道具 舉報

17#
ID:387558 發(fā)表于 2020-1-12 10:58 來自手機 | 只看該作者
不安全 發(fā)表于 2019-12-16 08:35
proteus是很棒,就是不閃退就好了

樓主分享感謝
回復

使用道具 舉報

18#
ID:602985 發(fā)表于 2020-8-25 16:02 | 只看該作者
我會仔細研究的,努力學習中。
回復

使用道具 舉報

19#
ID:989951 發(fā)表于 2022-3-22 08:04 | 只看該作者
謝謝分享,正在學習單片機通訊設計
回復

使用道具 舉報

20#
ID:915491 發(fā)表于 2022-4-1 17:10 | 只看該作者
謝謝樓主的帖子,我會仔細研究的,努力學習中。
回復

使用道具 舉報

21#
ID:915491 發(fā)表于 2022-4-2 11:26 | 只看該作者
modbus幀間隔時間,是字符靜止時間
回復

使用道具 舉報

22#
ID:1023327 發(fā)表于 2022-5-2 16:15 | 只看該作者
感謝,整好需要,研究一下
回復

使用道具 舉報

23#
ID:18797 發(fā)表于 2022-6-9 23:45 | 只看該作者
研究得夠深入的了
回復

使用道具 舉報

24#
ID:440585 發(fā)表于 2022-12-17 10:33 | 只看該作者
仿真圖有問題  發(fā)光管反了   
回復

使用道具 舉報

25#
ID:1058255 發(fā)表于 2022-12-19 13:48 | 只看該作者
學習下485
回復

使用道具 舉報

26#
ID:433219 發(fā)表于 2022-12-20 08:11 | 只看該作者
留點余量吧。。我一般是給足,總線2ms靜止時間,做間隔
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

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

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

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产在线一区二区三区 | 日韩在线观看网站 | 毛片a级 | 国产在线拍偷自揄拍视频 | 国产精品久久二区 | 久久成人精品视频 | 国产精品www| 亚洲丝袜天堂 | 国产亚洲精品美女久久久久久久久久 | 欧美影院 | 一区二区三区国产 | 日韩在线综合 | 2020亚洲天堂 | 国产91网站在线观看 | 亚洲免费网 | 97超级碰碰| 成人精品一区二区 | 精品成人av | 亚洲精品国产电影 | 国产日韩av一区二区 | 欧美日韩在线一区二区 | 一区二区亚洲 | 国产日韩精品久久 | 亚洲日本一区二区三区四区 | 国产精品视频免费看 | 曰批视频在线观看 | 亚洲国产精选 | 一区二区三区欧美在线观看 | 欧美在线不卡 | 亚州精品天堂中文字幕 | 久久成人一区二区三区 | 日韩欧美亚洲一区 | 午夜网址 | 国产在线视频在线观看 | 99在线观看视频 | www.99热| 91免费在线 | 丁香六月激情 | 欧美亚洲国产精品 | 成人国产精品视频 | 日韩国产三区 |