分享

各位帮我测试一下modbus RTU 从站协议代码

 5ccampus 2022-01-31
 本帖最后由 frame66 于 2015-5-16 09:39 编辑

我想通过arduino做一个modbus RUT通讯协议和其他一些产品通讯。
在网上找了些代码,但基本上要么没什么注释,要么注释全是英文看的累,要么是本人水平有限看不懂,要么是功能不完善。{:soso_e143:}
于是打算自己写!{:soso_e130:}
现在程序目前实现了modbus功能码的03、06、16。我用modbus调试软件modbus poll测试了一下,可以正常的读写数据。
今天就写这么多,有在时间继续完善。{:soso_e113:}
我把代码贴出来,注释写的很详细,大家帮我测试一下,看看哪里写的需要修改,我想完工后做成一个库文件,在和大家分享。



  1. #define bufferSize 255  //一帧数据的最大字节数量
  2. #define baudrate 115200  //定义通讯波特率
  3. #define slaveID 1  //定义modbus RTU从站站号
  4. #define modbusDataSize 100  //定义modbus数据库空间大小

  5. unsigned int modbusData[modbusDataSize]={};   //建立modbus数据库

  6. unsigned int calculateCRC(unsigned char* _regs,unsigned char arraySize);  //声明CRC校验函数
  7. void modbusRTU_slave();  //声明modbus RTU从站函数


  8. void setup()  //初始化函数
  9. {
  10.   Serial.begin(baudrate);
  11.   Serial.flush();


  12. }

  13. void loop()   //主循环
  14. {
  15.   modbusRTU_slave();  //执行modbus函数
  16. }


  17. void modbusRTU_slave()
  18. {
  19.   unsigned char frame[bufferSize];  //用于保存接收或发送的数据
  20.   unsigned int characterTime; //字符时间
  21.   unsigned int errorFlag=0;  //错误标志
  22.   unsigned int crc16;  //校验位
  23.   unsigned char address=0;

  24.   if (baudrate > 19200)  //波特率大于19200时进入条件
  25.   {
  26.     characterTime = 750;
  27.   }
  28.   else
  29.   {
  30.     characterTime = 15000000/baudrate;  //1.5字符时间
  31.   }
  32.   while(Serial.available()>0)  //如果串口缓冲区数据量大于0进入条件
  33.   {
  34.    
  35.     if(address<bufferSize)  //接收的数据量应小于一帧数据的最大字节数量
  36.     {
  37.       frame[address]=Serial.read();
  38.       address++;
  39.     }
  40.     else  //条件不满足时直接清空缓冲区
  41.     {
  42.        Serial.read();
  43.     }
  44.     delayMicroseconds(characterTime);  //等待1.5个字符时间
  45.     if(Serial.available()==0)  //1.5个字符时间后缓冲区仍然没有收到数据,认为一帧数据已经接收完成,进入条件
  46.     {
  47.       if(frame[0]==slaveID||frame[0]==0)  //站号匹配或者消息为广播形式,进入条件
  48.       {
  49.         crc16 = ((frame[address - 2] << 8) | frame[address - 1]);
  50.         if(calculateCRC(&frame[0],address - 2)==crc16)  //数据校验通过,进入条件
  51.         {
  52.           unsigned char function=frame[1];  //读取功能码
  53.           if (frame[0]!=0 && (function == 3))  //功能码03不支持广播消息
  54.           {
  55.             unsigned int startData=((frame[2] << 8) | frame[3]);  //读取modbus数据库起始地址           
  56.             unsigned int dataSize=((frame[4] << 8) | frame[5]);  //需要读取的modbus数据库数据长度
  57.             unsigned int endData=startData+dataSize;    //需要读取的modbus数据库数据的结束地址
  58.             unsigned char responseSize=5+dataSize*2;  //计算应答的数据长度
  59.             unsigned int temp1,temp2,temp3;
  60.             
  61.             if(dataSize>125)  //modbus一帧的最大数据量为255个字节,即一次最多读取125个数据
  62.             {
  63.               errorFlag=1;
  64.               //待添加错误信息返回函数
  65.             }
  66.             else
  67.             {
  68.               frame[0]=slaveID;  //设定站号
  69.               frame[1]=function;  //设定功能码
  70.               frame[2]=dataSize*2;  //设定数据长度
  71.               temp3=3;
  72.               for(temp1=startData;temp1<endData;temp1++)
  73.               {
  74.                 if(temp1>=modbusDataSize)  //需要读取的地址大于modbus数据库的最大地址
  75.                 {
  76.                   temp2=0;
  77.                 }
  78.                 else  //取出modbus数据库中的数据
  79.                 {
  80.                   temp2=modbusData[temp1];
  81.                 }
  82.                 frame[temp3]=temp2>>8;
  83.                 temp3++;
  84.                 frame[temp3]=temp2 & 0xFF;
  85.                 temp3++;
  86.               }
  87.               crc16 = calculateCRC(&frame[0],responseSize-2);
  88.               frame[responseSize-2] = crc16 >> 8;  //填写校验位
  89.               frame[responseSize-1] = crc16 & 0xFF;
  90.               Serial.write(&frame[0],responseSize);  //返回功能码03的消息
  91.             }
  92.           }
  93.           else if(function == 6)  //功能码为06时进入条件
  94.           {
  95.             unsigned int startData=((frame[2] << 8) | frame[3]);  //写入modbus数据库的地址           
  96.             unsigned int setData=((frame[4] << 8) | frame[5]);  //写入modbus数据库的数值
  97.             if(startData>=modbusDataSize)
  98.             {
  99.               errorFlag=1;
  100.               //待添加错误信息返回函数
  101.             }
  102.             else
  103.             {
  104.               modbusData[startData]=setData;  //写入数据到modbus数据库              
  105.               frame[0]=slaveID;  //设定站号
  106.               frame[1]=function;  //设定功能码
  107.               frame[2] = startData >> 8;  //填写数据库地址
  108.               frame[3] = startData & 0xFF;            
  109.               frame[4] = modbusData[startData] >> 8;  //填写数据库数值
  110.               frame[5] = modbusData[startData] & 0xFF;
  111.               crc16 = calculateCRC(&frame[0],6);  //计算校验值
  112.               frame[6] = crc16 >> 8;  //填写校验位
  113.               frame[7] = crc16 & 0xFF;
  114.               Serial.write(&frame[0],8);  //返回功能码06的消息              
  115.             }
  116.           }
  117.           else if(function==16)  //功能码为16时进入条件
  118.           {
  119.             if(frame[6]!=address-9)  //校验数据长度
  120.             {
  121.             }
  122.             else  //校验数据长度正确
  123.             {
  124.               unsigned int startData=((frame[2] << 8) | frame[3]);  //写入modbus数据库起始地址           
  125.               unsigned int dataSize=((frame[4] << 8) | frame[5]);  //需要写入的modbus数据库数据长度
  126.               unsigned int endData=startData+dataSize;    //需要写入的modbus数据库数据的结束地址
  127.               unsigned int temp1,temp2;
  128.               temp2=7;  //从数据贞的第8个数据开始读取            
  129.               for(temp1=startData;temp1<endData;temp1++)
  130.               {
  131.                 if(temp1>=modbusDataSize)  //需要写入的地址大于modbus数据库的最大地址
  132.                 {
  133.                   //待添加错误信息返回函数
  134.                 }
  135.                 else  //将数据写入modbus数据库中
  136.                 {
  137.                   modbusData[temp1]=(frame[temp2]<<8|frame[temp2+1]);
  138.                   temp2+=2;
  139.                 }
  140.               }
  141.               frame[0]=slaveID;  //填写站号,frame[1]到frame[5]不变
  142.               crc16 = calculateCRC(&frame[0],6);  //计算CRC校验
  143.               frame[6] = crc16 >> 8;  //填写校验位
  144.               frame[7] = crc16 & 0xFF;
  145.               Serial.write(&frame[0],8);  //发送功能码16的应答数据        
  146.             }
  147.           }      
  148.         }
  149.       }
  150.     }
  151.   }
  152. }




  153. //CRC校验函数
  154. //参数1:待校验数组的起始地址
  155. //参数2:待校验数组的长度
  156. //返回值CRC校验结果,16位,低字节在前
  157. unsigned int calculateCRC(unsigned char* _regs,unsigned char arraySize)
  158. {
  159.   unsigned int temp, temp2, flag;
  160.   temp = 0xFFFF;
  161.   for (unsigned char i = 0; i < arraySize; i++)
  162.   {
  163.     temp = temp ^ *(_regs+i);
  164.     for (unsigned char j = 1; j <= 8; j++)
  165.     {
  166.       flag = temp & 0x0001;
  167.       temp >>= 1;
  168.       if (flag)
  169.         temp ^= 0xA001;
  170.     }
  171.   }
  172.   temp2 = temp >> 8;
  173.   temp = (temp << 8) | temp2;
  174.   temp &= 0xFFFF;
  175.   return temp;
  176. }

复制代码



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多