Modbus CRC校验程序
- 格式:doc
- 大小:14.22 KB
- 文档页数:6
modbus crc校验算法
Modbus CRC校验算法用于检查Modbus通信中数据的完整性。
CRC校验算法基于循环冗余校验码(CRC)。
以下是Modbus CRC校验算法的步骤:
1. 初始化一个CRC寄存器(一般为16位,初始值为0xFFFF)和一个多项式寄存器(用于执行位移和异或操作)。
2. 对要发送的数据(包括请求或响应报文)的每个字节执行以下步骤:
a. 将CRC寄存器和数据字节进行位移(右移8位)。
b. 将位移后的CRC寄存器与多项式寄存器进行异或操作。
c. 将异或操作结果反复执行,在每次操作后进行位移,直到处理完所有位。
3. 返回最终处理后的CRC寄存器的值作为校验码。
这样,接收端可以使用同样的算法对接收到的数据执行CRC校验,然后将计算得到的校验码与接收到的校验码进行比较,以判断数据的完整性。
注意:Modbus协议中的CRC校验算法可能会因实现方式或版本而有所不同,因此在实际应用中应根据具体情况进行适当的调整。
以上提供的是一种常见的Modbus CRC校验算法实现。
MODBUS的CRC校验和程序modbu协议做为一种通用协议得到了广泛的应用,它有两种传输模式:ASCII和RTU。
ASCII模式采用LRC校验,RTU模式采用CRC校验。
CRC方法错误检测域的内容是通过对消息内容进行循环冗长检测方法得出的。
使用RTU模式,消息包括了一基于CRC方法的错误检测域。
CRC域检测了整个消息的内容。
CRC域是两个字节,包含一16位的二进制值。
它由传输设备计算后加入到消息中。
接收设备重新计算收到消息的CRC,并与接收到的CRC域中的值比较,如果两值不同,则有误。
CRC是先调入一值是全“1”的16位寄存器,然后调用一过程将消息中连续的8位字节各当前寄存器中的值进行处理。
仅每个字符中的8Bit数据对CRC有效,起始位和停止位以及奇偶校验位均无效。
CRC产生过程中,每个8位字符都单独和寄存器内容相或(OR),结果向最低有效位方向移动,最高有效位以0填充。
LSB被提取出来检测,如果LSB为1,寄存器单独和预置的值或一下,如果LSB为0,则不进行。
整个过程要重复8次。
在最后一位(第8位)完成后,下一个8位字节又单独和寄存器的当前值相或。
最终寄存器中的值,是消息中所有的字节都执行之后的CRC值。
CRC域附加在消息的最后,添加时先是低字节然后是高字节。
故CRC的高位字节是发送消息的最后一个字节。
下面是用VB实现的CRC校验和程序:FunctionCRC16(data()AByte)AString’CRC计算函数DimCRC16LoAByte,CRC16HiAByte’CRC寄存器DimCLAByte,CHAByte’多项式码&HA001DimSaveHiAByte,SaveLoAByteDimIAIntegerDimFlagAIntegerCRC16 Lo=&HFFCRC16Hi=&HFFCL=&H1CH=&HA0ForI=0ToUBound(data)CRC16Lo=CRC16Lo某ordata(I)’每一个数据与CRC寄存器进行异或ForFlag=0To7SaveHi=CRC16HiSaveLo=CRC16LoCRC16Hi=CRC16Hi\\2’高位右移一位CRC16Lo=CRC16Lo\\2’低位右移一位If((SaveHiAnd&H1)=&H1)Then’如果高位字节最后一位为1CRC16Lo=CRC16LoOr&H80’则低位字节右移后前面补1EndIf’否则自动补0If((SaveLoAnd&H1)=&H1)Then’如果LSB为1,则与多项式码进行异或CRC16Hi=CRC16Hi某orCHCRC16Lo=CRC16Lo某orCLEndIfNe某tFlagNe某tIDimReturnData(1)AByteReturnData(0)=CRC16Hi’CRC高位ReturnData(1)=CRC16Lo’CRC低位ad=Right(\EndFunction多线程串行通讯VB源码OptionE某plicitPrivateWithEventoTet1ATetE某e.clTetPrivateWithEventoTet2ATetE某e.clTetPrivateDeclareFunctionGetTickCountLib\Dimmi,iAIntegerDoEventLoopUntilGetTickCount()-tt>tEndSubSetoTet1=NewTetE某e.clTetoTet1.lMilliec=100oTet1.StartSub(1000)SetoTet2=NewTetE某e.clTetoTet2.lMilliec=100oTet2.StartSub(1000)SetoTet1=NothingSetoTet2=NothingEndSubPrivateSubForm_Unload(CancelAInteger)oTet1.StopSuboTet2.Stop SubSetoTet1=NothingSetoTet2=NothingEndSubPrivateSuboTet1_Progre(ByVallProgreALong)Lit1.AddItemlProgre Lit1.LitInde某=Lit1.LitCount-1EndSubPrivateSuboTet2_Progre(ByVallProgreALong)recieve_meageLit2.AddItemlProgreLit2.LitInde某=Lit2.LitCount-1EndSubPrivateSubend_meage()MgBo某\通讯错误,请确认线路是否连接\错误\Ele'Label1.Caption=\开始运行\EndIfPrivateSubent_mg_Click()end_meageEndSubPrivateSubhow_mg_Click()recieve_meageEndSub如何用VB实现Modbu串行通讯在一些应用中可能需要使用诸如VB来进行上位机监控程序的开发,而Modbu协议是这类应用中首选的通讯协议;Modbu协议以其简单易用,在工业领域里已广泛的为其他第三方设备所支持。
python⽣成crc校验⽣成modbus16位校验码CRC16 Modbus计算原理1. 预置 1 个 16 位的寄存器为⼗六进制FFFF(即全为 1) , 称此寄存器为 CRC寄存器。
2. 把第⼀个 8 位⼆进制数据 (通信信息帧的第⼀个字节) 与 16 位的 CRC寄存器的低 8 位相异或, 把结果放于 CRC寄存器。
3. 把 CRC 寄存器的内容右移⼀位( 朝低位)⽤ 0 填补最⾼位, 并检查右移后的移出位。
4. 如果移出位为 0, 重复第 3 步 ( 再次右移⼀位); 如果移出位为 1, CRC 寄存器与多项式A001 ( 1010 0000 0000 0001) 进⾏异或。
5. 重复步骤 3 和步骤 4, 直到右移 8 次,这样整个8位数据全部进⾏了处理。
6. 重复步骤 2 到步骤 5, 进⾏通信信息帧下⼀个字节的处理。
7. 将该通信信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的⾼、低字节进⾏交换。
8. 最后得到的 CRC寄存器内容即为 CRC码。
python代码实现def one_byte_crc(data, crc_data):"""处理⼀个字节的crc校验环节:param data:待处理的数据:param crc_data:crc寄存器值,最初始的为0xffff:return:"""# 把第⼀个8位⼆进制数据(通信信息帧的第⼀个字节)与16位的CRC寄存器的低8位相异或,把结果放于CRC寄存器。
crc_data_tmp1 = (get_crc_low(crc_data) ^ data) + (0xff00 & crc_data)length = 8while True:# 把CRC寄存器的内容右移⼀位(朝低位)⽤0填补最⾼位,并检查右移后的移出位。
# 如果移出位为0,重复第3步(再次右移⼀位);如果移出位为1,CRC寄存器与多项式A001(1010000000000001)进⾏异或。
博图TIA中ModbusRTU_CRC校验程序的实现博图TIA中ModbusRTU_CRC校验程序的实现使⽤SCL语⾔,在博图TIA中编写ModbusRTU_CRC校验程序,使⽤两个FC块,实现两种不同的应⽤CRC1将计算结果直接输出,CRC2将计算的结果插⼊到输⼊数组的最后端. TIA中⾃带了modbusRTU通讯库,之所以⾃⼰实现CRC校验码的计算只是为了更深⼊的学习TIA SCL编程序.实现效果及代码截图代码⽚段CRC1FUNCTION "CRC1" : Void{ S7_Optimized_Access := 'TRUE' }VERSION : 0.1VAR_INPUTCrcData : Variant;END_VARVAR_OUTPUTCrcValue : Word;CrcErr : Word;END_VARVAR_TEMPPreset : Word;LoopLength : Int;ArrayPoint : Int;i : Int;ArrayLength : UDInt;Array1 : Array[0..999] of Byte; // 最多1000个字节Err : Int;END_VARBEGIN#ArrayLength:= CountOfElements(#CrcData);IF #ArrayLength <= 1000 THEN //这⾥的1000如果需要调⼤,对应的数组临时变量Array1也要调⼤#Err := MOVE_BLK_VARIANT(SRC := #CrcData, COUNT := #ArrayLength, SRC_INDEX := 0, DEST_INDEX := 0, DEST => #Array1);#Preset := 16#FFFF;#LoopLength := 0;#ArrayPoint := 0;//计算CRC校验码WHILE #LoopLength < #ArrayLength DO //数据长度#Preset := #Preset XOR #Array1[#ArrayPoint];#ArrayPoint := #ArrayPoint + 1;FOR #i := 0 TO 7 DOIF (#Preset AND 16#01) = 16#01 THEN#Preset := SHR(IN := #Preset, N := 1);#Preset := #Preset XOR 16#A001;ELSE#Preset := SHR(IN := #Preset, N := 1);END_IF;END_FOR;#LoopLength := #LoopLength + 1;END_WHILE;//#CrcValue := #Preset;#CrcValue := SHR_WORD(IN := #Preset, N := 8) + SHL_WORD(IN := #Preset, N := 8);#CrcErr := 16#0000;ELSE#CrcErr := 16#8000;END_IF;END_FUNCTIONSEND1DATA_BLOCK "SEND1"{ S7_Optimized_Access := 'FALSE' }VERSION : 0.1NON_RETAINSTRUCTCrcData : Array[0..5] of Byte; // 该数组不⼤于1000字节CrcValue : Word;CrcError : Word;END_STRUCT;BEGINCrcData[0] := 16#01;CrcData[1] := 16#03;CrcData[2] := 16#00;CrcData[3] := 16#00;CrcData[4] := 16#00;CrcData[5] := 16#01;END_DATA_BLOCKCRC2FUNCTION "CRC2" : Void{ S7_Optimized_Access := 'TRUE' }VERSION : 0.1VAR_INPUTCommand : Variant;dataLen : Int;END_VARVAR_TEMPbuffer : Array[0..#MaxLen] of Byte;i : Int;j : Int;CrcReg : Word;Len : Int;END_VARVAR CONSTANTMaxLen : Int := 255;END_VARBEGINIF #dataLen = 0 OR #dataLen > CountOfElements(IN := #Command) - 2 THEN RETURN;ELSE#Len := #dataLen;END_IF;#CrcReg := 16#FFFF;//将数据转到缓冲区VariantGet(SRC:=#Command,DST=>#buffer);//计算CRC校验码FOR #i := 0 TO (#Len - 1) DO#CrcReg := #CrcReg XOR #buffer[#i];FOR #j := 0 TO 7 DOIF (#CrcReg AND 16#1) = 1 THEN#CrcReg := SHR_WORD(IN := #CrcReg, N := 1);#CrcReg := #CrcReg XOR 16#A001;ELSE#CrcReg := SHR_WORD(IN := #CrcReg, N := 1);END_IF;END_FOR;END_FOR;#buffer[#Len + 1] := SHR_WORD(IN := #CrcReg, N := 8);#buffer[#Len] := #CrcReg AND 16#FF;//将缓冲区数据再写⼊到指针所指向的区域VariantPut(SRC := #buffer,DST := #Command);END_FUNCTIONSEND2DATA_BLOCK "SEND2"{ S7_Optimized_Access := 'FALSE' }VERSION : 0.1NON_RETAINSTRUCTCrcData : Array[0..7] of Byte; // 该数组不⼤于1000字节END_STRUCT;BEGINCrcData[0] := 16#01;CrcData[1] := 16#03;CrcData[2] := 16#00;CrcData[3] := 16#00;CrcData[4] := 16#00;CrcData[5] := 16#01;END_DATA_BLOCK。
Modbus通信协议详解【附C语⾔CRC程序】MODBUS通讯协议及编程【⼀】⼀、Modbus 协议简介 Modbus 协议是应⽤于电⼦控制器上的⼀种通⽤语⾔。
通过此协议,控制器相互之间、控制器经由⽹络(例如以太⽹)和其它设备之间可以通信。
它已经成为⼀通⽤⼯业标准。
有了它,不同⼚商⽣产的控制设备可以连成⼯业⽹络,进⾏集中监控。
此协议定义了⼀个控制器能认识使⽤的消息结构,⽽不管它们是经过何种⽹络进⾏通信的。
它描述了⼀控制器请求访问其它设备的过程,如果回应来⾃其它设备的请求,以及怎样侦测错误并记录。
它制定了消息域格局和内容的公共格式。
当在⼀Modbus⽹络上通信时,此协议决定了每个控制器须要知道它们的设备地址,识别按地址发来的消息,决定要产⽣何种⾏动。
如果需要回应,控制器将⽣成反馈信息并⽤Modbus协议发出。
在其它⽹络上,包含了Modbus协议的消息转换为在此⽹络上使⽤的帧或包结构。
这种转换也扩展了根据具体的⽹络解决节地址、路由路径及错误检测的⽅法。
1、在Modbus⽹络上转输 标准的Modbus⼝是使⽤⼀RS-232C兼容串⾏接⼝,它定义了连接⼝的针脚、电缆、信号位、传输波特率、奇偶校验。
控制器能直接或经由Modem组⽹。
控制器通信使⽤主—从技术,即仅⼀设备(主设备)能初始化传输(查询)。
其它设备(从设备)根据主设备查询提供的数据作出相应反应。
典型的主设备:主机和可编程仪表。
典型的从设备:可编程控制器。
主设备可单独和从设备通信,也能以⼴播⽅式和所有从设备通信。
如果单独通信,从设备返回⼀消息作为回应,如果是以⼴播⽅式查询的,则不作任何回应。
Modbus协议建⽴了主设备查询的格式:设备(或⼴播)地址、功能代码、所有要发送的数据、⼀错误检测域。
从设备回应消息也由Modbus协议构成,包括确认要⾏动的域、任何要返回的数据、和⼀错误检测域。
如果在消息接收过程中发⽣⼀错误,或从设备不能执⾏其命令,从设备将建⽴⼀错误消息并把它作为回应发送出去。
modbus的CRC校验程序const unsigned char code auchCRCHi[] = {0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40};const unsigned char code auchCRCLo[] = {0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,0x43, 0x83, 0x41, 0x81, 0x80, 0x40};/////////////////////////////////////////////////////////////函数名:Modbus_crc16//功能: modbus代码校验//参数:字符型指针或数组首地址,数据个数//返回值: crc数据//备注:在收到和发送的过程中都需要调用。
总线论坛-Fieldbus首页»现场总线和工业以太网技术»Modbus»MODBUS协议的CRC校验子程序代码,包括VB,VC和asm51汇编lidan - 2007-4-19 7:37:00MODBUS协议规定:上位机发送8个字节召测指令,其中地址、设备类别、通信路由、指令类别、指令长度、指令各占1个字节,CRC校验码占2个字节;下位机应答7个字节,地址、设备类别、数据长度各占1个字节,数据、CRC校验码各占2个字节。
MODBUS协议规定CRC校验规则:CRC初始化为&HFF(CRC_L=&HFF,CRC_H=&HFF),将CRC_L与传输的第一个字节进行异或运算,然后将CRC进行右移(不循环)并判断:如移出的位为1,则CRC再与&HA001进行一次异或运算;如移出的位为0,则CRC不变。
如此右移8次即完成第一个字节的校验,重复上述运算及右移直至将全部字节校验完毕,所生成的CRC(16位)即为传输校验码。
为方便读者使用MODBUS协议,将VC、VB、ASM51环境下MODBUS协议的CRC校验子程序代码一并给出,供读者参考。
VC源代码CODE://***CRC Calculation for MODBUS Protocol for VC ***////数组snd为地址等传输字节,num为字节数,发为6收为5//unsigned int mb_crc(BYTE *snd,int num){ int i,j;unsigned int c,crc=0xFFfor (i=0;i<num;i ){ c=str[i] 0x00FF;crc^=c;for(j=0,j<8,j ){ if (crc 0x0001){crc>>=1;crc^=0xA001;}else crc>>=1}}return(crc);}VB源代码CODE:' //***CRC Calculation for MODBUS Protocol for VB***// Function mb_crc(ByRef snd() as BYTE,num as integer) as Long crc_l=crc_h=HFFfor i=1 to numcrc_l=crc_l XOR snd(i)for j=1 to 8if crc_l AND 1 thencrc_l=(crc_l-1)/2if crc_h and 1 thencrc_l=crc_l 128crc_h=(crc_h-1)/2end ifcrc_l=crc_l XOR HA0crc_h=crc_h XOR H01else:crc_l=crc_l/2if crc_h and 1 thencrcl_l=crc_l 128crc_h=(crc_h-1)/2else:crc_h=crc_h/2end ifend ifnext jnext imb_crc=crc_l crc_h*256End FunctionASM51源代码CODE:;CRC Calculation for MODBUS Protocol for ASM51 ;R1为发送(接收)字节的缓存首地址;R2为发送(接收)字节的字节数(不含CRC字节),;R3为CRC校验低位字节,;R4为CRC校验高位字节,CRC:MOV A,#0FFHMOV R4,AMOV R3,ACRC1: MOV A,@R1XRL A,R3MOV R3,AMOV R2,#08HCRC8: CLR CMOV A,R4RRC AMOV R4,AMOV A,R3RRC AMOV R3,AJNC CRC10MOV A,R3XRL A,#01HMOV R3,AMOV A,R4XRL A,#0A0HMOV R4,ACRC10: DJNZ R2,CRC8 INC R1DJNZ CRC1RET。
最详细易懂的CRC-16校验原理(附源程序)标签:crc校验计算方法详细易懂源程序2010-10-29 09:39阅读(8048)评论(0)最详细易懂的CRC-16校验原理(附源程序)1、循环校验码(CRC码):是数据通信领域中最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。
2、生成CRC码的基本原理:任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和‘1’取值的多项式一一对应。
例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为x5+x3+x2+x+1对应的代码101111。
标准CRC生成多项式如下表:名称生成多项式简记式* 标准引用CRC-4 x4+x+1 3 ITU G.704CRC-8 x8+x5+x4+1 0x31CRC-8 x8+x2+x1+1 0x07CRC-8 x8+x6+x4+x3+x2+x1 0x5ECRC-12 x12+x11+x3+x+1 80FCRC-16 x16+x15+x2+1 8005 IBM SDLCCRC16-CCITT x16+x12+x5+1 1021 ISO HDLC, ITU X.25, V.34/V.41/V.42, PPP-FCSCRC-32 x32+x26+x23+...+x2+x+1 04C11DB7 ZIP, RAR, IEEE 802 LAN/FDDI, IEEE 1394, PPP-FCSCRC-32c x32+x28+x27+...+x8+x6+1 1EDC6F41 SCTP3、CRC-16校验码的使用:现选择最常用的CRC-16校验,说明它的使用方法。
根据Modbus协议,常规485通讯的信息发送形式如下:地址功能码数据信息校验码1byte 1byte nbyte 2byteCRC校验是前面几段数据内容的校验值,为一个16位数据,发送时,低8位在前,高8为最后。
例如:信息字段代码为: 1011001,校验字段为:1010。
文章标题:深入探讨Modbus标准CRC校验算法在工业自动化领域,Modbus通信协议是一种应用广泛的串行通信协议,其中的CRC校验算法对于数据完整性的验证起着至关重要的作用。
本文将围绕Modbus标准CRC校验算法展开深入探讨,以便读者能够更全面地了解该算法的原理和应用。
1. 什么是Modbus通信协议?Modbus是一种用于工业领域的通信协议,它通常用于连接各种自动化设备和控制系统,如PLC(可编程逻辑控制器)、传感器、执行器等。
Modbus协议可以通过串行通信或以太网进行数据传输,实现设备之间的数据交换和控制命令的传输。
2. CRC校验算法的作用及原理CRC(Cyclic Redundancy Check)是循环冗余校验的缩写,是一种通过对数据进行计算并附加校验值来验证数据完整性的算法。
在Modbus通信中,CRC校验算法被用于检测通信数据在传输过程中是否发生了误码或数据损坏。
Modbus标准CRC校验算法采用的是CRC-16-Modbus算法,它基于多项式计算和位操作来生成16位的校验码。
在发送端,数据帧的CRC校验码由发送设备计算并附加在数据帧的末尾;在接收端,接收设备也对接收到的数据帧进行CRC校验,来验证接收到的数据是否完整和正确。
3. Modbus标准CRC校验算法的具体计算方法Modbus标准CRC校验算法的计算方法比较复杂,需要对数据帧进行按位运算和多项式计算。
需要对每个字节的数据进行移位和异或运算,然后再进行一系列的位操作和多项式运算,最终得到16位的CRC校验码并附加在数据帧的末尾。
4. Modbus标准CRC校验算法的应用场景Modbus标准CRC校验算法广泛应用于工业自动化领域的数据通信和控制系统中。
它能够有效地验证数据传输的完整性和准确性,确保通信数据的可靠性和稳定性。
在 PLC、传感器、执行器等设备之间的数据交换和控制指令传输中,CRC校验算法能够及时发现并纠正数据错误,保障系统的正常运行。
CRC16 Modbus 校验计算方法 Python 实现本文介绍了 CRC16 Modbus 校验计算方法在 Python 语言中的实现过程,旨在为从事相关领域的开发人员提供参考。
CRC16 Modbus 校验计算方法,是一种在 Modbus 通信协议中使用的循环冗余校验(CRC)算法。
在 Python 中,我们可以通过编写一个简单的函数来实现 CRC16 Modbus 校验计算方法。
以下是具体的实现步骤:```pythondef crc16_modbus(data):"""按照 CRC16 Modbus 校验计算方法,对输入数据进行校验计算 """crc = 0x0000for i in range(0, len(data), 2):crc = crc ^ data[i]for j in range(8):if (crc & 0x8000):crc = (crc << 1) ^ 0x0001else:crc = crc << 1return crc```上述代码定义了一个名为`crc16_modbus`的函数,该函数接收一个字节串`data`作为输入参数,按照 CRC16 Modbus 校验计算方法对数据进行校验计算,并返回校验结果。
在函数内部,我们首先将 CRC 值初始化为 0x0000。
然后,通过一个双重循环,将输入数据中的每个字节进行异或运算,并将结果累加到 CRC 值中。
接下来,我们对 CRC 值进行 8 次右移操作,并在每次右移后检查 CRC 值是否包含最高位的 1。
如果包含,则将 CRC 值异或上一个奇数数(0x0001);否则,直接进行右移操作。
最后,函数返回计算得到的 CRC 值。
在实际使用中,我们可以将输入数据和计算得到的 CRC 值进行异或运算,以验证数据是否发生变化。
总之,通过上述 Python 代码实现,我们可以在 Python 语言中实现 CRC16 Modbus 校验计算方法。
CRC 的生成循环冗余校验(CRC) 域为两个字节,包含一个二进制16 位值。
附加在报文后面的CRC 的值由发送设备计算。
接收设备在接收报文时重新计算CRC 的值,并将计算结果于实际接收到的CRC值相比较。
如果两个值不相等,则为错误。
CRC 的计算, 开始对一个16位寄存器预装全1. 然后将报文中的连续的8位子节对其进行后续的计算。
只有字符中的8个数据位参与生成CRC 的运算,起始位,停止位和校验位不参与CRC 计算。
CRC 的生成过程中,每个8–位字符与寄存器中的值异或。
然后结果向最低有效位(LSB) 方向移动(Shift) 1位,而最高有效位(MSB) 位置充零。
然后提取并检查LSB:如果LSB 为1,则寄存器中的值与一个固定的预置值异或;如果LSB 为0,则不进行异或操作。
这个过程将重复直到执行完8 次移位。
完成最后一次(第8 次)移位及相关操作后,下一个8位字节与寄存器的当前值异或,然后又同上面描述过的一样重复8 次。
当所有报文中子节都运算之后得到的寄存器中的最终值,就是CRC.生成CRC 的过程为:1. 将一个16 位寄存器装入十六进制FFFF (全1). 将之称作CRC 寄存器.2. 将报文的第一个8位字节与16 位CRC 寄存器的低字节异或,结果置于CRC 寄存器.3. 将CRC 寄存器右移1位(向LSB 方向),MSB 充零. 提取并检测LSB.4. (如果LSB 为0): 重复步骤3 (另一次移位).(如果LSB 为1): 对CRC 寄存器异或多项式值0xA001 (1010 0000 0000 0001).5. 重复步骤3 和4,直到完成8 次移位。
当做完此操作后,将完成对8位字节的完整操作。
6. 对报文中的下一个字节重复步骤2 到5,继续此操作直至所有报文被处理完毕。
7. CRC 寄存器中的最终内容为CRC 值.8. 当放置CRC 值于报文时,如下面描述的那样,高低字节必须交换。
Modbus CRC校验程序基本在网上对于MODBUS 的CRC校验程序都能找到两个版本,一个是直接运算的,一个是查表法的。
首先来看一下直接运算的算法运算步骤如下:步驟1:令16-bit 寄存器(CRC 暫存器) = FFFFH.步驟2:Exclusive OR 第一個8-bit byte 的訊息指令與低位元16-bit CRC 寄存器, 做Exclusive OR ,將結果存入CRC 寄存器內。
步驟3:又移一位CRC 寄存器,將0 填入高位處。
步驟4:檢查右移的值,如果是0, 將步驟3 的新值存入CRC 寄存器內, 否則ExclusiveOR A001H 與CRC 寄存器,將結果存入CRC 寄存器內。
步驟5:重複步驟3~步驟4,將8-bit 全部運算完成。
步驟6:重複步驟2~步驟5,取下一個8-bit 的訊息指令,直到所有訊息指令運算完成。
最後,得到的CRC 寄存器的值,即是CRC 的檢查碼。
值得注意的是CRC 的檢查碼必須交換放置於訊息指令的檢查碼中。
网上能找到的基本代码如下[cpp] view plaincopyprint?uint16 CRC16_Check(uint8*Pushdata,uint8 length) { uint16 Reg_CRC=0xffff; uint8 Temp_reg=0x00; uint8 i,j; for( i = 0;i<length; i ++) { Reg_CRC^= *Pushdata++;for (j = 0; j<8; j++) { if (Reg_CRC &0x0001) Reg_CRC=Reg_CRC>>1^0xA001; else Reg_CRC >>=1; } } return (Reg_CRC); } 基本算法是这个意思来着的,但答案其实是错的,校验没错为何说错呢,这里程序中的少了一句:值得注意的是CRC 的檢查碼必須交換放置於訊息指令的檢查碼中意思就是高字节地位输出,低字节高位输出。
翻阅GOOGLE、百度,发现什么样式的都有,直接高字节高位输出的有,高字节低位输出的也有。
究竟哪个才是对的呢,经翻阅《GBT19582.1-2008基于Modbus协议的工业自动化网络规范第二部》中规范中crc 占2个字节,低字节在数据流的倒数第二个字节,而高字节在数据流最末端,即应正了高字节低位输出的做法修改程序应该如下的才是正确:如今那么多错误程序真是误人子弟啊~ 真的要用人家的程序拿来也要阅读几次了解了才能用,不然那错的都不知道了[cpp] view plaincopyprint?uint16 CRC16_Check(uint8*Pushdata,uint8 length) { uint16 Reg_CRC=0xffff; uint8 Temp_reg=0x00; uint8 i,j; for( i = 0; i<length;i ++) { Reg_CRC^= *Pushdata++; for (j = 0; j<8; j++) { if (Reg_CRC & 0x0001) Reg_CRC=Reg_CRC>>1^0xA001; elseReg_CRC >>=1; } }Temp_reg=Reg_CRC>>8; return(Reg_CRC<<8|Temp_reg); }/***************************************************************** ****************************************************************** ***********************************************/下面的就是查表法来计算的,原理是根据生成多项式有唯一解的方法,先把所有值算出来放在数字所在位置,然后直接带入后面位运算即可,下次再详谈这个算法思路这个代码的好处就是占用运算时间少,对于实时性高的要求下,运算步骤要比直接计算法快得多,占用的内存资源也少很多,所以如果使用还是建议使用查表法来做程序,效果的优劣很明晰。
贴代码,代码可以直接使用:[cpp] view plaincopyprint?uint16CRC16(uint8 *Pushdata,uint8 length) { uint8 uchCRCHi=0xFF; uint8 uchCRCLo=0xFF; uint8 uIndex; while(length--) { uIndex=uchCRCHi^*Pushdata++; uchCRCHi=uchCRCLo^auchCRCHi[uIndex];uchCRCLo=auchCRCLo[uIndex]; } return (uchCRCHi<<8|uchCRCLo); } //以下是计算好的数组表:/* CRC 高位字节值表CRC high byte */ const uint8 code auchCRCHi[] = { 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 } ; /* CRC低位字节值表CRC low byte */ const uint8 code auchCRCLo[] = { 0x00, 0xC0, 0xC1,0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D,0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17,0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9,0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F,0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25,0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63,0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C,0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 } ;。