CRC-16/CRC-32 程序代码
不久前写一程序时要用到 CRC-16 ,但找来找去只在 UDDF 里找到一个 Delphi 的 CRC-32 程序代码,而且是用查表法,虽然说查表法速度快,但 256 项 32 位数据我怀疑可能会有输入错误, 让人不是那么放心,而我又不知道这个表是怎么算出来的。后来我又在一本两年前的笔记本里找到一段关于 CRC 的内容, 也不知是从哪里抄来的,还好里面有一段程序代码,是 CRC-16 的,这段程序正是产生 CRC 表的, 可是这区区几行的程序(基本上与下面的 BuilderTable16 函数相同)看得我一头雾水,直到这两天才弄明白, 并据此推出 CRC-32 的算法,现将全部程序列在下面,并作一些说明以助于理解,不但要知其然,还要知其所以然嘛:
补充:为了使这段程序更加实用,我将其整理为几个单元, 分别用于 Delphi 和 C++Builder 。包括对数据流 TMemoryStream 和字符串的处理。可以在此下载: 3.7KB。Aug.18-01
// 注意:因最高位一定为“1”,故略去
const unsigned short cnCRC_16 = 0x8005;
// CRC-16 = X16 + X15 + X2 + X0
const unsigned short cnCRC_CCITT = 0x1021;
// CRC-CCITT = X16 + X12 + X5 + X0,据说这个 16 位 CRC 多项式比上一个要好
const unsigned long cnCRC_32 = 0x04C10DB7;
// CRC-32 = X32 + X26 + X23 + X22 + X16 + X11 + X10 + X8 + X7 + X5 + X4 + X2 + X1 + X0
unsigned long Table_CRC[256]; // CRC 表
// 构造 16 位 CRC 表
void BuildTable16( unsigned short aPoly )
{
unsigned short i, j;
unsigned short nData;
unsigned short nAccum;
for ( i = 0; i < 256; i++ )
{
nData = ( unsigned short )( i << 8 );
nAccum = 0;
for ( j = 0; j < 8; j++ )
{
if ( ( nData ^ nAccum ) & 0x8000 )
nAccum = ( nAccum << 1 ) ^ aPoly;
else
nAccum <<= 1;
nData <<= 1;
}
Table_CRC[i] = ( unsigned long )nAccum;
}
}
// 计算 16 位 CRC 值,CRC-16 或 CRC-CCITT
unsigned short CRC_16( unsigned char * aData, unsigned long aSize )
{
unsigned long i;
unsigned short nAccum = 0;
BuildTable16( cnCRC_16 ); // or cnCRC_CCITT
for ( i = 0; i < aSize; i++ )
nAccum = ( nAccum << 8 ) ^ ( unsigned short )Table_CRC[( nAccum >> 8 ) ^ *aData++];
return nAccum;
}
// 构造 32 位 CRC 表
void BuildTable32( unsigned long aPoly )
{
unsigned long i, j;
unsigned long nData;
unsigned long nAccum;
for ( i = 0; i < 256; i++ )
{
nData = ( unsigned long )( i << 24 );
nAccum = 0;
for ( j = 0; j < 8; j++ )
{
if ( ( nData ^ nAccum ) & 0x80000000 )
nAccum = ( nAccum << 1 ) ^ aPoly;
else
nAccum <<= 1;
nData <<= 1;
}
Table_CRC[i] = nAccum;
}
}
// 计算 32 位 CRC-32 值
unsigned long CRC_32( unsigned char * aData, unsigned long aSize )
{
unsigned long i;
unsigned long nAccum = 0;
BuildTable32( cnCRC_32 );
for ( i = 0; i < aSize; i++ )
nAccum = ( nAccum << 8 ) ^ Table_CRC[( nAccum >> 24 ) ^ *aData++];
return nAccum;
}
说明: CRC 的计算原理如下(一个字节的简单例子)
11011000 00000000 00000000 <- 一个字节数据, 左移 16b
^10001000 00010000 1 <- CRC-CCITT 多项式, 17b
--------------------------
1010000 00010000 10 <- 中间余数
^1000100 00001000 01
-------------------------
10100 00011000 1100
^10001 00000010 0001
-----------------------
101 00011010 110100
^100 01000000 100001
---------------------
1 01011010 01010100
^1 00010000 00100001
-------------------
01001010 01110101 <- 16b CRC
仿此,可推出两个字节数据计算如下:d 为数据,p 为项式,a 为余数
dddddddd dddddddd 00000000 00000000 <- 数据 D ( D1, D0, 0, 0 )
^pppppppp pppppppp p <- 多项式 P
-----------------------------------
...
aaaaaaaa aaaaaaaa 0 <- 第一次的余数 A' ( A'1, A'0 )
^pppppppp pppppppp p
--------------------------
...
aaaaaaaa aaaaaaaa <- 结果 A ( A1, A0 )
由此与一字节的情况比较,将两个字节分开计算如下:
先算高字节:
dddddddd 00000000 00000000 00000000 <- D1, 0, 0, 0
^pppppppp pppppppp p <- P
-----------------------------------
...
aaaaaaaa aaaaaaaa <- 高字节部分余数 PHA1, PHA0
此处的部分余数与前面两字节算法中的第一次余数有如下关系,即 A'1 = PHA1 ^ D0, A'0 = PHA0:
aaaaaaaa aaaaaaaa <- PHA1, PHA0
^dddddddd <- D0
-----------------
aaaaaaaa aaaaaaaa <- A'1, A'0
低字节的计算:
aaaaaaaa 00000000 00000000 <- A'1, 0, 0
^pppppppp pppppppp p <- P
--------------------------
...
aaaaaaaa aaaaaaaa <- 低字节部分余数 PLA1, PLA0
^aaaaaaaa <- A'0 , 即 PHA0
-----------------
aaaaaaaa aaaaaaaa <- 最后的 CRC ( A1, A0 )
总结以上内容可得规律如下:
设部分余数函数
PA = f( d )
其中 d 为一个字节的数据(注意,除非 n = 0 ,否则就不是原始数据,见下文)
第 n 次的部分余数
PA( n ) = ( PA( n - 1 ) << 8 ) ^ f( d )
其中的
d = ( PA( n - 1 ) >> 8 ) ^ D( n )
其中的 D( n ) 才是一个字节的原始数据。
公式如下:
PA( n ) = ( PA( n - 1 ) << 8 ) ^ f( ( PA( n - 1 ) >> 8 ) ^ D( n ) )
可以注意到函数 f( d ) 的参数 d 为一个字节,对一个确定的多项式 P, f( d ) 的返回值
是与 d 一一对应的,总数为 256 项,将这些数据预先算出保存在表里,f( d )就转换为一
个查表的过程,速度也就可以大幅提高,这也就是查表法计算 CRC 的原理,在 CRC_16 和
CRC_32 两个函数的循环中的语句便是上面那个公式。
再来看 CRC 表是如何计算出来的,即函数 f( d ) 的实现方法。分析前面一个字节数据的
计算过程可发现,d 对结果的影响只表现为对 P 的移位异或,看计算过程中的三个 8 位
的列中只有低两个字节的最后结果是余数,而数据所在的高 8 位列最后都被消去了,因其
中的运算均为异或,不产生进位或借位,故每一位数据只影响本列的结果,即 d 并不直接
影响结果。再将前例变化一下重列如下:
11011000
--------------------------
10001000 00010000 1 // P
^ 1000100 00001000 01 // P
^ 000000 00000000 000 // 0
^ 10001 00000010 0001 // P
^ 0000 00000000 00000 // 0
^ 100 01000000 100001 // P
^ 00 00000000 0000000 // 0
^ 1 00010000 00100001 // P
-------------------
01001010 01110101
现在的问题就是如何根据 d 来对 P 移位异或了,从上面的例子看,也可以理解为每步
移位,但根据 d 决定中间余数是否与 P 异或。从前面原来的例子可以看出,决定的条
件是中间余数的最高位为0,因为 P 的最高位一定为1,即当中间余数与 d 相应位异或
的最高位为1时,中间余数移位就要和 P 异或,否则只需移位即可。具体做法见程序中
的 BuildTable16 和 BuildTable32 两个函数,其方法如下例(上例的变形,注意其中
空格的移动表现了 d 的影响如何被排除在结果之外):
d --------a--------
1 00000000 00000000 <- HSB = 1
0000000 000000000 <- a <<= 1
0001000 000100001 <- P, CRC-CCITT 不含最高位的 1
-----------------
1 0001000 000100001
001000 0001000010
000100 0000100001
-----------------
0 001100 0001100011 <- HSB = 0
01100 00011000110
-----------------
1 01100 00011000110 <- HSB = 1
1100 000110001100
0001 000000100001
-----------------
1 1101 000110101101 <- HSB = 0
101 0001101011010
-----------------
0 101 0001101011010 <- HSB = 1
01 00011010110100
00 01000000100001
-----------------
0 01 01011010010101 <- HSB = 0
1 010110100101010
-----------------
0 1 010110100101010 <- HSB = 1
0101101001010100
0001000000100001
-----------------
0100101001110101 <- CRC
结合这些,前面的程序就好理解了。至于 32 位 CRC 的计算与 16 相似,就不多加说明,请参考源程序。
[Mental Studio]Aug.9-2k
相关连接:http://mental.top263.net/share/dev/crc.zip
分享到:
相关推荐
以下是关于CRC-6、CRC-8、CRC-12、CRC-16和CRC-32的详细解释。 1. CRC-6:CRC-6通常用于简单的通信系统,如无线传感器网络。它产生6位的校验码,用于检测数据中的单个错误。CRC-6的多项式可以是不同的,但常见的有G...
在“crc.zip”这个压缩包中,包含了一个名为“crc.c”的源代码文件,这可能是一个用C语言编写的CRC校验程序。此程序很可能实现了CRC-16和CRC-CCITT两种不同的CRC算法。CRC-16是一种16位的CRC校验,通常用于各种通信...
CRC-16 校验原理和实现 CRC-16 是一种常用的差错校验码,它广泛应用于数据通信领域。它的特征是信息字段和校验字段的长度可以任意选定。本文将详细介绍 CRC-16 的原理、实现和使用方法。 CRC-16 的原理是基于...
主要内容:本文提供了利用C++实现CRC-16校验的一个简洁程序样例。通过对输入序列进行按字节逐位异或运算完成CRC-16检验,帮助确保通过串口通信所传送的数据完整性。附带详细的注解有助于开发者快速理解和运用该方法...
CRC即循环冗余校验码(Cyclic Redundancy Check [1] ):是数据通信领域中最常用的一种查错校验...本程序使用labview2017编写可以直接使用,后台未加密,常数的标明了数据类型,CRC-16只是一种,还有CRC-16/CCITT FALSH等
压缩包中的“CRC16各模式校验程序”很可能包含了针对上述不同CRC16模式的实现代码。这些代码通常由编程语言编写,如C、C++、Python或Java,它们实现了计算和验证CRC16校验码的功能。通过对这些代码的分析和学习,...
在提供的压缩包文件中,"CRC16"可能是包含了实现CRC16校验算法的C#源代码文件,你可以查阅这个文件以获取具体的实现细节和可能的优化。这个源代码对于理解和应用CRC16校验算法在C#环境中的实现非常有帮助。
在本项目中,"串口通信+CRC-CCITT - 16 -False (0x7101)检验" 提及的是串口通信结合了错误检测机制CRC(Cyclic Redundancy Check,循环冗余校验)的一种特定实现,特别是使用了CCITT-16版本,错误校验码为0x7101。...
CRC-16(Modbus)并行计算Verilog代码,结果可在网页http://www.ip33.com/crc.html上进行计算对比
Delphi版本的CRC16计算代码如下所示: ```delphi function mjComCRC(buf: PByte; len: Integer): Word; var i, j: Integer; chr: Word; CRC16Byte: Word; begin CRC16Byte := $ffff; for j := 0 to len - 1 do...
而"CRC"文件可能是CRC-16的C语言实现代码,用于展示如何在程序中实际计算CRC校验码。通过阅读这些文件,可以深入理解CRC-16的计算过程和具体应用方法,同时也可以了解如何在不同的系统和项目中集成CRC校验功能。 总...
当没有输入参数调用程序时,输出CRC-12,CRC-16,CRC-CCITT,CRC-32的生成多项式。 当一个参数调用程序时,参数为并行处理位数,默认生成多项式为CRC-32,输出是计算式。 当两个参数调用程序时,参数为并行处理位数...
crc16-ccitt 通过查表法实现,运算速度比较快,初始值为0xffff,并且是基于标准C语言的,并且已将CRC16运行程序封装成函数,只需要调用就好了,移植性强。
基于LabVIEW的CRC-16程序分析与实现,涉及将传统的CRC-16算法转换为LabVIEW中的图形化代码。LabVIEW提供了对位操作和算术运算的支持,因此实现CRC-16算法是可行的。在LabVIEW中实现CRC-16算法,首先需要理解其算法...
文件中的代码示例展示了如何使用C语言来实现不同类型的CRC算法。这些算法覆盖了从CRC-4到CRC-32的不同长度,以及针对特定协议(如Modbus、DS18B20等)的CRC算法。 ### 3. 具体CRC算法详解 #### 3.1 CRC-4/ITU - **...
标题提及了四种CRC类型:CRC8、CRC16、CRC-CCITT和CRC32,它们分别代表了不同宽度的CRC校验码: 1. **CRC8**:这是一种8位的CRC校验,适用于校验较短的数据块。它使用一个8位的生成多项式,计算过程相对简单,常...
在LabVIEW中实现CRC16校验可以极大地提高程序的可靠性,特别是在处理串口通信、文件读写等场景。 CRC16的工作原理基于多项式除法,通常会使用一个预定义的16位CRC生成多项式,例如常用的CRC-CCITT(X^16 + X^12 + X...
在提供的CRC16-FATEK-PLC文件中,可能包含了使用台湾永宏PLC实现CRC16/MODBUS和CRC16/CCITT算法的梯形图源代码或示例。 在实际应用中,PLC开发者可以根据需求选择适当的CRC算法,修改程序中的参数来切换CRC16类型。...
以下是C#语言实现的CRC16校验码的源程序代码: ```csharp uint i, j; uint crc16 = 0xFFFF; public uint crc16_modbus(byte[] modbusdata, uint Length) { for (i = 0; i ; i++) { crc16 ^= modbusdata[i]; // ...
本文件包“CRC-16-jisuanfangfa.rar_CRC-16”包含了关于CRC-16计算方法的源代码程序,主要帮助我们理解CRC-16的工作原理以及如何在实际编程中实现它。 CRC-16的工作原理基于多项式除法,其核心思想是通过一个固定的...