ABB机器人实现CRC校验算法(循环冗余校验)

CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。

在使用ABB机器人进行串口或TCP/IP通信时,为了确保数据发送和接收准确,可以对所发送和接收的数据进行CRC校验。但是ABB机器人是没有提供直接的CRC校验算法函数的,所以这就需要我们自行编写函数实现CRC算法。

CRC算法参数模型解释

  1. Name:CRC校验算法的名称;

  2. Width:CRC校验值的宽度;

  3. Poly:多项式;

  4. RefIn:输入反转,为ture时反转,false时不反转。在计算输入反转时,反转的操作是针对每一个字节的bit位进行反转,字节数据之间仍保持原有顺序。If this value is TRUE, each input byte is reflected before being used in the calculation. Reflected means that the bits of the input byte are used in reverse order. So this also means that bit 0 is treated as the most significant bit and bit 7 as least significant. Example with byte 0x82 = b10000010: Reflected(0x82) = Reflected(b10000010) = b01000001 = 0x41;

  5. Init:该参数的值有两种形式,全为0,全为1, 在判定并执行RefIn之后,当Init全为1时,表示在算法开始前对数据的前CRC位数(高位)先和对应位数个1进行异或(即:前CRC位数的值按位取反),再在后面补上CRC位数个0,才进行后续计算。当Init全为0时,在算法开始前对数据(这个数据是根据RefIn的值得到的)后面补上CRC位数个0后就可以进行后续计算;

  6. RefOut:输出反转, RefIn和RefOut这两个值同时为ture或者同时为false。 RefOut的反转是将计算获得的CRC码整体进行反转,而不是像RefIn一样仅反转字节内的bit; 

  7. XorOut:表示执行完RefOut之后,对结果进行异或,该值为全0或者全1。

CRC简化

A CRC-8 algorithm uses actually a 9bit generator polynom, but it would be cumbersome to track such an unaligned value in an algorithm. Fortunately, as described in the previous chapter, the most significant bit can be discarded. First, it is always 1. Second, because the divisor is always aligned in such a manner that this leading ‘1’ alignes with the next ‘1’ of the divident, the XOR result for this bit is always 0.

计算过程

详细的原理可以参考该网站:http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html,使用ABB机器人进行计算的代码见下一节。

Code

MODULE CRCModule
	!*****************************************************
	!Module Name:   CRCModule
	!Version:       1.0
	!Description:   CRC algorithm, support CRC-16/MODBUS, CRC-8 and CRC-32
	!Date:          2021-3-15
	!Author:        Michael
	!*****************************************************

	!2021-3-16, Michael, Fix bugs to support CRC_8 and CRC_32
	!2021-3-17, Michael, Add CalulateTableItem CalulateTableItemReflect
	!2021-3-19, Michael, Add ComputeCRCByTable

	RECORD RECORDCRCInfo
		string NAME;
		!Width must be greater than or equal to 8
		num WIDTH;
		dnum POLYNOMIAL;
		dnum INITIALVALUE;
		dnum FINALXORVALUE;
		bool INPUTREFLECTED;
		bool RESULTREFLECTED;
	ENDRECORD

	CONST RECORDCRCInfo CRC_8:=["CRC-8",8,0X07,0X00,0X00,FALSE,FALSE];
	CONST RECORDCRCInfo CRC_16_MODBUS:=["CRC-16/MODBUS",16,0X8005,0XFFFF,0X0000,TRUE,TRUE];
	CONST RECORDCRCInfo CRC_32:=["CRC-32",32,0X04C11DB7,0XFFFFFFFF,0XFFFFFFFF,TRUE,TRUE];

	LOCAL PERS dnum CRCRemainder:=0xcbf43926;

	LOCAL PERS dnum Table_CRC_8{256}:=[0x0,0x7,0xe,0x9,0x1c,0x1b,0x12,0x15,0x38,0x3f,0x36,0x31,0x24,0x23,0x2a,0x2d,0x70,0x77,0x7e,0x79,0x6c,0x6b,0x62,0x65,0x48,0x4f,0x46,0x41,0x54,0x53,0x5a,0x5d,0xe0,0xe7,0xee,0xe9,0xfc,0xfb,0xf2,0xf5,0xd8,0xdf,0xd6,0xd1,0xc4,0xc3,0xca,0xcd,0x90,0x97,0x9e,0x99,0x8c,0x8b,0x82,0x85,0xa8,0xaf,0xa6,0xa1,0xb4,0xb3,0xba,0xbd,0xc7,0xc0,0xc9,0xce,0xdb,0xdc,0xd5,0xd2,0xff,0xf8,0xf1,0xf6,0xe3,0xe4,0xed,0xea,0xb7,0xb0,0xb9,0xbe,0xab,0xac,0xa5,0xa2,0x8f,0x88,0x81,0x86,0x93,0x94,0x9d,0x9a,0x27,0x20,0x29,0x2e,0x3b,0x3c,0x35,0x32,0x1f,0x18,0x11,0x16,0x3,0x4,0xd,0xa,0x57,0x50,0x59,0x5e,0x4b,0x4c,0x45,0x42,0x6f,0x68,0x61,0x66,0x73,0x74,0x7d,0x7a,0x89,0x8e,0x87,0x80,0x95,0x92,0x9b,0x9c,0xb1,0xb6,0xbf,0xb8,0xad,0xaa,0xa3,0xa4,0xf9,0xfe,0xf7,0xf0,0xe5,0xe2,0xeb,0xec,0xc1,0xc6,0xcf,0xc8,0xdd,0xda,0xd3,0xd4,0x69,0x6e,0x67,0x60,0x75,0x72,0x7b,0x7c,0x51,0x56,0x5f,0x58,0x4d,0x4a,0x43,0x44,0x19,0x1e,0x17,0x10,0x5,0x2,0xb,0xc,0x21,0x26,0x2f,0x28,0x3d,0x3a,0x33,0x34,0x4e,0x49,0x40,0x47,0x52,0x55,0x5c,0x5b,0x76,0x71,0x78,0x7f,0x6a,0x6d,0x64,0x63,0x3e,0x39,0x30,0x37,0x22,0x25,0x2c,0x2b,0x6,0x1,0x8,0xf,0x1a,0x1d,0x14,0x13,0xae,0xa9,0xa0,0xa7,0xb2,0xb5,0xbc,0xbb,0x96,0x91,0x98,0x9f,0x8a,0x8d,0x84,0x83,0xde,0xd9,0xd0,0xd7,0xc2,0xc5,0xcc,0xcb,0xe6,0xe1,0xe8,0xef,0xfa,0xfd,0xf4,0xf3];
	LOCAL PERS dnum Table_CRC_16_MODBUS{256}:=[0x0,0xc0c1,0xc181,0x140,0xc301,0x3c0,0x280,0xc241,0xc601,0x6c0,0x780,0xc741,0x500,0xc5c1,0xc481,0x440,0xcc01,0xcc0,0xd80,0xcd41,0xf00,0xcfc1,0xce81,0xe40,0xa00,0xcac1,0xcb81,0xb40,0xc901,0x9c0,0x880,0xc841,0xd801,0x18c0,0x1980,0xd941,0x1b00,0xdbc1,0xda81,0x1a40,0x1e00,0xdec1,0xdf81,0x1f40,0xdd01,0x1dc0,0x1c80,0xdc41,0x1400,0xd4c1,0xd581,0x1540,0xd701,0x17c0,0x1680,0xd641,0xd201,0x12c0,0x1380,0xd341,0x1100,0xd1c1,0xd081,0x1040,0xf001,0x30c0,0x3180,0xf141,0x3300,0xf3c1,0xf281,0x3240,0x3600,0xf6c1,0xf781,0x3740,0xf501,0x35c0,0x3480,0xf441,0x3c00,0xfcc1,0xfd81,0x3d40,0xff01,0x3fc0,0x3e80,0xfe41,0xfa01,0x3ac0,0x3b80,0xfb41,0x3900,0xf9c1,0xf881,0x3840,0x2800,0xe8c1,0xe981,0x2940,0xeb01,0x2bc0,0x2a80,0xea41,0xee01,0x2ec0,0x2f80,0xef41,0x2d00,0xedc1,0xec81,0x2c40,0xe401,0x24c0,0x2580,0xe541,0x2700,0xe7c1,0xe681,0x2640,0x2200,0xe2c1,0xe381,0x2340,0xe101,0x21c0,0x2080,0xe041,0xa001,0x60c0,0x6180,0xa141,0x6300,0xa3c1,0xa281,0x6240,0x6600,0xa6c1,0xa781,0x6740,0xa501,0x65c0,0x6480,0xa441,0x6c00,0xacc1,0xad81,0x6d40,0xaf01,0x6fc0,0x6e80,0xae41,0xaa01,0x6ac0,0x6b80,0xab41,0x6900,0xa9c1,0xa881,0x6840,0x7800,0xb8c1,0xb981,0x7940,0xbb01,0x7bc0,0x7a80,0xba41,0xbe01,0x7ec0,0x7f80,0xbf41,0x7d00,0xbdc1,0xbc81,0x7c40,0xb401,0x74c0,0x7580,0xb541,0x7700,0xb7c1,0xb681,0x7640,0x7200,0xb2c1,0xb381,0x7340,0xb101,0x71c0,0x7080,0xb041,0x5000,0x90c1,0x9181,0x5140,0x9301,0x53c0,0x5280,0x9241,0x9601,0x56c0,0x5780,0x9741,0x5500,0x95c1,0x9481,0x5440,0x9c01,0x5cc0,0x5d80,0x9d41,0x5f00,0x9fc1,0x9e81,0x5e40,0x5a00,0x9ac1,0x9b81,0x5b40,0x9901,0x59c0,0x5880,0x9841,0x8801,0x48c0,0x4980,0x8941,0x4b00,0x8bc1,0x8a81,0x4a40,0x4e00,0x8ec1,0x8f81,0x4f40,0x8d01,0x4dc0,0x4c80,0x8c41,0x4400,0x84c1,0x8581,0x4540,0x8701,0x47c0,0x4680,0x8641,0x8201,0x42c0,0x4380,0x8341,0x4100,0x81c1,0x8081,0x4040];
	LOCAL PERS dnum Table_CRC_32{256}:=[0x0,0x77073096,0xee0e612c,0x990951ba,0x76dc419,0x706af48f,0xe963a535,0x9e6495a3,0xedb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988,0x9b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91,0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de,0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7,0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec,0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5,0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172,0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940,0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59,0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116,0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f,0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924,0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d,0x76dc4190,0x1db7106,0x98d220bc,0xefd5102a,0x71b18589,0x6b6b51f,0x9fbfe4a5,0xe8b8d433,0x7807c9a2,0xf00f934,0x9609a88e,0xe10e9818,0x7f6a0dbb,0x86d3d2d,0x91646c97,0xe6635c01,0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e,0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457,0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c,0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65,0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2,0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb,0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0,0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9,0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086,0x5768b525,0x206f85b3,0xb966d409,0xce61e49f,0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad,0xedb88320,0x9abfb3b6,0x3b6e20c,0x74b1d29a,0xead54739,0x9dd277af,0x4db2615,0x73dc1683,0xe3630b12,0x94643b84,0xd6d6a3e,0x7a6a5aa8,0xe40ecf0b,0x9309ff9d,0xa00ae27,0x7d079eb1,0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe,0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7,0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc,0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252,0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b,0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60,0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79,0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236,0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f,0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04,0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d,0x9b64c2b0,0xec63f226,0x756aa39c,0x26d930a,0x9c0906a9,0xeb0e363f,0x72076785,0x5005713,0x95bf4a82,0xe2b87a14,0x7bb12bae,0xcb61b38,0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0xbdbdf21,0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e,0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777,0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c,0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45,0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2,0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db,0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0,0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9,0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,0xbad03605,0xcdd70693,0x54de5729,0x23d967bf,0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94,0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d];

	PROC TestCRC()
		VAR byte data{9}:=[0X31,0X32,0X33,0X34,0X35,0X36,0X37,0X38,0X39];
		!CRC-8 : F4
		!CRC-16/MODBUS : 4B37
		!CRC-32 : CBF43926
		VAR byte crc{4};

		CRCRemainder:=ComputeCRC(data,CRC_8,crc);
		Logging "CRC-8="+DecToHex(ValToStr(CRCRemainder));
		Logging "CRC-8: "+DecToHex(ValToStr(crc{1}));

		CRCRemainder:=ComputeCRC(data,CRC_16_MODBUS,crc);
		Logging "CRC-16/MODBUS="+DecToHex(ValToStr(CRCRemainder));
		Logging "CRC-16/MODBUS: "+DecToHex(ValToStr(crc{1}))+DecToHex(ValToStr(crc{2}));

		CRCRemainder:=ComputeCRC(data,CRC_32,crc);
		Logging "CRC-32="+DecToHex(ValToStr(CRCRemainder));
		Logging "CRC-32: "+DecToHex(ValToStr(crc{1}))+DecToHex(ValToStr(crc{2}))+DecToHex(ValToStr(crc{3}))+DecToHex(ValToStr(crc{4}));

		CRCRemainder:=ComputeCRCByTable(data,CRC_8,Table_CRC_8,crc);
		Logging "CRC-8="+DecToHex(ValToStr(CRCRemainder));
		Logging "CRC-8: "+DecToHex(ValToStr(crc{1}));

		CRCRemainder:=ComputeCRCByTable(data,CRC_16_MODBUS,Table_CRC_16_MODBUS,crc);
		Logging "CRC-16/MODBUS="+DecToHex(ValToStr(CRCRemainder));
		Logging "CRC-16/MODBUS: "+DecToHex(ValToStr(crc{1}))+DecToHex(ValToStr(crc{2}));

		CRCRemainder:=ComputeCRCByTable(data,CRC_32,Table_CRC_32,crc);
		Logging "CRC-32="+DecToHex(ValToStr(CRCRemainder));
		Logging "CRC-32: "+DecToHex(ValToStr(crc{1}))+DecToHex(ValToStr(crc{2}))+DecToHex(ValToStr(crc{3}))+DecToHex(ValToStr(crc{4}));

		!CalculateTable;

	ENDPROC

	FUNC dnum ComputeCRCByTable(byte data{*},RECORDCRCInfo crcInfo,dnum crcTable{*},INOUT byte crc{*})
		VAR dnum curByte;
		VAR dnum dnumMaxValue:=0;
		VAR num pos;
		BitSet dnumMaxValue,crcInfo.WIDTH+1;
		dnumMaxValue:=dnumMaxValue-1;
		CRCRemainder:=crcInfo.INITIALVALUE;

		FOR i FROM 1 TO Dim(data,1) DO
			pos:=DnumToNum(BitAndDnum(BitXORDnum(CRCRemainder,NumToDnum(data{i})),0XFF));
			CRCRemainder:=BitXOrDnum(BitRShDnum(CRCRemainder,8),crcTable{pos+1});
		ENDFOR

		CRCRemainder:=BitXOrDnum(CRCRemainder,crcInfo.FINALXORVALUE);

		crc{1}:=DnumToNum(BitAndDnum(CRCRemainder,0XFF));
		crc{2}:=DnumToNum(BitAndDnum(BitRShDnum(CRCRemainder,8),0XFF));
		crc{3}:=DnumToNum(BitAndDnum(BitRShDnum(CRCRemainder,16),0XFF));
		crc{4}:=DnumToNum(BitAndDnum(BitRShDnum(CRCRemainder,24),0XFF));

		RETURN BitAndDnum(CRCRemainder,dnumMaxValue);

	ENDFUNC

	FUNC dnum ComputeCRC(byte data{*},RECORDCRCInfo crcInfo,INOUT byte crc{*})
		VAR dnum curByte;
		VAR dnum dnumMaxValue:=0;
		BitSet dnumMaxValue,crcInfo.WIDTH+1;
		dnumMaxValue:=dnumMaxValue-1;
		CRCRemainder:=crcInfo.INITIALVALUE;
		FOR i FROM 1 TO Dim(data,1) DO
			IF crcInfo.INPUTREFLECTED THEN
				CRCRemainder:=BitXOrDnum(CRCRemainder,Reflect(NumToDnum(data{i}),crcInfo.WIDTH));
			ELSE
				IF crcInfo.WIDTH>8 THEN
					CRCRemainder:=BitXOrDnum(CRCRemainder,BitLShDnum(NumToDnum(data{i}),crcInfo.WIDTH-8));
				ELSE
					CRCRemainder:=BitXOrDnum(CRCRemainder,NumToDnum(data{i}));
				ENDIF
			ENDIF

			FOR j FROM 1 TO 8 DO
				IF BitCheckDnum(CRCRemainder,crcInfo.WIDTH) THEN
					CRCRemainder:=BitLShDnum(CRCRemainder,1);
					CRCRemainder:=BitXOrDnum(CRCRemainder,crcInfo.POLYNOMIAL);
				ELSE
					CRCRemainder:=BitLShDnum(CRCRemainder,1);
				ENDIF
			ENDFOR
			CRCRemainder:=BitAndDnum(CRCRemainder,dnumMaxValue);
		ENDFOR

		IF crcInfo.RESULTREFLECTED THEN
			CRCRemainder:=Reflect(CRCRemainder,crcInfo.WIDTH);
		ENDIF
		CRCRemainder:=BitXOrDnum(CRCRemainder,crcInfo.FINALXORVALUE);

		crc{1}:=DnumToNum(BitAndDnum(CRCRemainder,0XFF));
		crc{2}:=DnumToNum(BitAndDnum(BitRShDnum(CRCRemainder,8),0XFF));
		crc{3}:=DnumToNum(BitAndDnum(BitRShDnum(CRCRemainder,16),0XFF));
		crc{4}:=DnumToNum(BitAndDnum(BitRShDnum(CRCRemainder,24),0XFF));

		RETURN BitAndDnum(CRCRemainder,dnumMaxValue);

	ENDFUNC

	FUNC dnum Reflect(dnum dnumOri,num numWidth)
		VAR dnum dnumReflected:=0;
		FOR i FROM 1 TO numWidth DO
			IF BitCheckDnum(dnumOri,i) THEN
				BitSet dnumReflected,numWidth-i+1;
			ENDIF
		ENDFOR
		RETURN dnumReflected;
	ENDFUNC

	FUNC byte ReflectByte(byte byteOri)
		byteOri:=BitOr(BitAnd(BitRSh(byteOri,1),0b01010101),BitLSh(BitAnd(byteOri,0b01010101),1));
		byteOri:=BitOr(BitAnd(BitRSh(byteOri,2),0x33),BitLSh(BitAnd(byteOri,0x33),2));
		byteOri:=BitOr(BitAnd(BitRSh(byteOri,4),0x0F),BitLSh(BitAnd(byteOri,0x0F),4));
		RETURN byteOri;
	ENDFUNC

	FUNC dnum ReflectWord(dnum dnumOri)
		dnumOri:=BitOrDnum(BitAndDnum(BitRShDnum(dnumOri,1),0x5555),BitLShDnum(BitAndDnum(dnumOri,0x5555),1));
		dnumOri:=BitOrDnum(BitAndDnum(BitRShDnum(dnumOri,2),0x3333),BitLShDnum(BitAndDnum(dnumOri,0x3333),2));
		dnumOri:=BitOrDnum(BitAndDnum(BitRShDnum(dnumOri,4),0x0F0F),BitLShDnum(BitAndDnum(dnumOri,0x0F0F),4));
		dnumOri:=BitOrDnum(BitAndDnum(BitRShDnum(dnumOri,8),0x00FF),BitLShDnum(BitAndDnum(dnumOri,0x00FF),8));
		RETURN dnumOri;
	ENDFUNC

	FUNC dnum SwitchWordOrder(dnum dnumOri)
		dnumOri:=BitOrDnum(BitAndDnum(BitRShDnum(dnumOri,8),0x00FF),BitAndDnum(BitLShDnum(dnumOri,8),0xFF00));
		RETURN dnumOri;
	ENDFUNC

	PROC CalculateTable()
		FOR i FROM 0 TO 255 DO
			Table_CRC_8{i+1}:=CalulateTableItem(NumToDnum(i),CRC_8);
			Table_CRC_16_MODBUS{i+1}:=CalulateTableItem(NumToDnum(i),CRC_16_MODBUS);
			Table_CRC_32{i+1}:=CalulateTableItem(NumToDnum(i),CRC_32);
		ENDFOR
	ENDPROC

	PROC CalculateTableByReflect()
		FOR i FROM 0 TO 255 DO
			Table_CRC_8{i+1}:=CalulateTableItemReflect(NumToDnum(i),CRC_8);
			Table_CRC_16_MODBUS{i+1}:=CalulateTableItemReflect(NumToDnum(i),CRC_16_MODBUS);
			Table_CRC_32{i+1}:=CalulateTableItemReflect(NumToDnum(i),CRC_32);
		ENDFOR
	ENDPROC

	FUNC dnum CalulateTableItem(dnum numRemainder,RECORDCRCInfo crcInfo)
		VAR dnum dnumMaxValue:=0;
		BitSet dnumMaxValue,crcInfo.WIDTH+1;
		dnumMaxValue:=dnumMaxValue-1;

		IF crcInfo.INPUTREFLECTED THEN
			numRemainder:=Reflect(numRemainder,crcInfo.WIDTH);
		ELSE
			IF crcInfo.WIDTH>8 THEN
				numRemainder:=BitLShDnum(numRemainder,crcInfo.WIDTH-8);
			ENDIF
		ENDIF

		FOR j FROM 1 TO 8 DO
			IF BitCheckDnum(numRemainder,crcInfo.WIDTH) THEN
				numRemainder:=BitLShDnum(numRemainder,1);
				numRemainder:=BitXOrDnum(numRemainder,crcInfo.POLYNOMIAL);
			ELSE
				numRemainder:=BitLShDnum(numRemainder,1);
			ENDIF
		ENDFOR

		IF crcInfo.INPUTREFLECTED THEN
			numRemainder:=Reflect(numRemainder,crcInfo.WIDTH);
		ENDIF

		RETURN BitAndDnum(numRemainder,dnumMaxValue);
	ENDFUNC

	FUNC dnum CalulateTableItemReflect(dnum numRemainder,RECORDCRCInfo crcInfo)
		VAR dnum numPolynomial;
		IF crcInfo.INPUTREFLECTED THEN
			numPolynomial:=Reflect(crcInfo.POLYNOMIAL,crcInfo.WIDTH);
		ELSE
			RETURN CalulateTableItem(numRemainder,crcInfo);
		ENDIF

		FOR j FROM 1 TO 8 DO
			IF BitCheckDnum(numRemainder,1) THEN
				numRemainder:=BitRShDnum(numRemainder,1);
				numRemainder:=BitXOrDnum(numRemainder,numPolynomial);
			ELSE
				numRemainder:=BitRShDnum(numRemainder,1);
			ENDIF
		ENDFOR

		RETURN numRemainder;
	ENDFUNC

ENDMODULE