据说刚过去的高考数学很难,小编当年上学时挺喜欢数学的,最近特意复习了一下CRC校验的计算过程。
CRC是众多校验方式中的一种,校验的目的是为了检测数据的正确性。在详细介绍CRC计算之前,我们先来看两个常见的较为简单点的校验:串口通信中的奇偶校验和身份证号码中的MOD 11-2校验。
先看奇偶校验,假设要发送8位数据10110101,奇校验是再加一位校验位,让这9位数据中的1的个数为奇数。
10110101->101101010 奇校验
偶校验是让这9位数据中1的个数为偶数。
10110101->101101011 偶校验
接收方收到数据后计算其奇偶性,如果不对,则说明数据传输中发生了错误。
奇偶校验优点是使用简单,缺点是检错率有限,只有奇数个数据位发生变化的错误能检测到,偶数个数据位变化的错误它检测不了。
最近拿着身份证去核酸检测的次数太多了,让我对身份证号码的组成产生了兴趣,尤其好奇的是为什么有的身份号号码最后一位是X。身份证号码总共18位,包括17位数字码和1位校验码。
1)1-6位是地址码,表示编码对象所在县。
2)7-14位是出生日期码,表示编码对象出生的年、月、日。
3)15-17位是顺序码,表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
4)校验码,用来检验身份证号码是否正确,采用MOD 11-2校验码系统。
校验的公式如下:
简单来说它的校验规则是:连校验码一起,由从右到左逐位乘以2的n次方取模11并求和,对11的余数必须为1。
以一个身份证号码11010519491231002X为例,
校验码计算如下:
1) 将前面的身份证号码17位数分别乘以不同的系数。从左到右的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
2) 将这17位数字和对应的系数各自相乘的结果相加7+9+0+5+0+20+2+9+24+27+7+18+30+5+0+0+4=167;
3) 用加出来的和167除以11,余数是2;
4) 余数2对应校验码是X;
看完上述计算方法之后,大家可以用自己的身份证号码试试。
该校验算法据说可以:
1) 如果某一位填错了,则校验算法可以检测出来。
2) 如果身份证号的相邻2位填反了,则校验算法可以检测出来。
这也是为什么要除以11而不是10的原因,其背后的数据理论推理已经超出了我的能力范畴,这里不再介绍了。
上面的两种校验和CRC校验没有什么关系,只是为了让大家对校验先有个感性的认识,下面来正式介绍CRC。
我在网上找了两个计算CRC的软件,输入同样的数据,选择同样的算法,得到的结果一样,
这两个软件的对应关系如下:
把CRC Calculator Info 中的这几个参数弄明白,CRC的过程你也就清楚了
Name:算法的名称,比如对于16bit的CRC来说就有好多个名字,也就是虽然都是16 bit CRC,但是计算方式也有好多种。
Width:指的CRC校验值的长度,通常有8bit,16bit,24bit,32bit等。
Poly:是多项式的值,以上面图中的CRC-8:x8+x2+x+1为例,它表示二进制为100000111,去掉最高位的1,十六进制表示就是0x07
Init: Init 的位数和Poly的位数相同,它的值为全0或者全F,当全为0时,在算法开始前对数据(这个数据是根据RefIn的值得到的)后面补上CRC位数个0后就可以进行后续计算了。当全为1时,表示在算法开始前对数据的前CRC位数(高位)先和对应位数个1进行异或(即:前CRC位数的值按位取反),再在后面补上CRC位数个0,才进行后续计算。
RefIn和Refout:它们要么全为False,要么全为True。
RefIn为False表示,输入的原始数据的每个字节的第7位作为最高有效位,第0位作为最低有效位,即正常计算即可
当RefIn为True时,输入的原始数据的每个字节需要做个逆序的处理,注意:针对的每个字节,而不是整个数据,以一个4字节的原始数据为例:
当Refout为False时,输出不做处理,当Refout为True,需要对输出数据做一次整个数据的逆序处理,注意:这里做的逆序和RefIn不同,它不是按字节逆序,而是整个逆序,以CRC-32为例来说明,最后的数据为32位,当Refout为True时,翻转如下:
XorOut:表示根据上面参数计算完后,和这个数再进行一次异或。
下面分析一下上面的结果0x79的由来:
1) 0x13 对应二进制为00010011
2) 由于RefIn为False,所以0x13不做处理还是00010011
3) 因为Init为0x00,00010011后面加8个0即可,输入数据变为0001001100000000
4) 多项式为x8+x2+x+1 对应二进制100000111,将上述0001001100000000 除以该多项式对应的100000111
整个计算过程如下:
最后得到余数:01111001
5) 由于RefOuT为False,所以余数不变,还为01111001
6) 由于Xorout为0,表示不用再取反,所以最终的结果就是01111001,即十六进制0x79
算一个不过瘾,我们再来计算一个
这一次和上面唯一的不同是将初始值由原来的0x00换为了0xFF,这个例子就是给大家演示初始值到底什么意思.
下面分析一下上面的结果0x79的由来:
1) 0x13 对应二进制为00010011
2) 由于RefIn为False,所以0x13不做处理还是00010011
3) 因为Init为0xFF,00010011的高8位要先与0xFF异或,输入数据变为11101100,后面再加入8个0,变为 1110110000000000
4) 多项式为x8+x2+x+1 对应二进制100000111,将上述1110110000000000 除以该多项式对应的100000111,最后得到余数:10001010
5) 由于RefOuT为False,所以余数不变,还为10001010
6) 由于Xorout为0,表示不用再取反,所以最终的结果就是10001010,即十六进制0x8A
这个例子演示了初始值的含义,我们再看最后一个例子,这个例子演示了RefIn和RefOut为True的具体含义
1) 0x13 对应二进制为00010011
2) 由于RefIn为True,所以0x13需要逆序一下得到11001000
3) 因为Init为0x00,所以11001000后面直接添加4个0即可,得到110010000000
4) 多项式为x4+x+1 对应二进制10011,将上述110010000000 除以10011,最后得到余数:0010
5) 由于RefOuT为True,所以余数要逆序变为0100
6) 由于Xorout为0,表示不用再取反,所以最终的结果就是0100
CRC每种参数模型的检错能力,同时CRC也可以纠错,这需要专业的数学计算,这也超出了我的能力,这里也不介绍了。
看完这些大家应该都清楚了CRC的计算,有些 MCU本身集成了硬件CRC模块,你只需要配置寄存器,就可以算出CRC结果了,或者也可以通过软件来实现,https://github.com/whik/crc-lib-c 有一个开源的代码可以用。不管哪种方法,了解下详细的计算过程还是有好处的。
CRC计算软件和github 软件代码下载如下:
https://cowtransfer.com/s/05d6adadea654c 或 打开【奶牛快传】cowtransfer.com 使用传输口令:diz36q 提取
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !