找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

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

51單片機(jī)RS485通訊程序仿真與MODBUS RTU靜止時(shí)間詳解

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
單片機(jī)RS485通訊的程序,工作中的成熟應(yīng)用案例。分享給大家。

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



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

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


#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;
}



單片機(jī)源程序如下:
  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í)器函數(shù)
  51. *********************************************************************************************************
  52. */
  53. void timefunc(void)
  54. {        unsigned char k;
  55.         unsigned char *tt;

  56.         if(!rt_ctrl)  time_2++;          //判斷靜止時(shí)間,配合下面添加結(jié)束符
  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;}          //如果緩沖期滿停止加結(jié)束標(biāo)志  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ù)時(shí)間判斷

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

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

  99.                 if(rt_rxbuf)
  100.                 {
  101.                         rt_ctrl=0;
  102.                         time_2=0;//如果有中斷,將時(shí)間清零          5555
  103.                         RI=0;                        //清接收中斷標(biāo)志
  104.                         t=inRxBuf;t++;        //緩沖器中,當(dāng)前尾端指針存入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) //串口中斷服務(wù)子程序
  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. *                                                                    定時(shí)器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;   /*啟動(dòng)定時(shí)器0 */
  138.         ET0 = 1; /*定時(shí)器中斷開*/       
  139. }
  140. /*
  141. *********************************************************************************************************
  142. *                                                                     看門狗復(fù)位
  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';        //設(shè)                               
  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ù)存入最后一個(gè)地址
  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黑下載附件…………
復(fù)制代碼

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



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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

查表是要比計(jì)算快,但表格是不太占地方了
回復(fù)

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

proteus是很棒,就是不閃退就好了
回復(fù)

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

樓主分享感謝
回復(fù)

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

使用道具 舉報(bào)

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

本版積分規(guī)則

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

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

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