CRC校验详解(附代码示例)

您所在的位置:网站首页 crc循环冗余码的作用 CRC校验详解(附代码示例)

CRC校验详解(附代码示例)

2023-11-12 18:21| 来源: 网络整理| 查看: 265

目录

1.CRC校验原理

2.生成多项式

3.以CRC-16校验为例讲解编程实现

3.3.1 完全按照CRC原理实现校验

3.3.2 工程中常用CRC校验过程

3.3.3 改进的CRC校验过程

4.以CRC-8校验为例讲解查表法

5.以CRC-16校验为例讲解查表法

5.1.生成表格

5.2.查表法实现

6.代码链接

CRC校验即循环冗余校验(Cyclic Redundancy Check),是基于数据计算一组效验码,用于核对数据传输过程中是否被更改或传输错误。首先看两个概念,后续会用到。

模2除法:也叫模2运算,就是结果除以2后取余数。模2除法每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在CRC计算中有应用到模2除法。多项式与二进制:二进制可表示成多项式的形式,比如二进制1101表示为: x3+x2+x0;1011表示为:x3+x1+x0。 1.CRC校验原理

CRC校验本质上是选取一个合适的除数,要进行校验的数据是被除数,然后做模2除法,得到的余数就是CRC校验值。

下面用具体的例子做讲解:给定一组数据A:10110011(二进制),选取除数B:11001。

首先需要在被除数A后加4个比特位0(具体加几个0是根据除数B的位数决定的,比如这里B是5位,那么A后面加4个0;如果B选6位,则A后面加5个0,总之加的0的个数比除数B的个数少1位。后面还会提到怎么添加)。进行模2除法运算。注意每次都是模2运算,即异或。最后得到余数C就是CRC校验值。注意余数位数必须比除数少1位,如果不够前面加0补齐。运算如下图所示

                   

2.生成多项式

第1章讲解了CRC校验的基本原理,通常我们把选取的除数称之为“生成多项式”。事实上,生成多项式的选取是由一定标准的,如果选的不好,那么检出错误的概率就会低很多。好在这个问题已经被专家们研究了很长一段时间了,对于我们这些使用者来说,只要把现成的成果拿来用就行了。下表是一些标准的CRC生成多项式,可以直接使用。

标准CRC生成多项式 名称生成多项式简记式CRC-4x4+x+10x03CRC-8x8+x5+x4+10x31CRC-8x8+x2+x1+1 0x07CRC-8x8+x6+x4+x3+x2+x10x5ECRC-12x12+x11+x3+x+10x080FCRC-16x16+x15+x2+1 0x8005CRC16-CCITTx16+x12+x5+10x1021CRC-32x32+x26+x23+...+x2+x+10x04C11DB7

更多标准CRC生成式请参考https://en.wikipedia.org/wiki/Cyclic_redundancy_check

有一点要特别注意,文献中提到的生成多项式经常会说到多项式的位宽(Width,简记为W),这个位宽不是多项式对应的二进制数的位数,而是位数减1。比如CRC8中用到的位宽为8的生成多项式,其实对应得二进制数有九位:100110001。另外一点,多项式表示和二进制表示都很繁琐,交流起来不方便,因此,文献中多用16进制简写法来表示,因为生成多项式的最高位肯定为1,最高位的位置由位宽可知,故在简记式中,将最高的1统一去掉了,如CRC32的生成多项式简记为04C11DB7实际上表示的是104C11DB7。当然,这样简记除了方便外,在编程计算时也有它的用处。所以在第一章中提到的在被除数后增加0的位数就是位宽,计算出的CRC校验值长度也是位宽。

3.以CRC-16校验为例讲解编程实现 3.3.1 完全按照CRC原理实现校验

实际工程中多使用CRC-16校验,即选取生成多项式为0x8005。按照前面提到的CRC校验原理,编程实现步骤如下:(注意实际编程时并不用这种直接的方法,如不想看可直接跳到3.3.2)

预置1个16位的变量为CRC,此值存放CRC校验值,赋初值为0;将需要校验的字符串str后面添加16个0;如果变量CRC最高位为1,变量CRC与0x8005异或,然后将变量CRC左移1位,最低位补入1比特新的数据(来自需要校验的字符串str);如果变量CRC最高位为0,直接将变量CRC左移1位,最低位补入1比特新的数据(来自需要校验的字符串str);重复2-3步,直到字符串str最后1位补入变量CRC中;此时得到的余数就是CRC校验值。

这种直接的方法有一个弊端,那就是在字符串前面加0,并不影响校验值,这就不符合我们的预期了。比如,我们想校验的1字节1001 1100,现在在前面补1字节0,变成2字节0000 0000 1001 1100,结果两个得到的校验值是一样的。所以在实际应用中,CRC校验过程做了一些改变:增加了“余数初始值”、“结果异或值”、“输入数据反转”和“输出数据反转”四个概念。

3.3.2 工程中常用CRC校验过程 余数初始值:即在计算开始前,先给变量CRC赋的初值。结果异或值:即在计算结束后,得到的变量CRC与这个值进行异或操作,就得到了最终的校验值。输入数据反转:即在计算开始前,将需要校验的数据反转,如数据位1011,反转后为1101。输出数据反转:即在计算结束后,与结果异或值异或之前,计算值反转,如计算结果为1011,反转后为1101。

实际应用中,生成多项式、余数初始值、结果异或值、输入数据反转和输出数据反转是有对应关系的,这些对应关系是大家都遵守的标准,如下表所示:

表3-1 常见CRC参数模型 CRC算法名称多项式公式宽度多项式(16进制)初始值(16进制)结果异或值(16进制)输入值反转输出值反转CRC-4/ITUx4 + x + 14030000truetrueCRC-5/EPCx4 + x3 + 15090900falsefalseCRC-5/ITUx5 + x4 + x2 + 15150000truetrueCRC-5/USBx5 + x2 + 15051F1FtruetrueCRC-6/ITUx6 + x + 16030000truetrueCRC-7/MMCx7 + x3 + 17090000falsefalseCRC-8x8 + x2 + x + 18070000falsefalseCRC-8/ITUx8 + x2 + x + 18070055falsefalseCRC-8/ROHCx8 + x2 + x + 1807FF00truetrueCRC-8/MAXIMx8 + x5 + x4 + 18310000truetrueCRC-16/IBMx16 + x15 + x2 + 116800500000000truetrueCRC-16/MAXIMx16 + x15 + x2 + 11680050000FFFFtruetrueCRC-16/USBx16 + x15 + x2 + 1168005FFFFFFFFtruetrueCRC-16/MODBUSx16 + x15 + x2 + 1168005FFFF0000truetrueCRC-16/CCITTx16 + x12 + x5 + 116102100000000truetrueCRC-16/CCITT-FALSEx16 + x12 + x5 + 1161021FFFF0000falsefalseCRC-16/x5x16 + x12 + x5 + 1161021FFFFFFFFtruetrueCRC-16/XMODEMx16 + x12 + x5 + 116102100000000falsefalseCRC-16/DNPx16 + x13 + x12 + x11 + x10 + x8 + x6 + x5 + x2 + 1163D650000FFFFtruetrueCRC-32x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 13204C11DB7FFFFFFFFFFFFFFFFtruetrueCRC-32/MPEG-2x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 13204C11DB7FFFFFFFF00000000falsefalse

接下来以CRC-16/IBM校验为例,讲解工程中使用的CRC校验编程实现。具体实现时,以字节为单位进行计算。

预置1个16位的变量CRC,存放校验值,首先根据表3-1赋初值0x0000;将第1个字节按照表3-1看是否需要反转,若需要,则按反转,若不需要,直接进入第3步。这里需要反转;把第1个字节按照步骤2处理后,与16位的变量CRC的高8位相异或,把结果放于变量CRC,低8位数据不变;把变量CRC的内容左移1位(朝高位)用0填补最低位,并检查左移后的移出位;如果移出位为0:重复第4步(再次左移一位);如果移出位为1,变量CRC与多项式8005(1000 0000 0000 0101)进行异或;重复步骤4和5,直到左移8次,这样整个8位数据全部进行了处理;重复步骤2到步骤6,进行通讯信息帧下一个字节的处理;将该通讯信息帧所有字节按上述步骤计算完成后,将得到的16位变量CRC按照表3-1看是否需要反转,这里需要反转;最后,与结果异或值异或,得到的变量CRC即为CRC校验值;

我在这里按照如上方法整理了一个通用代码,包含CRC-8,CRC-16和CRC-32。代码如下:

type.h

#ifndef __TYPE_H__ #define __TYPE_H__ #include typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; typedef unsigned char BOOL; #define FALSE 0 #define TRUE 1 #endif

crc.h文件

#ifndef __CRC_H__ #define __CRC_H__ #include "type.h" typedef struct { u8 poly;//多项式 u8 InitValue;//初始值 u8 xor;//结果异或值 BOOL InputReverse; BOOL OutputReverse; }CRC_8; typedef struct { u16 poly;//多项式 u16 InitValue;//初始值 u16 xor;//结果异或值 BOOL InputReverse; BOOL OutputReverse; }CRC_16; typedef struct { u32 poly;//多项式 u32 InitValue;//初始值 u32 xor;//结果异或值 BOOL InputReverse; BOOL OutputReverse; }CRC_32; const CRC_8 crc_8; const CRC_8 crc_8_ITU; const CRC_8 crc_8_ROHC; const CRC_8 crc_8_MAXIM; const CRC_16 crc_16_IBM; const CRC_16 crc_16_MAXIM; const CRC_16 crc_16_USB; const CRC_16 crc_16_MODBUS; const CRC_16 crc_16_CCITT; const CRC_16 crc_16_CCITT_FALSE; const CRC_16 crc_16_X5; const CRC_16 crc_16_XMODEM; const CRC_16 crc_16_DNP; const CRC_32 crc_32; const CRC_32 crc_32_MPEG2; u8 crc8(u8 *addr, int num,CRC_8 type) ; u16 crc16(u8 *addr, int num,CRC_16 type) ; u32 crc32(u8 *addr, int num,CRC_32 type) ; #endif

crc.c文件

#include #include "type.h" #include "CRC.h" const CRC_8 crc_8 = {0x07,0x00,0x00,FALSE,FALSE}; const CRC_8 crc_8_ITU = {0x07,0x00,0x55,FALSE,FALSE}; const CRC_8 crc_8_ROHC = {0x07,0xff,0x00,TRUE,TRUE}; const CRC_8 crc_8_MAXIM = {0x31,0x00,0x00,TRUE,TRUE}; const CRC_16 crc_16_IBM = {0x8005,0x0000,0x0000,TRUE,TRUE}; const CRC_16 crc_16_MAXIM = {0x8005,0x0000,0xffff,TRUE,TRUE}; const CRC_16 crc_16_USB = {0x8005,0xffff,0xffff,TRUE,TRUE}; const CRC_16 crc_16_MODBUS = {0x8005,0xffff,0x0000,TRUE,TRUE}; const CRC_16 crc_16_CCITT = {0x1021,0x0000,0x0000,TRUE,TRUE}; const CRC_16 crc_16_CCITT_FALSE = {0x1021,0xffff,0x0000,FALSE,FALSE}; const CRC_16 crc_16_X5 = {0x1021,0xffff,0xffff,TRUE,TRUE}; const CRC_16 crc_16_XMODEM = {0x1021,0x0000,0x0000,FALSE,FALSE}; const CRC_16 crc_16_DNP = {0x3d65,0x0000,0xffff,TRUE,TRUE}; const CRC_32 crc_32 = {0x04c11db7,0xffffffff,0xffffffff,TRUE,TRUE}; const CRC_32 crc_32_MPEG2 = {0x04c11db7,0xffffffff,0x00000000,FALSE,FALSE}; /***************************************************************************** *function name:reverse8 *function: 字节反转,如1100 0101 反转后为1010 0011 *input:1字节 *output:反转后字节 ******************************************************************************/ u8 reverse8(u8 data) { u8 i; u8 temp=0; for(i=0;i>i) & 0x01)


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3