您的位置:控制工程论坛网论坛 » 现场总线 » 51单片机实现实现MODBUS RTU协议

wj321

wj321   |   当前状态:离线

总积分:1211  2025年可用积分:0

注册时间: 2003-05-12

最后登录时间: 2009-11-05

空间 发短消息加为好友

51单片机实现实现MODBUS RTU协议

wj321  发表于 2008/10/24 16:17:09      2451 查看 2 回复  [上一主题]  [下一主题]

手机阅读

一、通讯协议:

1、通讯传送方式:

通讯传送分为独立的信息头,和发送的编码数据。以下的通讯传送方式定义也与MODBUS RTU通讯规约相兼容:

编 码 8位二进制
起始位 1位
数据位 8位
奇偶校验位 1位(偶校验位)
停止位 1位
错误校检 CRC(冗余循环码)

初始结构 = ≥4字节的时间

地址码 = 1 字节

功能码 = 1 字节

数据区 = N 字节

错误校检 = 16位CRC码

结束结构 = ≥4字节的时间

地址码:地址码为通讯传送的第一个字节。这个字节表明由用户设定地址码的从机将接收由主机发送来的信息。并且每个从机都有具有唯一的地址码,并且响应回送均以各自的地址码开始。主机发送的地址码表明将发送到的从机地址,而从机发送的地址码表明回送的从机地址。

功能码:通讯传送的第二个字节。ModBus通讯规约定义功能号为1到127。本仪表只利用其中的一部分功能码。作为主机请求发送,通过功能码告诉从机执行什么动作。作为从机响应,从机发送的功能码与从主机发送来的功能码一样,并表明从机已响应主机进行操作。如果从机发送的功能码的最高位为1(比如功能码大与此同时127),则表明从机没有响应操作或发送出错。

数据区:数据区是根据不同的功能码而不同。数据区可以是实际数值、设置点、主机发送给从机或从机发送给主机的地址

1楼 0 0 回复
  • wj321

    wj321   |   当前状态:离线

    总积分:1211  2025年可用积分:0

    注册时间: 2003-05-12

    最后登录时间: 2009-11-05

    空间 发短消息加为好友

    wj321   发表于 2008/10/24 16:13:52














    N


    CRC H


    CRC L


    Addr1


    3 H


    返回数据的字节数N


    Data (1N)


    CRC高位


    CRC低位


    帧 长 度:5N 个字节


    设备地址:1247


    功 能 码:3H


    数 据 量:实际的读取数据数量         


        据:返回数据的意义


    aHoldStart


    n= DataNum1

















    VW a VB a


    VWaVB a+1



    VW a+nVB a+n


    VWa+nVB a+n+1


    Data1


    Data(2)



    Data(N-1)


    Data(N)


    校 验 码:CRC16校验


     


    命令有误:


    1)        没有任何返回


    2)        返回异议帧

















    设备地址


    功能码


    错误信息


    CRC H


    CRC L


    Addr1


    83 H


    一个字节的错误信息


    CRC高位


    CRC低位

    2楼 回复本楼

    引用 wj321 2008/10/24 16:13:52 发表于2楼的内容

  • wj321

    wj321   |   当前状态:离线

    总积分:1211  2025年可用积分:0

    注册时间: 2003-05-12

    最后登录时间: 2009-11-05

    空间 发短消息加为好友

    wj321   发表于 2008/10/24 16:17:09

    uint8   testCoil; //用于测试 位地址1
    uint16  testRegister; //用于测试 字地址0


    uint8 localAddr = 1; //地址
    uint8 sendCount;  //发送字节个数
    uint8 receCount;  //接收字节个数
    uint8 sendPosi;  //发送位置
    uint8 sendBuf[32],receBuf[32]; //发送,接收缓冲区  
    uint8 checkoutError;    //校验结果
    uint8 receTimeOut;    //接收超时


    //CRC较验计算
    uint16 crc16(uint8 *puchMsg, uint16 usDataLen)
    {
     uint8 uchCRCHi = 0xFF ; /* 高CRC字节初始化 */
     uint8 uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */
     uint32 uIndex ; /* CRC循环中的索引 */
     while (usDataLen--) /* 传输消息缓冲区 */
     {
      uIndex = uchCRCHi ^ *puchMsg++ ; /* 计算CRC */
      uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
      uchCRCLo = auchCRCLo[uIndex] ;
     }
     return (uchCRCHi << 8 | uchCRCLo) ;
    }//uint16 crc16(uint8 *puchMsg, uint16 usDataLen)



    //MODBUS状态机,在主程序中调用即可
    void checkComm0Modbus(void)
    {
     uint16 crcData;
     uint16 tempData;
     
     if(receCount > 4)
     {
      switch(receBuf[1])
      {


       // 其它的功能码可以参照协议自行编写
       case 1://读取线圈状态(读取点 16位以内)
       case 3://读取保持寄存器(一个或多个)
       case 5://强制单个线圈
       case 6://设置单个寄存器   
         if(receCount >= 8)
         {//接收完成一组数据     
          //应该关闭接收中断
          UCSRB &= ~BIT(7);    
          if(receBuf[0]==localAddr && checkoutError==0)
          {
           crcData = crc16(receBuf,6);
           if(crcData == receBuf[7]+(receBuf[6]<<8))
           {//校验正确
            if(receBuf[1] == 1)
            {//读取线圈状态(读取点 16位以内)
             readCoil();        
            }
            else if(receBuf[1] == 3)
            {//读取保持寄存器(一个或多个)
             readRegisters();
            }
            else if(receBuf[1] == 5)
            {//强制单个线圈
             forceSingleCoil();        
            }
            else if(receBuf[1] == 6)
            {
             //presetSingleRegister();        
            }


           }
          }           
          receCount = 0; 
          checkoutError = 0; 
          UCSRB |= BIT(7);           
         }
         break;
      
       case 15://设置多个线圈
         tempData = receBuf[6];
         tempData += 9; //数据个数
         if(receCount >= tempData)
         {//应该关闭接收中断
          UCSRB &= ~BIT(7);     
          if(receBuf[0]==localAddr && checkoutError==0)
          {
           crcData = crc16(receBuf,tempData-2);
           if(crcData == (receBuf[tempData-2]<<8)+ receBuf[tempData-1])
           {
            //forceMultipleCoils();   
           }
          } 
          receCount = 0;
          checkoutError = 0;
          UCSRB |= BIT(7);
         }
         break;
       
       case 16://设置多个寄存器
         tempData = (receBuf[4]<<8) + receBuf[5];
         tempData = tempData * 2; //数据个数
         tempData += 9;
         if(receCount >= tempData)
         {//应该关闭接收中断
          UCSRB &= ~BIT(7);           
          if(receBuf[0]==localAddr && checkoutError==0)
          {
           crcData = crc16(receBuf,tempData-2);
           if(crcData == (receBuf[tempData-2]<<8)+ receBuf[tempData-1])
           {
            presetMultipleRegisters();   
           }
          } 
          receCount = 0;
          checkoutError = 0;
          UCSRB |= BIT(7);
         }
         break;
            
       default:
         break;   
      }
     }
    }

    3楼 回复本楼

    引用 wj321 2008/10/24 16:17:09 发表于3楼的内容

总共 , 当前 /