CRC校验(2):CRC32查表法详解、代码实现及CRC反转

这篇具有很好参考价值的文章主要介绍了CRC校验(2):CRC32查表法详解、代码实现及CRC反转。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

对于现在的CPU来说,基本上都在硬件上实现了CRC校验。但我们还是想用软件来实现一下CRC的代码,这样可以更深入地理解里面的原理。所以这一节就来详细地解释如何使用查表法从软件上来实现CRC-32的校验。另外,CRC还有一种反转的情况,实际上反转和不反转没有什么太大的区别,主要是需求和标准的不同。

1 找规律

再来看一下上一节中的例子,被除数为100100,除数为1101的计算过程:
CRC校验(2):CRC32查表法详解、代码实现及CRC反转
我们来找一下规律,思考一下在计算机中应该如何实现。不难发现以下规律:

  • 除数的最高位一定是1,因为我们用的是它所代表的多项式的最高阶组成的数字
  • 对于被除数来说,如果它的最高位不是1的话,就"除不动",什么都不做,左移一位,继续判断。
  • 由于做的是异或运算,所以当前的结果不一定要"减得动"被除数,只需要满足除数最高位为1,就和被除数做异或运算,然后再左移一位

2 查表法原理

如果我们要软件上实现CRC的话,要一位一位的做这些运算,效率是非常低的,所以我们就想能不能每8位计算一次,因为每个特定的数字与这个被除数的经过8次移位操作得到的异或结果都是相同的。
(1)为什么不每16位计算一次?
这样就要保存 2 16 2^{16} 216个uint16_t类型的数据,一个表格就有128KB,这比很多单片机的Flash要大了
(2)每8位计算一次的理论依据是什么
我们知道 C R C ( 0 ) = 0 CRC(0)=0 CRC(0)=0,然后由前面分析出的特性,我们可以推导出这个公式: C R C ( A + B ) = C R C ( A ) + C R C ( B ) CRC(A+B) = CRC(A) + CRC(B) CRC(A+B)=CRC(A)+CRC(B)

现在举个例子,假如我们想求 C R C ( 0 × a b c d e f 12 ) CRC(0×abcdef12) CRC(0×abcdef12),他就可以等价于:
C R C ( 0 × a b c d e f 12 ) = C R C ( 0 × a b 000000 ) + C R C ( 0 × 00 c d e f 12 ) = C R C ( 0 × a b ) + C R C ( 0 × c d ) + C R C ( 0 × e f ) + C R C ( 0 × 12 ) CRC(0×abcdef12) = CRC(0×ab000000) + CRC(0×00cdef12) = CRC(0×ab) + CRC(0×cd) + CRC(0×ef) + CRC(0×12) CRC(0×abcdef12)=CRC(0×ab000000)+CRC(0×00cdef12)=CRC(0×ab)+CRC(0×cd)+CRC(0×ef)+CRC(0×12)

假如我们定义一个8位的查表表格,这样我们就可以把本来每次要进行的32次移位和异或的运算缩短为了4次。

3 CRC32代码C语言实现

3.1 CRC32表格生成

经过前面的分析,我们知道,我们就是需要给uint8_t范围内(0~255)的每一个数做一下模二除法,然后将得到的结果保存到一个类型为uint8_t、大小为256的表格中。我们只需要判断CRC的最高位是否为1,若为0则左移一位,若为1则先将crc左移1位,然后和多项式做异或运算。CRC32表格生成代码如下:

void GenerateTable(uint32_t polynomial)
{
	for (int byte = 0; byte < 256; ++byte)
	 {
		uint32_t crc = byte;
		
		for (int bit = 32; bit > 0; --bit)
		{
			if (crc & 0x80000000)
			{
				  crc = (crc << 1) ^ polynomial;
			}
			else
			{
				  crc <<= 1;
			}
		}
		crcTable[byte] = crc;
	 }
}

如果你把上面的代码和前面的例子那张图做对比的话,我相信不少人会想为什么在CRC最高位为1的时候,执行的不是crc = (crc ^ polynomial) << 1

别忘了,对于CRC32来说,它的多项式所对应的二进制是有33位的,实际上我们无法用一个uint32_t的变量来保存这个多项式。但是我们知道,CRC32的多项式的最高位一定是1,所以这里我们就保存低32位作为polynomial。当代码中CRC的最高位为1时,它与CRC32的最高位异或的结果也一定是0。所以这里就先将CRC右移一位,然后与多项式异或,这样循环32次,就得到在多项式polynomial下的byte所对应的CRC,将其存入crcTable中。

3.2 CRC查表代码实现

有了前面的表格,我们使用下面的函数就能计算长度为len的字符串msg的CRC32了,在循环中,将CRC的高八位所对应的数在CRC32表格中的结果与上次计算出来的CRC右移八位的结果进行异或,最后就得到了这个字符串的CRC结果了。

unsigned int calcMsgCRC(char *msg, unsigned int len)
{
    unsigned long crc = 0;
    for (int n = 0; n < len; n++)
    {
    	uint8_t c = msg[n] & 0xff;
        crc = crcTable[(crc >> 24) ^ c] ^ (crc << 8);
    }
    return crc;
}
  • 在某些协议的CRC算法中,CRC有一个固定的初始值,求出来CRC结果后也还需要异或一个固定数

另外,在有些场合下,比如从串口中获取最新的固件来升级系统,固件的完整性也通过CRC校验。在有的设备中没有那么大的内存将整个buffer保存下来,再计算整个buffer的CRC校验。对于这种情况,我们可以一段一段地计算CRC,然后将前一次CRC的结果赋值给函数中unsigned long crc的初始值。也就是calcMsgCRC函数的参数也要多加一个lastCRC

  • 我之前从NXP SDK提取了一个分段CRC的代码,大家也可以用这个框架:C语言 CRC32分段计算实现

4 CRC反转

有时你可能会发现一些标准协议的CRC的表格和你生成的就是不一样。这可能是因为CRC有两种实现方式:

  • 非反转CRC:数据的每一位从最高位到最低位依次处理
  • 反转CRC:数据的每个字节或比特位的处理顺序可以根据需求进行反转。

4.1 CRC32的标准反转函数

对于这个反转的定义我一直没有找到,但我找到了代码,所以还是从代码中来理解一下,所谓的反转函数是如何反转的。

  • 当然,这些反转都可以自己定义,比如小端的CRC你转成大端的,每一位反转等。这里介绍的反转方法是在一些知名的协议中使用的CRC算法中用到的。
uint32_t Reverse(uint32_t value)
{
  value = ((value & 0xAAAAAAAA) >> 1) | ((value & 0x55555555) << 1);
  value = ((value & 0xCCCCCCCC) >> 2) | ((value & 0x33333333) << 2);
  value = ((value & 0xF0F0F0F0) >> 4) | ((value & 0x0F0F0F0F) << 4);
  value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8);
  value = (value >> 16) | (value << 16);
  return value;
}

(1)将value的奇数位和偶数位分别取出,并将奇数位右移1位,偶数位左移1位,然后将它们进行按位或运算。这样可以交换奇数位和偶数位的值。
(2)将value的每2位分组,其中每组的高位和低位进行比特顺序的反转。同样,采用按位与和按位或运算,将高位和低位进行交换。
(3)将value的每4位分组,同样对每组的高位和低位进行比特顺序的反转。
(4)将value的每8位分组,同样对每组的高位和低位进行比特顺序的反转。
(5)将value的高16位和低16位进行比特顺序的反转

4.2 CRC输入/输出反转

另外,我们可以选择对CRC在输入时反转,也可以选择对CRC在输出时反转,当然也可以选择都反转。所以这时候反转CRC32表格生成函数需要做小小的修改:

void GenerateTable(uint32_t polynomial, bool reflectIn, bool reflectOut)
{
    for (int byte = 0; byte < 256; ++byte)
    {
        uint32_t crc = (reflectIn ? (Reverse(uint32_t(byte)) >> 24) : byte);

        for (int bit = 32; bit > 0; --bit)
        {
            if (crc & 0x80000000)
            {
                crc = (crc << 1) ^ polynomial;
            }
            else
            {
                crc <<= 1;
            }
        }
        crcTable[byte] = (reflectOut ? Reverse(crc) : crc);
    }
}

对于反转CRC的计算函数来说(假设输入和输出都反转),要这样修改:

unsigned int calcMsgCRCReverse(char *msg, unsigned int len)
{
    unsigned long crc = 0;
    for (int n = 0; n < len; n++)
    {
        uint8_t c = msg[n] & 0xff;
        c = Reverse(c);
        crc = crcTable[(crc ^ c) & 0xFF] ^ (crc >> 8);
    }
    return Reverse(crc);
}

使用反转CRC或非反转CRC时,CRC表格的生成和查找方法也会有所不同。这两种方式在实际应用中并没有明显的性能或准确性差异,选择哪种方式主要取决于应用的需求和标准。文章来源地址https://www.toymoban.com/news/detail-483447.html

到了这里,关于CRC校验(2):CRC32查表法详解、代码实现及CRC反转的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • C#: CRC8,CRC16,CRC32 校验代码

    C#: CRC8,CRC16,CRC32 校验代码

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

    2024年01月18日
    浏览(10)
  • STM32简易多级菜单(数组查表法)

    STM32简易多级菜单(数组查表法)

    单片机开发中,有时会用到屏幕来显示内容,当需要逐级显示内容时,就需要使用多级菜单的形式了。 多级菜单的实现,大体分为两种设计思路: 通过双向链表实现 通过数组查表实现 总体思路都是把菜单的各个界面联系起来,可以从上级菜单跳到下级菜单,也可从下级菜单

    2024年02月02日
    浏览(20)
  • STM32+ESP8266水墨屏天气时钟:简易多级菜单(数组查表法)

    STM32+ESP8266水墨屏天气时钟:简易多级菜单(数组查表法)

    本次的水墨屏幕项目需要做一个多级菜单的显示,所以写出来一起学习,本篇文章不单单适合于水墨屏,像0.96OLED屏幕也适用,区别就是修改显示函数。 多级菜单的实现,一般有两种实现的方法 1.通过双向链表实现 2.通过数组查表实现 两种方法的思路都是把所有的界面连接在

    2024年04月10日
    浏览(12)
  • 【FPGA/verilog -入门学习10】verilog 查表法实现正弦波形发生器

    【FPGA/verilog -入门学习10】verilog 查表法实现正弦波形发生器

    用查找表设计实现一个正弦波形发生器 寻址的位宽是10位,数据量是1024个,输出的数据是16位 数据量是1024个: x = linspace(0,2*pi,1024) 输出数据是16位: y范围:0~2^16 -1 = 0~65535 y =( sin(x)+1)*65535/2 寻址的位宽是10位 输入是0~1023 1023 占用10位 操作步骤 1,使用matlab 生成数据,制作

    2024年02月05日
    浏览(92)
  • (python)数据校验-CRC32校验

    目录 前言 数据校验概念 CRC校验算法 CRC计算原理 算法逻辑 流程图 CRC算法种类 代码实现CRC算法 python实现算法① python实现算法② 总结         在二次开发eCan上位机应用时,遇到了采用CRC(全称是循环冗余校验)32算法 校验文件传输完整性 的场景,浅浅地记录一下使用心得.  

    2023年04月16日
    浏览(14)
  • 【STM32】CRC(循环冗余校验)

    【STM32】CRC(循环冗余校验)

    (1)CRC(Cyclic Redundancy Check),循环冗余校验 (2) 什么是校验,为什么需要校验:数据传输,数据存储过程中需要使用到的 (3)什么是 冗余:表示比实际上要传输的数据还要多 (4)校验实现的关键: 冗余少、运算少、识错纠错能力强 (有些算法只能识别错误但是无法纠错) (1)(

    2024年02月03日
    浏览(12)
  • 5.0 CRC32校验技术概述

    5.0 CRC32校验技术概述

    CRC校验技术是用于检测数据传输或存储过程中是否出现了错误的一种方法,校验算法可以通过计算应用与数据的循环冗余校验(CRC)检验值来检测任何数据损坏。通过运用本校验技术我们可以实现对特定内存区域以及磁盘文件进行完整性检测,并以此来判定特定程序内存是否

    2024年02月08日
    浏览(10)
  • CRC32校验算法原理及计算说明

    CRC32校验算法原理及计算说明

    本文主要介绍如何使用CRC32校验算法计算得出FCS(Frame Check Sequence帧检验序列). 参考: 1、CRC32加密算法原理 2、CRC多项式对应代码 作用:在远距离数据通信中,为确保高效而无差错地传送数据,对数据进行校验即差错控制。 CRC原理实际上就是在一个K位二进制数据序列之后附

    2023年04月22日
    浏览(9)
  • C语言CRC-32 STM32格式校验函数

    C语言CRC-32 STM32格式校验函数

    CRC-32校验产生4个字节长度的数据校验码,通过计算得到的校验码和获得的校验码比较,用于验证获得的数据的正确性。基本的CRC-32校验算法实现,参考: C语言标准CRC-32校验函数 不同厂家通过对输入数据前处理和输出数据后处理的方式不同,又产生了不同的厂家校验函数,这

    2023年04月27日
    浏览(11)
  • CRC校验算法详解

            数据在传输过程(比如通过网线在两台计算机间传文件)中,由于传输信道的原因,可能会有误码现象(比如说发送数字5但接收方收到的却是6),方法发送额外的数据让接收方校验是否正确,这就是数据校验。常用的校验方法是和校验,就是将传送的数据(按字节

    2024年02月09日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包