Modbus协议问题请教

[复制链接]
 楼主| 冰零分子 发表于 2016-2-29 10:51 | 显示全部楼层 |阅读模式
本帖最后由 冰零分子 于 2016-2-29 12:31 编辑

刚接触Modbus协议,用单片机串口走Modbus时遇到一问题,
主要用到06和03号功能
用Modbus调试精灵测试程序03号读寄存器没有问题,
但是06号写寄存器经多次测试小于0x1F74(十进制8052)的数都正常,
大于0x1F74通讯只能成功写入一次,从第二次以后都会报错

 楼主| 冰零分子 发表于 2016-2-29 10:52 | 显示全部楼层
正常通讯如下:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 冰零分子 发表于 2016-2-29 10:57 | 显示全部楼层
写入大于0x1F74的数据时
第一次正常,以后显示通讯错误
如下:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 冰零分子 发表于 2016-2-29 10:59 | 显示全部楼层
程序部分:
/*******************************************************************************
** 名    称 : ModbusRTUCommunication
** 功    能 : 分析ModbusRTU命令,并返回ModbusRTU数据
** 入口参数 : 无
** 出口参数 : 无
*******************************************************************************/
void ModbusRTUCommunication()
{
unsigned char cy_q,cy_s;
if(cy_receive_buf[0] == 1)  //判断从机地址
{
  start_crc_chec();   //crc校验
  if((crc_chec.bytes.hi==cy_receive_buf[cy_uartnumber-1])&&(crc_chec.bytes.lo==cy_receive_buf[cy_uartnumber-2])) //判断校验码
  {
   VICIntEnable &= ( !(1u << 0x06) );  //关中断
   switch(cy_receive_buf[1])   
            {     
    case 3://读寄存器
       /*地址分析 2/3字节为起始地址 4/5字节为寄存器个数 最多读15个寄存器(15*2*8位) */   
                    if((cy_receive_buf[2]==0)&&(cy_receive_buf[4]==0)&&((cy_receive_buf[3]+cy_receive_buf[5])<=14))  ////地址分析 2/3字节为起始地址 4/5字节为寄存器个数  
                    {      
                        cy_receive_buf[2]=cy_receive_buf[5]*2;  // 响应字节数=寄存器个数*2
                        cy_uartnumber=3;         // 字节起始地址
                        cy_q=cy_receive_buf[3]+cy_receive_buf[5];   //最大字节地址
      /*从机响应*/
                        for(cy_s=cy_receive_buf[3];cy_s<cy_q;cy_s++)   
                        {   
                            cy_receive_buf[cy_uartnumber]=(V[cy_s]&0xFF00)>>8;   
                            cy_uartnumber++;   
                            cy_receive_buf[cy_uartnumber]=V[cy_s]&0x00FF;   
                            cy_uartnumber++;      
                        }   
                        generic_crc(cy_uartnumber);   
                        cy_uartnumber=cy_uartnumber+2;   
                        frame_send(cy_uartnumber);//发送数据   
                    }   
                break;   
                case 6://写单个寄存器   
                    if((cy_receive_buf[2]==0)&&(cy_receive_buf[3]<=14))  //地址分析   
                    {      
                        V[cy_receive_buf[3]]=cy_receive_buf[4]*0x100+cy_receive_buf[5];//更改指定数据   
                        frame_send(cy_uartnumber);//发送数据   
                    }  
                break;
    default:
    break;
   }
   
   VICIntEnable = 1u << 0x06;      //开中断
  }

}
cy_uartnumber = 0;
}
 楼主| 冰零分子 发表于 2016-2-29 11:02 | 显示全部楼层
在中断中调用:
/**********************************************************************************************************
** 函数名称 :IRQ_UART0()
** 函数功能 :串口0接收中断服务程序
** 入口参数 :无
** 出口参数 :无
*********************************************************************************************************
*/
void __irq IRQ_UART0 (void)
{
     uint8 i;
     i = 0;

      if ((U0IIR & 0x0F) == 0x04)   //中断表示寄存器.2==1    接收数据可用中断标识  14个字符触发中断
   {
            for (i=0; i<14; i++)
         {
             cy_receive_buf[i] = U0RBR;  // 读取FIFO的数据,并清除中断
          }
          cy_uartnumber = 13;
    }
   else if((U0IIR & 0x0F) == 0x0C)   //字符超时指示CTI (接收字符未达到触发点)
     {
           while((U0LSR&0x01)==1)    //线状态寄存器.0==1  U0RBR包含有效数字
        {
         cy_receive_buf[i] = U0RBR;  // 读取FIFO的数据,并清除中断
         i++;
         cy_uartnumber++;
        }
    }
     if( (cy_uartnumber > 14)||( cy_uartnumber<5) )  //主要使用功能号3/6
    {
        cy_uartnumber = 0;
     }

    ModbusRTUCommunication();
    VICVectAddr = 0x00;
}
 楼主| 冰零分子 发表于 2016-2-29 11:04 | 显示全部楼层
望前辈们指点,先行谢过
 楼主| 冰零分子 发表于 2016-2-29 12:58 | 显示全部楼层
没有人吗?我还没发现问题所在
undersky 发表于 2016-2-29 13:13 | 显示全部楼层
先确认是哪里的问题。第一、上位机换一个试试(推荐用Modbus Poll),其次检查你下位机06处理的地方,单步调试看下就知道问题所在了
ningling_21 发表于 2016-2-29 14:06 | 显示全部楼层
冰零分子 发表于 2016-2-29 12:58
没有人吗?我还没发现问题所在

换个软件试试,例如MODSCAN32
ocon 发表于 2016-2-29 15:14 | 显示全部楼层
监听一下这个串口实际传输的数据,应该能找到答案,如果问题出在单片机,重点检查一下CRC校验计算,数据帧间的延时等等。
 楼主| 冰零分子 发表于 2016-3-1 09:06 | 显示全部楼层
undersky 发表于 2016-2-29 13:13
先确认是哪里的问题。第一、上位机换一个试试(推荐用Modbus Poll),其次检查你下位机06处理的地方,单步 ...

谢谢,我下个试试:)
 楼主| 冰零分子 发表于 2016-3-1 09:07 | 显示全部楼层
ningling_21 发表于 2016-2-29 14:06
换个软件试试,例如MODSCAN32

谢谢你的建议,我试下
 楼主| 冰零分子 发表于 2016-3-1 09:07 | 显示全部楼层
ocon 发表于 2016-2-29 15:14
监听一下这个串口实际传输的数据,应该能找到答案,如果问题出在单片机,重点检查一下CRC校验计算,数据帧 ...


嗯嗯,谢谢你的建议,我检查下
 楼主| 冰零分子 发表于 2016-3-1 10:10 | 显示全部楼层
ocon 发表于 2016-2-29 15:14
监听一下这个串口实际传输的数据,应该能找到答案,如果问题出在单片机,重点检查一下CRC校验计算,数据帧 ...

前辈,现在情况是这样的:
用串口直接发送0x1234到寄存器1接收没有问题

发送0x1f75以后的数字就会出现第一次接收正常,第二次以后就接收不到数据:

第一次接收正常应该说crc校验没有问题吧,小于0x1f75的数据通讯都正常,时间间隔应该也可以。
那么除了这些可能还有没有其他情况呢?或者是不是我忽略了什么

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
元方义城 发表于 2016-3-1 10:16 | 显示全部楼层
冰零分子 发表于 2016-3-1 10:10
前辈,现在情况是这样的:
用串口直接发送0x1234到寄存器1接收没有问题

可能是程序问题,死在什么地方了。
 楼主| 冰零分子 发表于 2016-3-1 10:25 | 显示全部楼层
本帖最后由 冰零分子 于 2016-3-1 10:29 编辑
ocon 发表于 2016-2-29 15:14
监听一下这个串口实际传输的数据,应该能找到答案,如果问题出在单片机,重点检查一下CRC校验计算,数据帧 ...

还有一个就是,我发现一旦通讯不正常再写0x1f75以下的数据也接收不到应答了。

我通讯函数ModbusRTUCommunication最后一句是把接收字符数cy_uartnumber赋0了,

也就是说,通讯成不成功下次接收数据都应该重新接收,

应该两次的通讯互不影响才对(不知分析的对不对)。

现在通讯失败后会干扰下次通讯,是不是说通讯失败后cy_uartnumber就没有被清零
ningling_21 发表于 2016-3-1 10:40 | 显示全部楼层
冰零分子 发表于 2016-3-1 10:25
还有一个就是,我发现一旦通讯不正常再写0x1f75以下的数据也接收不到应答了。

我通讯函数ModbusRTUCommun ...

如果程序可以调试,进入调试模式很容易就找到问题
 楼主| 冰零分子 发表于 2016-3-1 11:04 | 显示全部楼层
元方义城 发表于 2016-3-1 10:16
可能是程序问题,死在什么地方了。

是的,应该是死在哪里了。

我在中断函数里加了个计数变量如下:

void __irq IRQ_UART0 (void)
{
        static uint8 j=0;
        uint8 i;

        j++;
        UART0_SendByte(j);
        UART0_SendByte('\n');
         
        i = 0;
       
        if ((U0IIR & 0x0F) == 0x04)                 //中断表示寄存器.2==1    接收数据可用中断标识         14个字符触发中断
        {
                for (i=0; i<14; i++)
                {
                        cy_receive_buf = U0RBR;                // 读取FIFO的数据,并清除中断       
                }
                cy_uartnumber = 13;
        }
        else if((U0IIR & 0x0F) == 0x0C)                 //字符超时指示CTI (接收字符未达到触发点)
        {
                while((U0LSR&0x01)==1)                  //线状态寄存器.0==1  U0RBR包含有效数字
                {
                        cy_receive_buf = U0RBR;                // 读取FIFO的数据,并清除中断
                        i++;       
                        cy_uartnumber++;
                }
        }
        if( (cy_uartnumber > 14)||( cy_uartnumber<5) )                //主要使用功能号3/6
        {
                cy_uartnumber = 0;
        }
       
                ModbusRTUCommunication();
       
        VICVectAddr = 0x00;
}


j每进入一次中断加1然后测试,结果如下:

1,正常情况,

可以看到每次应答前都有加1

2,写0x1f75以后的情况:

串口自第二次写入后无j值也无法收到。那就是没有产生中断或串口发送无响应者两种情况了吧?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 冰零分子 发表于 2016-3-1 11:05 | 显示全部楼层
ningling_21 发表于 2016-3-1 10:40
如果程序可以调试,进入调试模式很容易就找到问题

木有JLINK
ocon 发表于 2016-3-1 11:23 | 显示全部楼层
你已经明白解决之道,相信3天之内就可以解决这个小问题:lol
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:每天进步一点点

15

主题

950

帖子

19

粉丝
快速回复 在线客服 返回列表 返回顶部