打印

Modbus协议问题请教

[复制链接]
1813|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 冰零分子 于 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;
}

使用特权

评论回复
5
冰零分子|  楼主 | 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;
}

使用特权

评论回复
6
冰零分子|  楼主 | 2016-2-29 11:04 | 只看该作者
望前辈们指点,先行谢过

使用特权

评论回复
7
冰零分子|  楼主 | 2016-2-29 12:58 | 只看该作者
没有人吗?我还没发现问题所在

使用特权

评论回复
8
undersky| | 2016-2-29 13:13 | 只看该作者
先确认是哪里的问题。第一、上位机换一个试试(推荐用Modbus Poll),其次检查你下位机06处理的地方,单步调试看下就知道问题所在了

使用特权

评论回复
9
ningling_21| | 2016-2-29 14:06 | 只看该作者
冰零分子 发表于 2016-2-29 12:58
没有人吗?我还没发现问题所在

换个软件试试,例如MODSCAN32

使用特权

评论回复
10
ocon| | 2016-2-29 15:14 | 只看该作者
监听一下这个串口实际传输的数据,应该能找到答案,如果问题出在单片机,重点检查一下CRC校验计算,数据帧间的延时等等。

使用特权

评论回复
11
冰零分子|  楼主 | 2016-3-1 09:06 | 只看该作者
undersky 发表于 2016-2-29 13:13
先确认是哪里的问题。第一、上位机换一个试试(推荐用Modbus Poll),其次检查你下位机06处理的地方,单步 ...

谢谢,我下个试试:)

使用特权

评论回复
12
冰零分子|  楼主 | 2016-3-1 09:07 | 只看该作者
ningling_21 发表于 2016-2-29 14:06
换个软件试试,例如MODSCAN32

谢谢你的建议,我试下

使用特权

评论回复
13
冰零分子|  楼主 | 2016-3-1 09:07 | 只看该作者
ocon 发表于 2016-2-29 15:14
监听一下这个串口实际传输的数据,应该能找到答案,如果问题出在单片机,重点检查一下CRC校验计算,数据帧 ...


嗯嗯,谢谢你的建议,我检查下

使用特权

评论回复
14
冰零分子|  楼主 | 2016-3-1 10:10 | 只看该作者
ocon 发表于 2016-2-29 15:14
监听一下这个串口实际传输的数据,应该能找到答案,如果问题出在单片机,重点检查一下CRC校验计算,数据帧 ...

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

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

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

使用特权

评论回复
15
元方义城| | 2016-3-1 10:16 | 只看该作者
冰零分子 发表于 2016-3-1 10:10
前辈,现在情况是这样的:
用串口直接发送0x1234到寄存器1接收没有问题

可能是程序问题,死在什么地方了。

使用特权

评论回复
16
冰零分子|  楼主 | 2016-3-1 10:25 | 只看该作者
本帖最后由 冰零分子 于 2016-3-1 10:29 编辑
ocon 发表于 2016-2-29 15:14
监听一下这个串口实际传输的数据,应该能找到答案,如果问题出在单片机,重点检查一下CRC校验计算,数据帧 ...

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

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

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

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

现在通讯失败后会干扰下次通讯,是不是说通讯失败后cy_uartnumber就没有被清零

使用特权

评论回复
17
ningling_21| | 2016-3-1 10:40 | 只看该作者
冰零分子 发表于 2016-3-1 10:25
还有一个就是,我发现一旦通讯不正常再写0x1f75以下的数据也接收不到应答了。

我通讯函数ModbusRTUCommun ...

如果程序可以调试,进入调试模式很容易就找到问题

使用特权

评论回复
18
冰零分子|  楼主 | 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值也无法收到。那就是没有产生中断或串口发送无响应者两种情况了吧?

使用特权

评论回复
19
冰零分子|  楼主 | 2016-3-1 11:05 | 只看该作者
ningling_21 发表于 2016-3-1 10:40
如果程序可以调试,进入调试模式很容易就找到问题

木有JLINK

使用特权

评论回复
20
ocon| | 2016-3-1 11:23 | 只看该作者
你已经明白解决之道,相信3天之内就可以解决这个小问题:lol

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

15

主题

950

帖子

19

粉丝