本次的通信对象是西门子S7-1500。一、新建数据类型打开博途新建1500项目CPU选1513-1PN新建数据类型命名为AreaVar这个命名为AreaVar的数据类型包含了数据分区I/QM/DB)、DB编号、数据起点、数据字节长度等信息可用来进行各种以字节为单位的块间操作。二、新建FC块命名为create_up_msg用来组态上行报文接口定义Input 数据类型 备注frameHeader1 Byte 帧头字节1frameHeader2 Byte 帧头字节2srcAreaVar AreaVar 区域变量该区域变量所指区域内的字节就是上行数据的有效字节frameEnd1 Byte 帧尾字节1frameEnd2 Byte 帧尾字节2DB_CRC_TABLE Array[0..255] of Word CRC查表运算的表格InOutdestArea AreaVar 目标区域变量组态好的字节存放在这里用以发送TempTemp_1 AreaVarwCRC Wordi DIntbTemp BytewTemp WordnIndex DIntscl////////////////////////////组态帧头/////////////////////////// POKE(area : #destArea.Area, dbNumber : #destArea.DBNum, byteOffset : 0, value : #frameHeader1); //帧头1 POKE(area : #destArea.Area, dbNumber : #destArea.DBNum, byteOffset : 1, value : #frameHeader2); //帧头2 ////////组态区域变量的信息本帧数据的区域和起点、长度////// POKE(area : #destArea.Area, //本帧数据的区域 dbNumber : #destArea.DBNum, byteOffset : 2, value : #srcAreaVar.Area); POKE(area : #destArea.Area, //字节占位 dbNumber : #destArea.DBNum, byteOffset : 3, value : #srcAreaVar.Area); tmp_MDint : #srcAreaVar.DBNum; //tmp_MDintMD1000 POKE_BLK(area_src : 16#83, //DB编号 dbNumber_src : 0, byteOffset_src : 1000, area_dest : #destArea.Area, dbNumber_dest : #destArea.DBNum, byteOffset_dest : 4, count : 4); tmp_MDint : #srcAreaVar.Offset; //tmp_MDintMD1000 POKE_BLK(area_src : 16#83, //地址偏移 dbNumber_src : 0, byteOffset_src : 1000, area_dest : #destArea.Area, dbNumber_dest : #destArea.DBNum, byteOffset_dest : 8, count : 4); tmp_MDint : #srcAreaVar.byteCount; //tmp_MDintMD1000 POKE_BLK(area_src : 16#83, //变量字节数 dbNumber_src : 0, byteOffset_src : 1000, area_dest : #destArea.Area, dbNumber_dest : #destArea.DBNum, byteOffset_dest : 12, count : 4); ///////////////////////////////组态帧数据内容///////////////////////// POKE_BLK(area_src : #srcAreaVar.Area, dbNumber_src : #srcAreaVar.DBNum, byteOffset_src : #srcAreaVar.Offset, area_dest : #destArea.Area, dbNumber_dest : #destArea.DBNum, byteOffset_dest : 16, count : #srcAreaVar.byteCount); ///////////////////////////////组态帧尾//////////////////////////// POKE(area : #destArea.Area, dbNumber : #destArea.DBNum, byteOffset : 16 #srcAreaVar.byteCount, value : #frameEnd1); //帧尾1 POKE(area : #destArea.Area, dbNumber : #destArea.DBNum, byteOffset : 17 #srcAreaVar.byteCount, value : #frameEnd2); //帧尾2 /////////////////////////////////CRC16校验和////////////////////////////// #destArea.byteCount : 18 #srcAreaVar.byteCount; #wCRC : 16#FFFF; FOR #i : 0 TO #destArea.byteCount-1 DO #bTemp : PEEK(area : #destArea.Area, byteOffset : #i, dbNumber : #destArea.DBNum); #wTemp : #wCRC XOR BYTE_TO_WORD(IN : #bTemp); #wTemp : #wTemp AND 16#00FF; #nIndex : WORD_TO_INT(IN : #wTemp); #wCRC : SHR(IN : #wCRC, N : 8) XOR #DB_CRC_TABLE[#nIndex]; END_FOR; //写入CRC字 POKE(area : #destArea.Area, dbNumber : #destArea.DBNum, byteOffset : 18 #srcAreaVar.byteCount, value : #wCRC); #destArea.byteCount : 20 #destArea.byteCount;完整的上行报文帧结构帧头1 帧头2 本次数据的区域参数分区、DB号、字节偏移、字节长度 数据字节块 帧尾1 帧尾2 CRC字三、CRC的查表表格新建全局DB块命名为DB_CRC_TABLE数据类型为Array[0..255] of WordArray的内容为这个是CRC16的查表运算映射表[ 16#0000,16#C0C1,16#C181,16#0140,16#C301,16#03C0,16#0280,16#C241, 16#C601,16#06C0,16#0780,16#C741,16#0500,16#C5C1,16#C481,16#0440, 16#CC01,16#0CC0,16#0D80,16#CD41,16#0F00,16#CFC1,16#CE81,16#0E40, 16#0A00,16#CAC1,16#CB81,16#0B40,16#C901,16#09C0,16#0880,16#C841, 16#D801,16#18C0,16#1980,16#D941,16#1B00,16#DBC1,16#DA81,16#1A40, 16#1E00,16#DEC1,16#DF81,16#1F40,16#DD01,16#1DC0,16#1C80,16#DC41, 16#1400,16#D4C1,16#D581,16#1540,16#D701,16#17C0,16#1680,16#D641, 16#D201,16#12C0,16#1380,16#D341,16#1100,16#D1C1,16#D081,16#1040, 16#F001,16#30C0,16#3180,16#F141,16#3300,16#F3C1,16#F281,16#3240, 16#3600,16#F6C1,16#F781,16#3740,16#F501,16#35C0,16#3480,16#F441, 16#3C00,16#FCC1,16#FD81,16#3D40,16#FF01,16#3FC0,16#3E80,16#FE41, 16#FA01,16#3AC0,16#3B80,16#FB41,16#3900,16#F9C1,16#F881,16#3840, 16#2800,16#E8C1,16#E981,16#2940,16#EB01,16#2BC0,16#2A80,16#EA41, 16#EE01,16#2EC0,16#2F80,16#EF41,16#2D00,16#EDC1,16#EC81,16#2C40, 16#E401,16#24C0,16#2580,16#E541,16#2700,16#E7C1,16#E681,16#2640, 16#2200,16#E2C1,16#E381,16#2340,16#E101,16#21C0,16#2080,16#E041, 16#A001,16#60C0,16#6180,16#A141,16#6300,16#A3C1,16#A281,16#6240, 16#6600,16#A6C1,16#A781,16#6740,16#A501,16#65C0,16#6480,16#A441, 16#6C00,16#ACC1,16#AD81,16#6D40,16#AF01,16#6FC0,16#6E80,16#AE41, 16#AA01,16#6AC0,16#6B80,16#AB41,16#6900,16#A9C1,16#A881,16#6840, 16#7800,16#B8C1,16#B981,16#7940,16#BB01,16#7BC0,16#7A80,16#BA41, 16#BE01,16#7EC0,16#7F80,16#BF41,16#7D00,16#BDC1,16#BC81,16#7C40, 16#B401,16#74C0,16#7580,16#B541,16#7700,16#B7C1,16#B681,16#7640, 16#7200,16#B2C1,16#B381,16#7340,16#B101,16#71C0,16#7080,16#B041, 16#5000,16#90C1,16#9181,16#5140,16#9301,16#53C0,16#5280,16#9241, 16#9601,16#56C0,16#5780,16#9741,16#5500,16#95C1,16#9481,16#5440, 16#9C01,16#5CC0,16#5D80,16#9D41,16#5F00,16#9FC1,16#9E81,16#5E40, 16#5A00,16#9AC1,16#9B81,16#5B40,16#9901,16#59C0,16#5880,16#9841, 16#8801,16#48C0,16#4980,16#8941,16#4B00,16#8BC1,16#8A81,16#4A40, 16#4E00,16#8EC1,16#8F81,16#4F40,16#8D01,16#4DC0,16#4C80,16#8C41, 16#4400,16#84C1,16#8581,16#4540,16#8701,16#47C0,16#4680,16#8641, 16#8201,16#42C0,16#4380,16#8341,16#4100,16#81C1,16#8081,16#4040 ]四、简单验证一下CRC的准确性创建源数据块src_DBDB1DB内为混合类型的数据内容如下创建一个数据类型为Array[0..100] of Byte的DB块DB5命名为bytes_DB用来存放上行报文PLC程序的OB11、定义两个tmp区域变量TMP_AreaVar1、TMP_AreaVar2先组态5个字节数据测试一下编程OB1:程序段1scl#TMP_AreaVar1.Area : 16#84; //DB区 #TMP_AreaVar1.DBNum : 1; //DB号 #TMP_AreaVar1.Offset : 0; //字节偏移 #TMP_AreaVar1.byteCount : 5; //长度 #TMP_AreaVar2.Area : 16#84; #TMP_AreaVar2.DBNum : 5; #TMP_AreaVar2.Offset : 0; #TMP_AreaVar2.byteCount : 5;程序段2下载并运行程序监控DB5的数据组态好的上行报文数据解析pythonimport crcmod crc16 crcmod.predefined.Crc(modbus) # 测试数据和 PLC 一样 data bytes([0x41,0x41,0x84,0x84,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x11,0x22,0x33,0x44,0x55,0x42,0x42]) # 计算 CRC crc16.update(data) result crc16.crcValue # 16 位结果 print(fPython CRC: {result:04X})运行截图PLC内和python的CRC运算结果相符。