×
单片机 > 单片机程序设计 > 详情

51单片机RS485通讯程序仿真与MODBUS RTU静止时间详解

发布时间:2020-06-15 发布时间:
|

仿真原理图如下

由于MODBUS-RTU 要求每帧信息中的数据间隔时间不得超过1.5字符的静止时间。 

因此: 
(1)、当串口产生接收中断后,立即重装“超时定时器”的初始值。(注:超时定时器的初始值和波特率有关) 

问题如下:  
(a)、由于要求在串口接收中断中重装超时定时器初始值,那么,该超时定时器就只能采用“溢出模式”,而不能采用“CTC模式” 
(b)、超时定时器必须在程序初始化时就一直开启。 
我的理解对吗? 

(2)两帧之间的3.5字符的静止时间该如何实现呢? 
接收到每个字节的时候,初始化定时器就行,最后一个字节后,定时器就溢出了
利用单独的软件定时器,来判断一帧接收报文结束,可以防止若报文接收不完整,该帧通信任务无法结束而影响下一帧的接收。 

由于一帧报文中字节与字节之间的时间间隔和帧与帧之间的时间间隔相比要小得多,因此每当接收一个新字节,就启动软件定时器开始计时,定时器的时间设定为帧与帧的最小时间间隔。波特率不同,该时间间隔也不同。若不到预定的时间内又接收到下一个字节,则说明一帧报文未结束,定时器重新计时;若定时器顺利计数到预定时间,就会触发相应的中断号,在该定时器中断子程序中设定帧结束标志字节,表明一帧报文接收完毕。当主程序内检测到一帧报文接收完毕后,会通过核查从方地址及循环冗余校验字节是否正确来判断该帧的有效性。若确定接收到的是一帧发送给已方的正确报文,则会根据报文内的功能码对该帧命令进行相应的处理,并准备发送帧。

上面就是解决以下两个问题的方法 
(1)、当前帧两个字节之间的1.5字符静止时间 
(2)、两帧之间3.5字符的静止时间 

这里有个SPI串口扩展芯片uCSU122P,内置MODBUS引擎,DIP28,或许对你有用点击此处下载  (原文件名:ucmu2_dat_v122.pdf)

其实这个时间不用这么准确啦,因为是问答式的协议,你可以以某个定时时间查询串口缓冲区字符的长度,如果两次读入的长度一样就认为一帧结束了,这个查询间隔根据波特率微调,就是3.5个字符时间。


7楼的做法不严格,如果第一次定时查询的时候正在收最后一个字节,第二次查显然收完了,第三次查数据不变,那么就导致了7个字符的间隔,如果对方在3.5~7字符之间又来了数据,就麻烦了; 

T1.5和T3.5最严格的方法还是开定时器,但是可以灵活一点;低波特率(<19200)的时候严格定时,和波特率相关;高波特率(>19200)的时候就固定定时(T1.5=750us,T3.5=1750us),这样降低了CPU中断响应的负担。
给你这个程序片段应该可以解决你的问题,我的程序经过严格的测试,高扫描周期、波特率19200下连续运行了一个星期,没出一个错误 


#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<  ModbusFunctionUnion.Data[IsrCount++]=UDR0; 




单片机源程序如下:

/*------------------------------------------------------------------------------

MEASURE.C:  Remote Measurement Recorder using the C51 COMPILER


Copyright 1990-2005 Keil Software, Inc.

------------------------------------------------------------------------------*/

#include "includes.h"


sbit       CS=P1^0;                                  //看门狗端口

sbit       ctrl_485=P1^5;         //RS485控制端口


unsigned char RxBuf[LenRxBuf];           //接收缓冲区

unsigned char *inRxBuf,*outRxBuf; //接收缓冲区指针

/***************判断静止字符**************/

unsigned char  time_3;

unsigned char  time_2;

bit                  rt_rxbuf; 

bit                   rt_ctrl;

/***************接收字符**************/

unsigned char  ComBuf[MaxLenComBuf+1] ;

unsigned char  ch;

unsigned char  k1;

unsigned char  n1;

int          i=-1;

int State=StatInputCom;



/*

*********************************************************************************************************

*                                                                                 串口初始化函数

*********************************************************************************************************

*/

void InitSerial()

{

        TMOD=TMOD&0x0F;

        TMOD=TMOD|0x20;

        TH1=0xF4;//2400 , 11.0592MHz

        TL1=0xF4,

        SCON=0x50;PCON=0x00;

        TR1=1;

}

/*

*********************************************************************************************************

*                                                             缓冲区初始化(只定义接收缓冲区)

*********************************************************************************************************

*/

void InitSerialBuffer(void)                   //串口缓冲区初始化

{

        inRxBuf=RxBuf;outRxBuf=RxBuf;

        ctrl_485=0;                              //接收模式

        ES=1;

        EA=1;

}

/*

*********************************************************************************************************

*                                                                  定时器函数

*********************************************************************************************************

*/

void timefunc(void)

{        unsigned char k;

        unsigned char *tt;


        if(!rt_ctrl)  time_2++;          //判断静止时间,配合下面添加结束符

    time_3++;


        if(time_2>20&&!rt_ctrl)

        {   //ES=0;

                for(k=0;k<4;k++)

                {

                        tt=inRxBuf;tt++;                        //数据暂存        5555

                        if(tt==RxBuf+LenRxBuf) tt=RxBuf;        //                5555

                        if(tt==outRxBuf) {break;}          //如果缓冲期满停止加结束标志  5555

                        else

                        {

                                if(k==0)*inRxBuf=0x4A ;

                                if(k==1)*inRxBuf=0x59 ;

                                if(k==2)*inRxBuf=0x48 ;

                                if(k==3)*inRxBuf=0x59 ;

                                inRxBuf=tt;

}

关键字:51单片机  RS485通讯  MODBUS  RTU  静止时间 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
单片机中高阻态的实质及意义