搜索
bottom↓
回复: 15

MODBUS通讯之数据接收 体现那个3.5倍的字符传输时间

[复制链接]

出0入0汤圆

发表于 2007-9-26 11:23:39 | 显示全部楼层 |阅读模式
1、        通信接口
1.1 标    准:RS485
1.2 工作方式:串行,异步,半双工
1.3 数据格式:起始位1位,数据位8位,停止位1位,无奇偶校验
1.4 通信速率:4800 bit/s
2、        通信方式
主站周期性查询从站,从站按相应报文格式回答。主站可单独和从从站通信,也能以广播方式和所有从站通信。如果单独通信,从站返回消息作为回应,如果是以广播方式查询的,则不作任何回应。
3、        传输规定
点对点通信。采用MODBUS-RTU通信协议。每两个字符之间发送或者接收的时间间隔不能超过1.5倍字符传输时间。如果两个字符时间间隔超过了3.5倍的字符传输时间,规约就认为一帧数据已经接收,新的一帧数据传输开始。

-------------------------------------------------
我的思路是:
1.当一帧数据传输时,mcu每接收到一个字节的时候,初始化定时器,定时器的时间大于3.5倍的字符传输时间
2.一旦此帧数据发送结束,时间超过3.5倍的字符传输时间,定时器就发生中断,处理刚接收到的数据
3.这样可以接收不定长度的数据帧.程序现在还很简单,没有过长帧处理

程序如下
void uart1_init(void)
{
uchar i;
UCSR1B = 0x00; //disable while setting baud rate
UCSR1A = 0x00;
UCSR1C = 0x06; //异步模式,
UBRR1L = 0x8f /*INVALID SETTING*/; //4800
UBRR1H = 0x00 /*INVALID SETTING*/; //set baud rate hi
UCSR1B = 0xd8;//bit7接收结束中断使能,bit6发送结束中断使能,bit4接收使能,bit3发送使能


}
void timer0_init(void) //定时器0初始化
{
TCCR0 = 0x00; //stop
ASSR  = 0x00; //set async mode
TCNT0 = 0x95; //set count 0x29:50hz  0x95:100hz   11.0592mhz的晶振
OCR0  = 0xD7;
TCCR0 = 0x07; //start timer
  TIMSK = 0x01; //定时器/ 计数器中断屏蔽寄存器 timer interrupt sources

}

#pragma interrupt_handler timer0_ovf_isr:17  //定时器/ 计数器0 溢出 中断
void timer0_ovf_isr(void)
{
uchar i;

asm("nop");
if(Recv_buf[1]==0x10)
{
   rece1Disable;//使能发送
   asm("nop");
  while(!(UCSR1A&(1<<UDRE1)));  //串口1可以发送判断  这个地方就可以放置数据处理的函数了
  UDR1=0x99;

}
  //关定时器0中断,数据接收完毕,目前没有数据过长处理,稍后放置检验程序
  TCCR0 = 0x00; //stop
  TCNT0 = 0x00;
  TIMSK = 0x00; //关定时器0中断,而后在接收中断中打开
   Recv_Num=0; //接收计数器清零
  for(i=0;i<15;i++)
  {
   Recv_buf=0;
  }

  asm("nop");
}
//串口1接收中断
#pragma interrupt_handler uart1_rx_isr:31
void uart1_rx_isr(void)
{
uchar i ;
   asm("nop");
if(Recv_en) //允许接收数据
  {
   Recv_buf[Recv_Num]=UDR1;
   Recv_Num=Recv_Num+1;
  //
  }
  timer0_init();  //开定时器
  asm("nop");

}
//串口1发送中断
#pragma interrupt_handler uart1_tx_isr:33
void uart1_tx_isr(void)
{
  asm("nop");
  rece1Enable;  //发送结束 中断 使能接收
  asm("nop");

}

------------------------------------------------------------
请大家批评指正

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2007-9-26 13:28:43 | 显示全部楼层
这个3.5字符时间太害人了,误导人。
注意一下协议里面的那个,同一数据贞里面的两个字符间隔时间不要超过1.5倍的字符时间就OK了。
根据不同的波特率计算出单个字符的时间。在接收中断里面放一个标志,然后根据单个字符时间给这个变量赋值。
然后根据单个字符时间定义一个定时器中断,在定时中断里面改变那个标志,超过时间没有跟新接收中断里面的那个变量,数据贞就接受失败,从新来过。。。。。。

出0入0汤圆

发表于 2007-9-26 19:28:05 | 显示全部楼层
今天调了大半天,搞定了EVIEW文本屏MD204L和M64通MODBUS,因为用的功能不多,就读位,写位,读字,写字,
msec(uchar a)
{
uchar b;
for(;a>0;a--)
   for(b=0;b<100;b++);
}
/*=======================校验码生成========================================*/   //这个是从OURAVR下的呵呵
uint crc_make(uchar *ptr, uint len)  
{   
uint crc=0xffff;   
uchar i;   
while(len!=0)
     {   
      crc^=*ptr;
      for(i=0;i<8;i++)
         {   
            if((crc&0x0001)==0)
                                        crc=crc>>1;
            else  
                    {
                     crc=crc>>1;
                     crc^=0xa001;  
                    }                     
         }  
                         len-=1;
                         ptr++;  
      }   
return crc;
}   
/*======================校验码比较======================= */         
uchar crc_check(uchar a)     //a为接收到的字符个数
       {
        uint crc0,crc1;
        crc0=crc_make(rxd_data,(a-2));
        crc1=(rxd_data[a-1]<<8)+rxd_data[a-2];
        if(crc0==crc1)  return 1;
        else            return 0;
        }
/*====================开始发送================================*/
send_start(uchar a)
{
uchar b;
PORTE|=4;
for(b=0;b<a;b++)
   {
    UDR0=send_data;
    while(!(UCSR0A&0x20));
        msec(1);
   }
PORTE&=~4;
}
            //转发送


/*======================读位======================= */   
read_bit()
{
uchar b,c;
uint a;
if(crc_check(8)==0)     return;               //CRC检查
a=(rxd_data[2]<<8)+rxd_data[3];               //合并地址
b=a/16;                                       //取位所在的节地址
c=a%16;                                       //取位在字地址的位地址      
send_data[0]=1;                               //发送本地地址
send_data[1]=1;                               //发送功能码
send_data[2]=2;                               //发送字节数
if((m_sign&(1<<c))!=0)   send_data[3]=1;   //读该地址的位状态并发送
else                        send_data[3]=0;
send_data[4]=0;                               //位状态的低字节
a=crc_make(send_data,5);                      //生成CRC
send_data[6]=a>>8;                            //发送高字节
send_data[5]=a&255;                           //发送低字节
send_start(8);                                //开始发送
}
/*======================读字======================= */   
read_word()
{
uint a;
if(crc_check(8)==0)     return;               //CRC检查
a=(rxd_data[2]<<8)+rxd_data[3];               //合并地址
send_data[0]=1;                               //发送本地地址
send_data[1]=3;                               //发送功能码
send_data[2]=2;                               //发送字节数
send_data[3]=(modbus_word[a]>>8);             //读该地址高字节并发送
send_data[4]=(modbus_word[a]&255);            //读该地址低字节并发送
a=crc_make(send_data,5);                      //生成CRC
send_data[6]=a>>8;                            //发送高字节
send_data[5]=a&255;                           //发送低字节
send_start(8);                                //开始发送
}
/*======================写位======================= */   
write_bit()
{
uchar b,c;
uint a;
if(crc_check(8)==0)     return;    //CRC检查
a=(rxd_data[2]<<8)+rxd_data[3];    //合并地址
b=a/16;                            //取位所在的节地址
c=a%16;                            //取位在字地址的位地址
if(rxd_data[4]==0xff)     m_sign|=(1<<c);    //写1
else                      m_sign&=~(1<<c);   //写0
send_data[0]=1;             //发送本地地址                    
send_data[1]=5;             //发送功能码
send_data[2]=rxd_data[2];   //发送高8位地址
send_data[3]=rxd_data[3];   //发送低8位地址
send_data[4]=rxd_data[4];    //读该位状态高字节
send_data[5]=00;            //发送位状态的低字节
a=crc_make(send_data,6);    //生成CRC
send_data[7]=a>>8;          //发送高字节
send_data[6]=a&255;        //发送低字节
send_start(9);             //开始发送
}
/*======================读字======================= */   
write_word()
{
uchar b,c;
uint a;
if(crc_check(11)==0)     return;               //CRC检查
a=(rxd_data[2]<<8)+rxd_data[3];               //合并地址
modbus_word[a]=(rxd_data[7]<<8)+rxd_data[8];
send_data[0]=1;                               //发送本地地址
send_data[1]=16;                               //发送功能码
send_data[2]=rxd_data[2];                               //发送字节数
send_data[3]=rxd_data[3];             //读该地址高字节并发送
send_data[4]=0;
send_data[5]=1;                     //读该地址低字节并发送
a=crc_make(send_data,5);                      //生成CRC
send_data[7]=a>>8;                            //发送高字节
send_data[6]=a&255;                           //发送低字节
send_start(9);                                //开始发送
}
/*====================485服务函数1MS调用一次===========================*/
rs485_check()
{
if(send_overtime<4)  {send_overtime++;}  //如果接收超时标志小于5,加1
if((send_overtime==4)&&(rxd_end))        //如果超时且接收结束,就处理数据
      {
           rxd_pointer=0;                     //接收指针清0
           rxd_end=0;                         //清零接收标志
           switch(rxd_data[1])                //判断命令
               {
                    case 1:read_bit();break;            //读位
                        case 3:read_word();break;           //读字
                        case 5:write_bit();break;           //写位
                        case 16:write_word();break;          //写字
                        default:          break;            
                        }
        }
}
/*==============================================================*/
main()
{
DDRE=0XE;
DDRB=0X60;
TCCR0=0X0C;
TIMSK|=2;
OCR0=230;
UCSR0B=0X98;
UCSR0C=0X6;
UBRR0L=95;
UBRR0H=0;
SREG|=0X80;
  while(1)
  {
   if(m_sign[5]&(1<<8))   PORTB&=~(1<<6);
   else                   PORTB|=(1<<6);
   if(m_sign[0]&(1<<9))   PORTB&=~(1<<5);
   else                   PORTB|=(1<<5);
  }
  
}
#pragma interrupt_handler rs458rx:19
rs458rx()
{
rxd_data[rxd_pointer]=UDR0;    //数据进缓存
rxd_pointer++;                 //接收指针加1
rxd_end=1;                     //接收标志置1
send_overtime=0;               //接收超时标志清0
}
#pragma interrupt_handler t0_ctc:16
t0_ctc()
{

rs485_check();             //485工作程序,保证1MS调用一次,
}

出0入0汤圆

发表于 2011-6-24 23:13:35 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-6-28 14:00:51 | 显示全部楼层
mark一下!

出0入0汤圆

发表于 2012-6-28 14:11:40 | 显示全部楼层
不 马克 

改 收藏

出0入0汤圆

发表于 2012-7-1 21:12:16 | 显示全部楼层
正在研究中

出0入0汤圆

发表于 2013-1-3 17:22:34 | 显示全部楼层
mark modbus

出0入0汤圆

发表于 2013-1-4 08:08:36 | 显示全部楼层
不錯的資料.謝謝.

出0入0汤圆

发表于 2013-1-4 08:45:28 | 显示全部楼层
先保存,要用的时候再看。

出0入0汤圆

发表于 2013-1-24 08:53:51 | 显示全部楼层
貌似很给力啊!仔细研究下

出0入0汤圆

发表于 2013-1-24 15:27:10 | 显示全部楼层
我用Labview搞的

出0入0汤圆

发表于 2013-5-28 16:39:46 | 显示全部楼层
收藏了,谢谢楼主。。。。。。。。。。

出0入0汤圆

发表于 2013-7-8 14:00:00 | 显示全部楼层
这个需要好好看看。

出0入0汤圆

发表于 2013-7-23 11:23:08 | 显示全部楼层
需要看看

出0入4汤圆

发表于 2013-12-27 21:20:28 | 显示全部楼层
好好的研究啊!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-23 22:15

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表