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

news2024/11/25 18:11:32

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

文章目录

  • 1 找规律
  • 2 查表法原理
  • 3 CRC32代码C语言实现
    • 3.1 CRC32表格生成
    • 3.2 CRC查表代码实现
  • 4 CRC反转
    • 4.1 CRC32的标准反转函数
    • 4.2 CRC输入/输出反转

1 找规律

再来看一下上一节中的例子,被除数为100100,除数为1101的计算过程:
在这里插入图片描述
我们来找一下规律,思考一下在计算机中应该如何实现。不难发现以下规律:

  • 除数的最高位一定是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的标准反转函数

代码如下所示:

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表格的生成和查找方法也会有所不同。这两种方式在实际应用中并没有明显的性能或准确性差异,选择哪种方式主要取决于应用的需求和标准。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/647814.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MongoDB入门笔记

MongoDB入门笔记 1.MongoDB简介 MongoDB是一个开源、高性能、无模式的文档型数据库&#xff0c;当初的设计就是用于简化开发和方便扩展&#xff0c;是NoSQL数据库产品中的一种。是最像关系型数据库&#xff08;MySQL&#xff09;的非关系型数据库。 它支持的数据结构非常松散…

LuatOS-Air AT应用指南--CMUX

目录 简介 语法规则 参数定义 简介 CMUX是指串口多路复用。串口的多路复用器模式&#xff0c;就是使一个串行接口能够将数据传输到四个不同的客户应用程序。 要在Linux下使用模块的CMUX功能&#xff0c;需要在内核中开启相应的支持&#xff0c;开启方法见下图 将Air724UG开…

flink学习文档四 checkpoint机制

目的 checkpoint作为flink保障任务稳健运行的一个重要机制&#xff0c;在日常使用和flink 学习框架图 简单创建一个FlinkKafkaConsumer kafka是大数据中常用的消息存储中间件&#xff0c;也是flink任务中最常用的source源之一&#xff0c;因此flink 也为 kafka提供了内置的连接…

(UE5 5.2)HISM Mobile DrawInstance在渲染层的实现浅分析

在 (UE4 4.27) UHierarchicalInstancedStaticMesh(HISM)原理分析 这篇博客大致介绍HISM组件从游戏线程到渲染线程的重建KD-Tree和剔除并提交DrawCall逻辑&#xff0c;但是没有分析渲染层的大致数据结构和实现. FHierarchicalStaticMeshSceneProxy的相关数据结构 可以看出FHier…

YOLOv5改进系列(9)——替换主干网络之EfficientNetv2

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

C++ 设计模式----组件协作型模式

面向对象设计&#xff0c;为什么&#xff1f; 回答&#xff1a;变化是复用的天敌&#xff01;面向对象设计最大的优势在于&#xff1a;抵御变化 重新认识面向对象 理解隔离变化 ​ 从宏观层面来看&#xff0c;面向对象的构建方式更能适应软件的变化&#xff0c;能将变化所…

LLMs:OpenAI 官方文档发布提高 GPT 使用效果指南—GPT最佳实践(GPT best practices)翻译与解读

LLMs&#xff1a;OpenAI 官方文档发布提高 GPT 使用效果指南—GPT最佳实践(GPT best practices)翻译与解读 导读&#xff1a;为了获得优质输出&#xff0c;需要遵循几点基本原则&#xff1a; >> 写清楚指令&#xff1a;将任务和期望输出描述得尽可能清楚。GPT 无法读取您…

NLP——Question Answering 问答模型

文章目录 2 key approachesInformation retrieval-based QAQuestion Processing 问题处理Answer Types Retrieval 文档检索Answer Extraction 答案提取 Knowledge-based QASemantic Parsing 语义解析 Hybrid QAEvaluation 2 key approaches Information retrieval-based QA 基于…

JSBridge

在Hybrid模式下&#xff0c;H5会经常需要使用Native的功能&#xff0c;比如打开二维码扫描、调用原生页面、获取用户信息等&#xff0c;同时Native也需要向Web端发送推送、更新状态等&#xff0c;而JavaScript是运行在单独的JS Context中&#xff08;Webview容器、JSCore等&…

mysql select是如何一步步执行的呢?

mysql select执行流程如图所示 server侧 在8.0之前server存在查询语句对应数据的缓存&#xff0c;不过在实际使用中比较鸡肋&#xff0c;对于更新比较频繁、稍微改点查询语句都会导致缓存无法用到 解析 解析sql语句为mysql能够直接执行的形式。通过词法分析识别表名、字段名等…

IIC总线实验

IIC总线实验 一、IIC总线基础概念 1、I2C总线是PHLIPS公司在八十年代初推出的一种同步串行半双工总线&#xff0c;主要用于连接整体电路。 2、I2C总线为两线制&#xff0c;只有两根双向信号线 3、一根是数据线SDA&#xff0c;另一根是时钟线SCL 4、I2C硬件结构简单&#xf…

第四章 模型篇:模型训练与示例

文章目录 SummaryAutogradFunctions ()GradientBackward() OptimizationOptimization loopOptimizerLearning Rate SchedulesTime-dependent schedulesPerformance-dependent schedulesTraining with MomentumAdaptive learning rates optim.lr_scheluder Summary 在pytorch_t…

一分钟学一个 Linux 命令 - find 和 grep

前言 大家好&#xff0c;我是 god23bin。欢迎来到《一分钟学一个 Linux 命令》系列&#xff0c;每天只需一分钟&#xff0c;记住一个 Linux 命令不成问题。今天需要你花两分钟时间来学习下&#xff0c;因为今天要介绍的是两个常用的搜索命令&#xff1a;find 和 grep 命令。 …

Spring是什么?

目录 1、Spring的简介 2、Spring七大功能模块 3、Spring的优点 4、Spring的缺点 5、Sprig容器 6、Spring的生态圈&#xff08;重点&#xff09;***** 7、Spring中bean的生命周期 1、Spring的简介 Spring的英文翻译为春天&#xff0c;可以说是给Java程序员带来了春天&…

认识泛型

目录 什么是泛型 引出泛型 语法 泛型类的使用 语法 示例 类型推导(Type Inference) 裸类型(Raw Type) 小结&#xff1a; 泛型如何编译的 擦除机制 为什么不能实例化泛型类型数组 泛型方法 定义语法 泛型接口 泛型数组 什么是泛型 一般的类和方法&#xff0c…

013:解决vue中不能加载.geojson的问题

第013个 查看专栏目录: VUE — element UI 本文章目录 问题状态造成这个结果的原因&#xff1a;解决办法Vue Loader 其他特性&#xff1a;专栏目标 问题状态 在做vue项目的时候&#xff0c;碰到这样一个问题&#xff0c;vue页面中引用一个.geojson文件&#xff0c;提示如下错误…

Redis-原生命令

string 单值 set key value get key 对象 set user:1 value Mset user:1:name zhangsan user:1:sex man Mget user:1:name user:1:sex 分布式锁 setnx product:1001 true 计数器/全局序列号维护 incr article:readcount:{文章id} get article:readcount:{文章id} 哈希hash…

JavaEE语法第一章、计算机工作原理

【计算机科学速成课】[40集全/精校] - Crash Course Computer Science_哔哩哔哩_bilibili 目录 一、计算机发展史 二、冯诺依曼体系&#xff08;Von Neumann Architecture&#xff09; 三、CPU简单介绍 3.1CPU介绍 3.2并行和并发 四、操作系统&#xff08;Operating Syste…

【netty基础四】netty与nio

文章目录 一. 反应堆1. 堵塞模型2. Java NIO的工作原理 二. Netty与NIO 一. 反应堆 1. 堵塞模型 阻塞I/O在调用InputStream.read()方法时是阻塞的&#xff0c;它会一直等到数据到来&#xff08;或超时&#xff09;时才会返回&#xff1b; 同样&#xff0c;在调用ServerSocke…

PN7160 card emulation

AN13861.pdf 1 简介 本文档的目的是举例说明如何为特定的 CE 场景正确设置卡仿真 (CE)。 有关 CE 体系结构的详细说明&#xff0c;请查看用户手册 [5]。 卡仿真的硬件设置&#xff0c;参考[13]和[14]。 要求&#xff1a; • MCUXpresso 和/或Android 和/或Linux 的知识 • PN…