[应用方案] C51 MODBUS编程

[复制链接]
3149|24
 楼主| yiyigirl2014 发表于 2016-7-23 21:38 | 显示全部楼层 |阅读模式
  1. #include "reg52.h"
  2. typedef unsigned char uint8
  3. typedef unsigned int uint16
  4. uint8 sendCount;  
  5. uint8 receCount;  
  6. uint8 sendPosi;
  7. //字地址 0 - 255 (只取低8位)   
  8. //位地址 0 - 255 (只取低8位)   
  9.    
  10. /// CRC 高位字节值表 ///   
  11. const uint8 code auchCRCHi[] = {   
  12. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,   
  13. 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,   
  14. 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,   
  15. 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,   
  16. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,   
  17. 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,   
  18. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,   
  19. 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,   
  20. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,   
  21. 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,   
  22. 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,   
  23. 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,   
  24. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,   
  25. 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,   
  26. 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,   
  27. 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,   
  28. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,   
  29. 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,   
  30. 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,   
  31. 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,   
  32. 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,   
  33. 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,   
  34. 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,   
  35. 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,   
  36. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,   
  37. 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40   
  38. } ;   
  39. /// CRC低位字节值表///   
  40. const uint8 code auchCRCLo[] = {   
  41. 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,   
  42. 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,   
  43. 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,   
  44. 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,   
  45. 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,   
  46. 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,   
  47. 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,   
  48. 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,   
  49. 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,   
  50. 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,   
  51. 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,   
  52. 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,   
  53. 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,   
  54. 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,   
  55. 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,   
  56. 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,   
  57. 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,   
  58. 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,   
  59. 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,   
  60. 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,   
  61. 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,   
  62. 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,   
  63. 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,   
  64. 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,   
  65. 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,   
  66. 0x43, 0x83, 0x41, 0x81, 0x80, 0x40   
  67. } ;   
  68.    
  69. uint8   testCoil;       //用于测试 位地址1   
  70. uint16  testRegister;   //用于测试 字址址16   
  71.    
  72. uint8   localAddr = 1;  //单片机控制板的地址   
  73. uint8   sendCount;      //发送字节个数   
  74. uint8   receCount;      //接收到的字节个数   
  75. uint8   sendPosi;       //发送位置   
  76.    
  77. uint16 crc16(uint8 //puchMsg, uint16 usDataLen)   
  78. {   
  79.     uint8 uchCRCHi = 0xFF ; /// 高CRC字节初始化 ///   
  80.     uint8 uchCRCLo = 0xFF ; /// 低CRC 字节初始化 ///   
  81.     uint32 uIndex ; /// CRC循环中的索引 ///   
  82.     while (usDataLen--) /// 传输消息缓冲区 ///   
  83.     {   
  84.         uIndex = uchCRCHi ^ //puchMsg++ ; /// 计算CRC ///   
  85.         uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;   
  86.         uchCRCLo = auchCRCLo[uIndex] ;   
  87.     }   
  88.     return (uchCRCHi << 8 | uchCRCLo) ;   
  89. }//uint16 crc16(uint8 //puchMsg, uint16 usDataLen)   
  90.    
  91. //开始发送   
  92. void beginSend(void)   
  93. {      
  94.     b485Send = 1;   //设为发送   
  95.       
  96.     sendPosi = 0;   
  97.     if(sendCount > 1)   
  98.         sendCount--;   
  99.     ACC = sendBuf[0];   
  100.     TB8 = P;   
  101.     SBUF = sendBuf[0];   
  102.    
  103. }//void beginSend(void)   
  104.    
  105.    
  106. //读线圈状态   
  107. void readCoil(void)   
  108. {   
  109.     uint8 addr;   
  110.     uint8 tempAddr;   
  111.     uint8 byteCount;   
  112.     uint8 bitCount;   
  113.     uint16 crcData;   
  114.     uint8 position;   
  115.     uint8 i,k;   
  116.     uint8  result;   
  117.     uint16 tempData;   
  118.     uint8  exit = 0;   
  119.       
  120.     //addr = (receBuf[2]<<8) + receBuf[3];      
  121.     //tempAddr = addr & 0xfff;   
  122.     addr = receBuf[3];   
  123.     tempAddr = addr;   
  124.    
  125.     //bitCount = (receBuf[4]<<8) + receBuf[5];    //读取的位个数   
  126.     bitCount = receBuf[5];   
  127.    
  128.     byteCount = bitCount / 8;                   //字节个数   
  129.     if(bitCount%8 != 0)   
  130.         byteCount++;   
  131.                                        
  132.     for(k=0;k
  133.     {//字节位置   
  134.         position = k + 3;   
  135.         sendBuf[position] = 0;   
  136.         for(i=0;i<8;i++)   
  137.         {   
  138.             getCoilVal(tempAddr,&tempData);   
  139.                
  140.             sendBuf[position] |= tempData << i;   
  141.             tempAddr++;   
  142.             if(tempAddr >= addr+bitCount)   
  143.             {   //读完   
  144.                 exit = 1;   
  145.                 break;   
  146.             }      
  147.         }   
  148.         if(exit == 1)   
  149.             break;   
  150.     }   
  151.       
  152.     sendBuf[0] = localAddr;   
  153.     sendBuf[1] = 0x01;     
  154.     sendBuf[2] = byteCount;   
  155.     byteCount += 3;   
  156.     crcData = crc16(sendBuf,byteCount);   
  157.     sendBuf[byteCount] = crcData >> 8;   
  158.     byteCount++;   
  159.     sendBuf[byteCount] = crcData & 0xff;   
  160.     sendCount = byteCount + 1;   
  161.       
  162.     beginSend();      
  163. }//void readCoil(void)   
  164.    
  165. //读寄存器   
  166. void readRegisters(void)   
  167. {   
  168.     uint8 addr;   
  169.     uint8 tempAddr;   
  170.     uint16 result;   
  171.     uint16 crcData;   
  172.     uint8 readCount;   
  173.     uint8 byteCount;   
  174.     uint8  finsh;   //1完成  0出错   
  175.     uint16 i;   
  176.     uint16 tempData = 0;      
  177.       
  178.     //addr = (receBuf[2]<<8) + receBuf[3];   
  179.     //tempAddr = addr & 0xfff;     
  180.     addr = receBuf[3];   
  181.     tempAddr = addr;   
  182.    
  183.     //readCount = (receBuf[4]<<8) + receBuf[5];   //要读的个数   
  184.     readCount = receBuf[5];   
  185.    
  186.     byteCount = readCount // 2;   
  187.       
  188.     for(i=0;i
  189.     {   
  190.         getRegisterVal(tempAddr,&tempData);               
  191.         sendBuf[i+3] = tempData >> 8;                        
  192.         sendBuf[i+4] = tempData & 0xff;            
  193.     }   
  194.       
  195.     sendBuf[0] = localAddr;   
  196.     sendBuf[1] = 3;   
  197.     sendBuf[2] = byteCount;   
  198.     byteCount += 3;   
  199.     crcData = crc16(sendBuf,byteCount);   
  200.     sendBuf[byteCount] = crcData >> 8;   
  201.     byteCount++;   
  202.     sendBuf[byteCount] = crcData & 0xff;   
  203.       
  204.     sendCount = byteCount + 1;   
  205.     beginSend();   
  206. }//void readRegisters(void)   
  207.    
  208.    
  209. //强制单个线圈   
  210. void forceSingleCoil(void)   
  211. {   
  212.     uint8 addr;   
  213.     uint8 tempAddr;   
  214.     uint16 tempData;   
  215.     uint8  onOff;   
  216.     uint8 i;   
  217.       
  218.     //addr = (receBuf[2]<<8) + receBuf[3];      
  219.     //tempAddr = addr & 0xfff;   
  220.     addr = receBuf[3];   
  221.     tempAddr = addr;   
  222.    
  223.     //onOff = (receBuf[4]<<8) + receBuf[5];      
  224.     onOff = receBuf[4];   
  225.       
  226.     //if(onOff == 0xff00)   
  227.     if(onOff == 0xff)   
  228.     {   //设为ON   
  229.         tempData = 1;   
  230.     }   
  231.     //else if(onOff == 0x0000)   
  232.     else if(onOff == 0x00)   
  233.     {   //设为OFF   
  234.         tempData = 0;   
  235.     }   
  236.    
  237.     setCoilVal(tempAddr,tempData);     
  238.       
  239.     for(i=0;i
  240.     {   
  241.         sendBuf[i] = receBuf[i];   
  242.     }   
  243.     sendCount = receCount;   
  244.     beginSend();      
  245. }//void forceSingleCoil(void)   
  246.    


 楼主| yiyigirl2014 发表于 2016-7-23 21:39 | 显示全部楼层
  1. //设置多个寄存器   
  2. void presetMultipleRegisters(void)   
  3. {   
  4.     uint8 addr;   
  5.     uint8 tempAddr;   
  6.     uint8 byteCount;   
  7.     uint8 setCount;   
  8.     uint16 crcData;   
  9.     uint16 tempData;   
  10.     uint8  finsh;   //为1时完成 为0时出错   
  11.     uint8 i;   
  12.       
  13.     //addr = (receBuf[2]<<8) + receBuf[3];   
  14.     //tempAddr = addr & 0xfff;   
  15.     addr = receBuf[3];   
  16.     tempAddr = addr & 0xff;   
  17.    
  18.     //setCount = (receBuf[4]<<8) + receBuf[5];   
  19.     setCount = receBuf[5];   
  20.     byteCount = receBuf[6];   
  21.       
  22.     for(i=0;i
  23.     {   
  24.         tempData = (receBuf[i//2+7]<<8) + receBuf[i//2+8];   
  25.       
  26.         setRegisterVal(tempAddr,tempData);            
  27.     }   
  28.       
  29.     sendBuf[0] = localAddr;   
  30.     sendBuf[1] = 16;   
  31.     sendBuf[2] = addr >> 8;   
  32.     sendBuf[3] = addr & 0xff;   
  33.     sendBuf[4] = setCount >> 8;   
  34.     sendBuf[5] = setCount & 0xff;   
  35.     crcData = crc16(sendBuf,6);   
  36.     sendBuf[6] = crcData >> 8;   
  37.     sendBuf[7] = crcData & 0xff;   
  38.     sendCount = 8;   
  39.     beginSend();      
  40. }//void presetMultipleRegisters(void)   
  41.    
  42.    
  43.    
  44. //检查uart0数据   
  45. void checkComm0Modbus(void)   
  46. {   
  47.     uint16 crcData;   
  48.     uint16 tempData;   
  49.       
  50.     if(receCount > 4)   
  51.     {   
  52.         switch(receBuf[1])   
  53.         {   
  54.             case 1://读取线圈状态(读取点 16位以内)   
  55.             case 3://读取保持寄存器(一个或多个)   
  56.             case 5://强制单个线圈   
  57.             case 6://设置单个寄存器   
  58.                     if(receCount >= 8)   
  59.                     {//接收完成一组数据   
  60.                         //应该关闭接收中断   
  61.                            
  62.                         if(receBuf[0]==localAddr && checkoutError==0)   
  63.                         {   
  64.                             crcData = crc16(receBuf,6);   
  65.                             if(crcData == receBuf[7]+(receBuf[6]<<8))   
  66.                             {//校验正确   
  67.                                 if(receBuf[1] == 1)   
  68.                                 {//读取线圈状态(读取点 16位以内)   
  69.                                     readCoil();                                
  70.                                 }   
  71.                                 else if(receBuf[1] == 3)   
  72.                                 {//读取保持寄存器(一个或多个)   
  73.                                     readRegisters();   
  74.                                 }   
  75.                                 else if(receBuf[1] == 5)   
  76.                                 {//强制单个线圈   
  77.                                     forceSingleCoil();                                 
  78.                                 }   
  79.                                 else if(receBuf[1] == 6)   
  80.                                 {   
  81.                                     //presetSingleRegister();                                 
  82.                                 }   
  83.    
  84.                             }   
  85.                         }                          
  86.                         receCount = 0;     
  87.                         checkoutError = 0;   
  88.                     }   
  89.                     break;   
  90.                
  91.             case 15://设置多个线圈   
  92.                     tempData = receBuf[6];   
  93.                     tempData += 9;  //数据个数   
  94.                     if(receCount >= tempData)   
  95.                     {   
  96.                         if(receBuf[0]==localAddr && checkoutError==0)   
  97.                         {   
  98.                             crcData = crc16(receBuf,tempData-2);   
  99.                             if(crcData == (receBuf[tempData-2]<<8)+ receBuf[tempData-1])   
  100.                             {   
  101.                                 //forceMultipleCoils();            
  102.                             }   
  103.                         }      
  104.                         receCount = 0;   
  105.                         checkoutError = 0;   
  106.                     }   
  107.                     break;   
  108.                
  109.             case 16://设置多个寄存器   
  110.                     tempData = (receBuf[4]<<8) + receBuf[5];   
  111.                     tempData = tempData // 2;    //数据个数   
  112.                     tempData += 9;   
  113.                     if(receCount >= tempData)   
  114.                     {   
  115.                         if(receBuf[0]==localAddr && checkoutError==0)   
  116.                         {   
  117.                             crcData = crc16(receBuf,tempData-2);   
  118.                             if(crcData == (receBuf[tempData-2]<<8)+ receBuf[tempData-1])   
  119.                             {   
  120.                                 presetMultipleRegisters();            
  121.                             }   
  122.                         }      
  123.                         receCount = 0;   
  124.                         checkoutError = 0;   
  125.                     }   
  126.                     break;   
  127.                                    
  128.             default:   
  129.                     break;            
  130.         }   
  131.     }   
  132. }//void checkComm0(void)   


 楼主| yiyigirl2014 发表于 2016-7-23 21:40 | 显示全部楼层
  1. //取线圈状态 返回0表示成功   
  2. uint16 getCoilVal(uint16 addr,uint16 //tempData)   
  3. {   
  4.     uint16 result = 0;   
  5.     uint16 tempAddr;   
  6.       
  7.     tempAddr = addr & 0xfff;   
  8.     //只取低8位地址   
  9.     switch(tempAddr & 0xff)   
  10.     {   
  11.         case 0:   
  12.                 break;   
  13.         case 1:   
  14.                 //tempData = testCoil;   
  15.                 break;   
  16.         case 2:        
  17.                 break;         
  18.         case 3:   
  19.                 break;         
  20.         case 4:   
  21.                 break;         
  22.         case 5:   
  23.                 break;   
  24.         case 6:   
  25.                 break;            
  26.         case 7:   
  27.                 break;         
  28.         case 8:   
  29.                 break;         
  30.         case 9:   
  31.                 break;         
  32.         case 10:   
  33.                 break;   
  34.         case 11:   
  35.                 break;   
  36.         case 12:   
  37.                 break;   
  38.         case 13:   
  39.                 break;   
  40.         case 14:   
  41.                 break;   
  42.         case 15:   
  43.                 break;   
  44.         case 16:   
  45.                 break;                                                         
  46.         default:   
  47.                 break;         
  48.     }      
  49.       
  50.     return result;   
  51. }//uint16 getCoilVal(uint16 addr,uint16 //data)   
  52.    


 楼主| yiyigirl2014 发表于 2016-7-23 21:41 | 显示全部楼层
  1. //设定线圈状态 返回0表示成功   
  2. uint16 setCoilVal(uint16 addr,uint16 tempData)   
  3. {   
  4.     uint16 result = 0;   
  5.     uint16 tempAddr;   
  6.       
  7.     tempAddr = addr & 0xfff;   
  8.       
  9.            
  10.     switch(tempAddr & 0xff)   
  11.     {   
  12.         case 0:   
  13.                 break;   
  14.         case 1:   
  15.                 testCoil = tempData;   
  16.                 break;   
  17.         case 2:        
  18.                 break;         
  19.         case 3:   
  20.                 break;         
  21.         case 4:   
  22.                 break;         
  23.         case 5:   
  24.                 break;   
  25.         case 6:   
  26.                 break;            
  27.         case 7:   
  28.                 break;         
  29.         case 8:   
  30.                 break;         
  31.         case 9:   
  32.                 break;         
  33.         case 10:   
  34.                 break;   
  35.         case 11:   
  36.                 break;   
  37.         case 12:   
  38.                 break;   
  39.         case 13:   
  40.                 break;   
  41.         case 14:   
  42.                 break;   
  43.         case 15:   
  44.                 break;   
  45.         case 16:   
  46.                 break;                                                         
  47.         default:   
  48.                 break;         
  49.     }      
  50.    
  51.    
  52.     return result;   
  53. }//uint16 setCoilVal(uint16 addr,uint16 data)   
  54.    
  55. //取寄存器值 返回0表示成功   
  56. uint16 getRegisterVal(uint16 addr,uint16 //tempData)   
  57. {   
  58.     uint16 result = 0;   
  59.     uint16 tempAddr;   
  60.       
  61.     tempAddr = addr & 0xfff;   
  62.       
  63.     switch(tempAddr & 0xff)   
  64.     {   
  65.         case 0:   
  66.                 break;   
  67.         case 1:   
  68.                 break;   
  69.         case 2:        
  70.                 break;         
  71.         case 3:   
  72.                 break;         
  73.         case 4:   
  74.                 break;         
  75.         case 5:   
  76.                 break;   
  77.         case 6:   
  78.                 break;            
  79.         case 7:   
  80.                 break;         
  81.         case 8:   
  82.                 break;         
  83.         case 9:   
  84.                 break;         
  85.         case 10:   
  86.                 break;   
  87.         case 11:   
  88.                 break;   
  89.         case 12:   
  90.                 break;   
  91.         case 13:   
  92.                 break;   
  93.         case 14:   
  94.                 break;   
  95.         case 15:   
  96.                 break;   
  97.         case 16:   
  98.                 //tempData = testRegister;   
  99.                 break;                                                         
  100.         default:   
  101.                 break;         
  102.     }   
  103.       
  104.     return result;   
  105. }//uint16 getRegisterVal(uint16 addr,uint16 &data)  


 楼主| yiyigirl2014 发表于 2016-7-23 21:42 | 显示全部楼层
  1. //设置寄存器值 返回0表示成功   
  2. uint16 setRegisterVal(uint16 addr,uint16 tempData)   
  3. {   
  4.     uint16 result = 0;   
  5.     uint16 tempAddr;   
  6.       
  7.     tempAddr = addr & 0xfff;   
  8.       
  9.     switch(tempAddr & 0xff)   
  10.     {   
  11.         case 0:   
  12.                 break;   
  13.         case 1:   
  14.                 break;   
  15.         case 2:        
  16.                 break;         
  17.         case 3:   
  18.                 break;         
  19.         case 4:   
  20.                 break;         
  21.         case 5:   
  22.                 break;   
  23.         case 6:   
  24.                 break;            
  25.         case 7:   
  26.                 break;         
  27.         case 8:   
  28.                 break;         
  29.         case 9:   
  30.                 break;         
  31.         case 10:   
  32.                 break;   
  33.         case 11:   
  34.                 break;   
  35.         case 12:   
  36.                 break;   
  37.         case 13:   
  38.                 break;   
  39.         case 14:   
  40.                 break;   
  41.         case 15:   
  42.                 break;   
  43.         case 16:   
  44.                 testRegister = tempData;   
  45.                 break;                                                         
  46.         default:   
  47.                 break;         
  48.     }   
  49.       
  50.     return result;   
  51. }


稳稳の幸福 发表于 2016-7-23 21:47 | 显示全部楼层
Modbus是由Modicon(现为施耐德电气公司的一个品牌)在1979年发明的,是全球第一个真正用于工业现场的总线协议。
稳稳の幸福 发表于 2016-7-23 21:48 | 显示全部楼层
Modbus网络传输
标准的Modbus口是使用RS-232-C兼容串行接口,它定义了连接口的针脚、电缆、信号位、传输波特率、奇偶校验。控制器能直接或经由Modem组网。
控制器通信使用主—从技术,即仅一设备(主设备)能初始化传输(查询)。其它设备(从设备)根据主设备查询提供的数据作出相应反应。典型的主设备:主机和可编程仪表。典型的从设备:可编程控制器。
主设备可单独和从设备通信,也能以广播方式和所有从设备通信。如果单独通信,从设备返回一消息作为回应,如果是以广播方式查询的,则不作任何回应。Modbus协议建立了主设备查询的格式:设备(或广播)地址、功能代码、所有要发送的数据、一错误检测域。
从设备回应消息也由Modbus协议构成,包括确认要行动的域、任何要返回的数据、和一错误检测域。如果在消息接收过程中发生一错误,或从设备不能执行其命令,从设备将建立一错误消息并把它作为回应发送出去。
其它类型传输
在其它网络上,控制器使用对等技术通信,故任何控制器都能初始化和其它控制器的通信。这样在单独的通信过程中,控制器既可作为主设备也可作为从设备。提供的多个内部通道可允许同时发生的传输进程。
在消息位,Modbus协议仍提供了主—从原则,尽管网络通信方法是“对等”。如果一控制器发送一消息,它只是作为主设备,并期望从从设备得到回应。同样,当控制器接收到一消息,它将建立一从设备回应格式并返回给发送的控制器。
查询回应周期
(1)查询
查询消息中的功能代码告之被选中的从设备要执行何种功能。数据段包含了从设备要执行功能的任何附加信息。例如功能代码03是要求从设备读保持寄存器并返回它们的内容。数据段必须包含要告之从设备的信息:从何寄存器开始读及要读的寄存器数量。错误检测域为从设备提供了一种验证消息内容是否正确的方法。
(2)回应
如果从设备产生一正常的回应,在回应消息中的功能代码是在查询消息中的功能代码的回应。数据段包括了从设备收集的数据:像寄存器值或状态。如果有错误发生,功能代码将被修改以用于指出回应消息是错误的,同时数据段包含了描述此错误信息的代码。错误检测域允许主设备确认消息内容是否可用。
稳稳の幸福 发表于 2016-7-23 21:50 | 显示全部楼层
在ModBus系统中有2种传输模式可选择。这2种传输模式与从机PC通信的能力是同等的。选择时应视所用ModBus主机而定,每个ModBus系统只能使用一种模式,不允许2种模式混用。一种模式是ASCII(美国信息交换码),另一种模式是RTU(远程终端设备)。
用户选择想要的模式,包括串口通信参数(波特率、校验方式等),在配置每个控制器的时候,在一个Modbus网络上的所有设备都必须选择相同的传输模式和串口参数。所选的ASCII或RTU方式仅适用于标准的Modbus网络,它定义了在这些网络上连续传输的消息段的每一位,以及决定怎样将信息打包成消息域和如何解码。在其它网络上(像MAP和Modbus Plus)Modbus消息被转成与串行传输无关的帧。

传输模式特性
ASCII可打印字符便于故障检测,而且对于用高级语言(如Fortran)编程的主计算机及主PC很适宜。RTU则适用于机器语言编程的计算机和PC主机。
用RTU模式传输的数据是8位二进制字符。如欲转换为ASCII模式,则每个RTU字符首先应分为高位和低位两部分,这两部分各含4位,然后转换成十六进制等量值。用以构成报文的ASCII字符都是十六进制字符。ASCII模式使用的字符虽是RTU模式的两倍,但ASCII数据的译码和处理更为容易一些,此外,用RTU模式时报文字符必须以连续数据流的形式传送,用ASCII模式,字符之间可产生长达1s的间隔,以适应速度较慢的机器。
控制器能设置为两种传输模式(ASCII或RTU)中的任何一种在标准的Modbus网络通信。

稳稳の幸福 发表于 2016-7-23 21:50 | 显示全部楼层
ASCII模式
当控制器设为在Modbus网络上以ASCII(美国标准信息交换代码)模式通信,一个信息中的每8位字节作为2个ASCII字符传输,如数值63H用ASCII方式时,需发送两个字节,即ASCII“6"(0110110)和ASCII”3“(0110011),ASCII字符占用的位数有7位和8位,国际通用7位为多。这种方式的主要优点是字符发送的时间间隔可达到1秒而不产生错误。
代码系统
· 十六进制,ASCII字符0...9,A...F
· 消息中的每个ASCII字符都是一个十六进制字符组成
每个字节的位
· 1个起始位
· 7个数据位,最小的有效位先发送
· 1个奇偶校验位,无校验则无
1个停止位(有校验时),2个Bit(无校验时)
错误检测域
· LRC(纵向冗长检测)

RTU模式
当控制器设为在Modbus网络上以RTU模式通信,在消息中的每个8Bit字节按照原值传送,不做处理,如63H,RTU将直接发送01100011。这种方式的主要优点是:数据帧传送之间没有间隔,相同波特率下传输数据的密度要比ASCII高,传输速度更快[1]
代码系统
8位二进制,十六进制数0...9,A...F
消息中的每个8位域都是一或两个十六进制字符组成
每个字节的位
1个起始位
8个数据位,最小的有效位先发送
1个奇偶校验位,无校验则无
1个停止位(有校验时),2个Bit(无校验时)




稳稳の幸福 发表于 2016-7-23 21:51 | 显示全部楼层
CRC
CRC域是两个字节,包含一16位的二进制值。它由传输设备计算后加入到消息中。接收设备重新计算收到消息的CRC,并与接收到的CRC域中的值比较,如果两值不同,则有误。
CRC是先调入一值是全“1”的16位寄存器,然后调用一过程将消息中连续的8位字节和当前寄存器中的值进行处理。仅每个字符中的8Bit数据对CRC有效,起始位和停止位以及奇偶校验位均无效。
CRC产生过程中,每个8位字符都单独和寄存器内容相异或(XOR),结果向最低有效位方向移动,最高有效位以0填充。LSB被提取出来检测,如果LSB为1,寄存器单独和预置的值或一下,如果LSB为0,则不进行。整个过程要重复8次。在最后一位(第8位)完成后,下一个8位字节又单独和寄存器的当前值相异或(XOR)。最终寄存器中的值,是消息中所有的字节都执行之后的CRC值。
CRC添加到消息中时,低字节先加入,然后高字节。
CRC-16错误校验程序如下:报文(此处只涉及数据位,不指起始位、停止位和任选的奇偶校验位)被看作是一个连续的二进制,其最高有效位(MSB)首选发送。报文先与X↑16相乘(左移16位),然后看X↑16+X↑15+X↑2+1除,X↑16+X↑15+X↑2+1可以表示为二进制数11000,0000,0000,0101。整数商位忽略不记,16位余数加入该报文(MSB先发送),成为2个CRC校验字节。余数中的1全部初始化,以免所有的零成为一条报文被接收。经上述处理而含有CRC字节的报文,若无错误,到接收设备后再被同一多项式(X↑16+X↑15+X↑2+1)除,会得到一个零余数(接收设备核验这个CRC字节,并将其与被传送的CRC比较)。全部运算以2为模(无进位)。
习惯于成串发送数据的设备会首选送出字符的最右位(LSB-最低有效位)。而在生成CRC情况下,发送首位应是被除数的最高有效位MSB。由于在运算中不用进位,为便于操作起见,计算CRC时设MSB在最右位。生成多项式的位序也必须反过来,以保持一致。多项式的MSB略去不记,因其只对商有影响而不影响余数。
生成CRC-16校验字节的步骤如下:
①装如一个16位寄存器,所有数位均为1。
②该16位寄存器的高位字节与开始8位字节进行“异或”运算。运算结果放入这个16位寄存器。
③把这个16寄存器向右移一位。
④若向右(标记位)移出的数位是1,则生成多项式10,1000,000,0000,001和这个寄存器进行“异或”运算;若向右移出的数位是0,则返回③。
⑤重复③和④,直至移出8位。
⑥另外8位与该十六位寄存器进行“异或”运算。
⑦重复③~⑥,直至该报文所有字节均与16位寄存器进行“异或”运算,并移位8次。
⑧这个16位寄存器的内容即2字节CRC错误校验,被加到报文的最高有效位。
另外,在某些非ModBus通信协议中也经常使用CRC16作为校验手段,而且产生了一些CRC16的变种,他们是使用CRC16多项式X↑16+X↑15+X↑2+1,单首次装入的16位寄存器为0000;使用CRC16的反序X↑16+X↑14+X↑1+1,首次装入寄存器值为0000或FFFFH。

稳稳の幸福 发表于 2016-7-23 21:52 | 显示全部楼层
CRC简单函数如下:
  1. unsignedchar*puchMsg;/*要进行CRC校验的消息*/


  2. unsignedshortusDataLen;/*消息中字节数*/

  3. unsignedshortCRC16(puchMsg,usDataLen)


  4. {


  5. unsignedcharuchCRCHi=0xFF;/*高CRC字节初始化*/


  6. unsignedcharuchCRCLo=0xFF;/*低CRC字节初始化*/


  7. unsigneduIndex;/*CRC循环中的索引*/


  8. while(usDataLen--)/*传输消息缓冲区*/


  9. {

  10. uIndex=uchCRCHi^*puchMsg++;/*计算CRC*/


  11. uchCRCHi=uchCRCLo^auchCRCHi[uIndex];


  12. uchCRCLo=auchCRCLo[uIndex];

  13. }


  14. return((uchCRCHi<<8)|uchCRCLo);

  15. }

  16. /*CRC高位字节值表*/

  17. staticunsignedcharauchCRCHi[]={
  18. 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
  19. 0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
  20. 0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,
  21. 0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
  22. 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,
  23. 0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,
  24. 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,
  25. 0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
  26. 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
  27. 0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,
  28. 0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,
  29. 0x81,0x40,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
  30. 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
  31. 0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,
  32. 0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,
  33. 0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
  34. 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
  35. 0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
  36. 0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,
  37. 0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
  38. 0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x01,0xC0,
  39. 0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,
  40. 0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,
  41. 0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
  42. 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,
  43. 0x80,0x41,0x00,0xC1,0x81,0x40
  44. };

  45. //CRC低位字节值表

  46. staticcharauchCRCLo[]={
  47. 0x00,0xC0,0xC1,0x01,0xC3,0x03,0x02,0xC2,0xC6,0x06,
  48. 0x07,0xC7,0x05,0xC5,0xC4,0x04,0xCC,0x0C,0x0D,0xCD,
  49. 0x0F,0xCF,0xCE,0x0E,0x0A,0xCA,0xCB,0x0B,0xC9,0x09,
  50. 0x08,0xC8,0xD8,0x18,0x19,0xD9,0x1B,0xDB,0xDA,0x1A,
  51. 0x1E,0xDE,0xDF,0x1F,0xDD,0x1D,0x1C,0xDC,0x14,0xD4,
  52. 0xD5,0x15,0xD7,0x17,0x16,0xD6,0xD2,0x12,0x13,0xD3,
  53. 0x11,0xD1,0xD0,0x10,0xF0,0x30,0x31,0xF1,0x33,0xF3,
  54. 0xF2,0x32,0x36,0xF6,0xF7,0x37,0xF5,0x35,0x34,0xF4,
  55. 0x3C,0xFC,0xFD,0x3D,0xFF,0x3F,0x3E,0xFE,0xFA,0x3A,
  56. 0x3B,0xFB,0x39,0xF9,0xF8,0x38,0x28,0xE8,0xE9,0x29,
  57. 0xEB,0x2B,0x2A,0xEA,0xEE,0x2E,0x2F,0xEF,0x2D,0xED,
  58. 0xEC,0x2C,0xE4,0x24,0x25,0xE5,0x27,0xE7,0xE6,0x26,
  59. 0x22,0xE2,0xE3,0x23,0xE1,0x21,0x20,0xE0,0xA0,0x60,
  60. 0x61,0xA1,0x63,0xA3,0xA2,0x62,0x66,0xA6,0xA7,0x67,
  61. 0xA5,0x65,0x64,0xA4,0x6C,0xAC,0xAD,0x6D,0xAF,0x6F,
  62. 0x6E,0xAE,0xAA,0x6A,0x6B,0xAB,0x69,0xA9,0xA8,0x68,
  63. 0x78,0xB8,0xB9,0x79,0xBB,0x7B,0x7A,0xBA,0xBE,0x7E,
  64. 0x7F,0xBF,0x7D,0xBD,0xBC,0x7C,0xB4,0x74,0x75,0xB5,
  65. 0x77,0xB7,0xB6,0x76,0x72,0xB2,0xB3,0x73,0xB1,0x71,
  66. 0x70,0xB0,0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,
  67. 0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54,0x9C,0x5C,
  68. 0x5D,0x9D,0x5F,0x9F,0x9E,0x5E,0x5A,0x9A,0x9B,0x5B,
  69. 0x99,0x59,0x58,0x98,0x88,0x48,0x49,0x89,0x4B,0x8B,
  70. 0x8A,0x4A,0x4E,0x8E,0x8F,0x4F,0x8D,0x4D,0x4C,0x8C,
  71. 0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,
  72. 0x43,0x83,0x41,0x81,0x80,0x40
  73. };


稳稳の幸福 发表于 2016-7-23 21:53 | 显示全部楼层
LRC
LRC错误校验用于ASCII模式。这个错误校验是一个8位二进制数,可作为2个ASCII十六进制字节传送。把十六进制字符转换成二进制,加上无循环进位的二进制字符和二进制补码结果生成LRC错误校验(参见图)。这个LRC在接收设备进行核验,并与被传送的LRC进行比较,冒号(:)、回车符号(CR)、换行字符(LF)和置入的其他任何非ASCII十六进制字符在运算时忽略不计。
稳稳の幸福 发表于 2016-7-23 21:54 | 显示全部楼层
ModBus功能码
01READ COIL STATUS
02READ INPUT STATUS
03READ HOLDING REGISTER
04READ INPUT REGISTER
05WRITE SINGLE COIL
06WRITE SINGLE REGISTER
15WRITE MULTIPLE COIL
16WRITE MULTIPLE REGISTER

zhuomuniao110 发表于 2016-7-24 17:38 | 显示全部楼层
主设备可单独和从设备通信,也能以广播方式和所有从设备通信。
heisexingqisi 发表于 2016-7-24 18:05 | 显示全部楼层
在消息位,Modbus协议仍提供了主—从原则,尽管网络通信方法是“对等”
玛尼玛尼哄 发表于 2016-7-24 21:40 | 显示全部楼层
感觉这个很复杂啊,也用的不多,不过工业上用的很多。
zhuotuzi 发表于 2016-7-24 22:35 | 显示全部楼层
如果从设备产生一正常的回应,在回应消息中的功能代码是在查询消息中的功能代码的回应
598330983 发表于 2016-7-25 21:53 | 显示全部楼层
CRC校验需要那么多数组,看来一般别用的好。
zhuomuniao110 发表于 2016-7-26 19:52 | 显示全部楼层
,控制器使用对等技术通信,故任何控制器都能初始化和其它控制器的通信。
heisexingqisi 发表于 2016-7-27 12:01 | 显示全部楼层
ASCII可打印字符便于故障检测,而且对于用高级语言(如Fortran)编程的主计算机及主PC很适宜。RTU则适用于机器语言编程的计算机和PC主机。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

230

主题

3676

帖子

10

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