搜索
bottom↓
回复: 5

MODBUS程序,为什么不能正确读取到当前数据帧的长度?

[复制链接]

出0入0汤圆

发表于 2008-7-18 17:40:19 | 显示全部楼层 |阅读模式
我采用 ATMEAL 128单片机
 (1)、定时中断时间间隔=10MS(CTC模式)
  (2)、串口接收采用中断方式
  (3)、串口发送采用中断方式
 (4)、通讯协议采用MODBUS-RTU,
 测试过程中:
    上位机每隔1秒钟给单片机写51个(int)数据,通过MODBUS预置多个寄存器命令(功能码=16),但是上位机没有收到任何信息。
 现象:
    经跟踪单片机程序,单片机读取的数据帧的长度竟然是随机的,导致CRC校验通不过,因此单片机不给上位机回送应答信号。
    也就是下段代码:
              tempData = (USART1_mscomm_buffer[4]<<8) + USART1_mscomm_buffer[5]; //预置寄存器数量
              tempData = tempData * 2;
              tempData += 9;                    //预置寄存器数量*2+9=上位机数据帧的长度
 奇怪的原因:
    (1).我的定时中断服务程序大约需要0.8ms
        (2).当我将定时中断服务程序中的大部分代码屏蔽后,单片机能够正确收到数据帧的长度。
    (3).不屏蔽定时中断服务程序中的代码,而是每次只写2个(int)数据,单片机能够正确收到数据帧的长度。
void main(void)
{
 unsigned int crcData;
 unsigned int tempData;
   
  //初始化省略      
  while (1)
  {
     USART1_Time_Proc();//超时处理(长时间没收到数据,将接收指针 USART1_receCount=0 )
    if (USART1_receCount > 5)
    {
      switch (USART1_mscomm_buffer[1]) //分析功能码
      {
            case 16: //预置多个寄存器
              tempData = (USART1_mscomm_buffer[4]<<8) + USART1_mscomm_buffer[5]; //预置寄存器数量
              tempData = tempData * 2;
              tempData += 9;                    //预置寄存器数量*2+9=上位机数据帧的长度
             if (USART1_receCount >= tempData)
             {
                   UCSR1B &= ~BIT(7);
                   if (USART1_mscomm_buffer[0] == module_addr))//比较设备地址
                   {
                     crcData = CRC16(USART1_mscomm_buffer,tempData-2); //计算CRC
                       //如果CRC校验通过
                     if (crcData == (USART1_mscomm_buffer[tempData-2]<<8)+ USART1_mscomm_buffer[tempData-1])
                          USART1_PresetMultipleRegisters();//调用预置多个寄存器子程序
                    }
                    USART1_receCount = 0; //接收指针清零
                    USART1_checkoutError = 0;
                    UCSR1B |= BIT(7);
             }
             break;
           }
     }
  }
}


//接收中断
#pragma interrupt_handler USART1_RI_ISR:iv_USART1_RX
void USART1_RI_ISR(void)
{
        unsigned char ch;
        unsigned char status;

        status = UCSR0A;
        ch = UDR1;
        if (USART1_receCount < MSCOMM_BUFFER_LENGTH)
                USART1_mscomm_buffer[USART1_receCount++] = ch;
        USART1_receTimeOut = 10;  //超时计数单元
                                   // 接收到一个字符后,如果100ms内未再次接收到数据,则将 USART1_receCount=0
}

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

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

发表于 2008-7-18 20:32:54 | 显示全部楼层
你可以这样评估:

假如USART按通常标准工作为9600bps,1,8,0,1,那么1秒钟可以传送960个字节,你的上位机一次下发51个字,最少需要106ms(大约1个字节/1ms)。而在这段时间内,每10ms被定时中断打断,而且将近1ms,接收能正常吗?漏掉是当然的了。

出0入0汤圆

 楼主| 发表于 2008-7-24 11:28:01 | 显示全部楼层
谢谢 machao 老师,那么如何解决呢?
  (1)、因为MODBUS_RTU最多可以一次预置120个(INT)寄存器,也就是说,上位机缓冲区长度大约250,这么长的缓冲区一般的商业软件是如何处理的呢?
  (2)、难道非要将定时中断间隔时间设置>接收一帧数据所需要的时间吗?
  (3)、当我将10MS的定时中断服务程序中的代码屏蔽一部分(也就是服务程序执行时间缩短),却又接收正常,这又是为什么?

出0入0汤圆

发表于 2008-7-24 11:59:00 | 显示全部楼层
估计你的程序中没有采用中断嵌套的结构,那么当定时中断先到,USART的中断后到时,后到的中断只能挂起,等待CPU响应。

如果定时中断服务非常快,马上返回了,那么挂起的USART中断还能及时的得到响应,在下一个字节全部收到前,CPU已经把当前的数据取走了。但如果定时中断服务时间长,那么USART后面接受到的数据就把当前数据冲掉了。

解决办法:

1。如果除了USART中断外,系统只有定时中断服务时,该中断服务时间要短,小于一个字节规定的串送时间(以1楼数据,小于1ms)
2。采用中断嵌套结构。在定时中断服务中,首先开放USART中断。

我的教材在介绍中断一章中讲到一个原则:“中断服务程序的执行时间要尽可能的短”。要做到这点,软件设计的整体框架和编程思路非常重要。

还是建议你买本我的编写的教材,仔细学习参考。

出0入0汤圆

 楼主| 发表于 2008-7-24 13:03:00 | 显示全部楼层
谢谢 machao 老师。
  你的AVR指南下册,什么时候出版。

出0入0汤圆

发表于 2008-7-24 13:17:18 | 显示全部楼层
没有这个“下册”了。技术发展很快,原来准备的内容已经有些过时了,有更好的芯片代替。

你的问题还到不了下册的深度,是基本的东西。愿意的话,购买我的教材,把基础的东西真正掌握好。具体书名上面帖子里有。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-6-9 19:45

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

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