【基础知识】CRC(循环冗余校验)直接计算和查表法

这篇具有很好参考价值的文章主要介绍了【基础知识】CRC(循环冗余校验)直接计算和查表法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

CRC概述

校验

校验是什么,个人理解就是经过一个算法,使用大量数据(几MB的数据)生成较小长度的一串信息(如16Bit),并切要做到

  1. 原数据不同时,生成的信息大概率不同(不是加密算法不考虑刻意造数据的情况)
  2. 原数据中任意一个或几个数据出现错误时,生成的信息不同(所有的原信息都需要被囊括进生成数据中,也就是说每一个数据都必须参与校验的计算过程并且能对其产生影响

CRC名词解释

宽度 (WIDTH)

即最后生成的数据长度

可以为任意长度

一般使用16bit或32bit

0xFFFF是16bit的校验和

0xFFFFFFFF是32bit的校验和

多项式 (POLY)

这个多项式的每项的系数为0或1,最高系数是和宽度相等,因为有0次幂存在,因此总多项式为

宽度+1项

例如,16bit的CRC16-MODBUS的生成多项式为

crc查表法,基础知识,单片机,嵌入式硬件,用HEX表示为0x1 8005

要求最高幂(这里是x的16次方)系数为1,最低次幂(永远是x的0次方)系数为1

因为最高次一定为1,因此可以将其省略,所有生成多项式的值为0x8005

注意:多项式的其他位可以是任意的,但是检验效果并不完全相同,建议使用某些协议规定的多项式

初始值 (INIT)

因为使用程序进行CRC计算时使用一个宽度 (WIDTH)为长度的寄存器进行

我们需要给这个寄存器写入初始值,写入的初始值即为这个值

一般为全是0或者全是1

如16bit为0xFFFF或0x0000

结果异或值 (XOROUT)

计算完成后输出之前需要于此值进行异或操作

一般为全0或全1,长度和宽度 (WIDTH)相等

输入数据反转(REFIN)

CRC计算时是以字节(Byte)为单位输入寄存器中的

这个参数是说明输入是从这个数据的

直接输入寄存器则是高位到低位

经过反转输入则是低位到高位

具体输入到寄存器的哪个位置之后再说

反转的例子

0x80(0b1000 0000)反转之后就是0x01(0b0000 0001)

0x50(0b0101 0000)反转之后就是0x0A (0b0000 1010)

输出数据反转(REFOUT)

输出数据反转和输入反转类似

只不过是以宽度 (WIDTH)为基准反转而已

0x8000(0b1000 0000 0000 0000)反转是0x0001 (0b0000 0000 0000 0001)

0xA000(0b1010 0000 0000 0000)反转是0x0005 (0b0000 0000 0000 0101)

使用CRC的协议会将这些都规定好,需要时查询即可

CRC手算过程

这只是帮助理解所使用的过程,应用中一般人不会手算CRC吧,这里先忽略初值,选取简单的数据和多项式

模二加减(异或运算(XOR))

这三个说法是等价的,即运算的两个算子相同为0,不同为1,c语言中为

^

例子:0b0^0b1=0b1  0b1100^0b1110=0b0010

运算过程

  1. 在数据最后补0(位数与宽度相等)
  2. 将多项式(完整的)的最高项与数据的第一个1对齐(其他位当作0即可(也就是不改变数据))
  3. 整体进行异或操作
  4. 重复上述操作,直到所有数据均参与了计算(参与有2种,作为0被掠过也算参与)

例子

crc查表法,基础知识,单片机,嵌入式硬件

 crc查表法,基础知识,单片机,嵌入式硬件

所以计算值为0b1110(0xE) 

进入在线计算网站验证如下图

crc查表法,基础知识,单片机,嵌入式硬件

 直接计算

输入数据不反转

 从刚才的手算过程中可以看出,如果每一个数据均需要被遍历,如果为0则掠过,如果为1则进行异或操作

很明显需要使用循环来进行操作

因为是按照字节(Byte)来传输数据

因此需要嵌套循环,外层改变传入的数据,内层遍历8个位(bit)

  1. 首先建立寄存器(CRC),这里以16位为例,则需要数据类型为u16
  2. 填入初值,这里是0xFFFF
  3. 因为要向左移位,因此将数据放到寄存器的高8位(32位就是最高的8位)中(与高8位异或放入)
  4. 如果最高位为1,则先左移1位后寄存器与多项式异或,如果为0则仅仅左移1位
  5. 重复直到所有数据都参与
  6. 寄存器中剩下的便是CRC结果

为什么要放到高8位呢

因为异或具有交换律和结合律,这里不证明,请查百度

因此后8位本质上是将高8位的数据所计算的异或值(本应该与下一个字节进行计算的)先一步进行运算

在往后只需要将下一个字节的数据与移位后的高8位(上一个字节提前计算的)异或即可了

代码例子(C语言写的)

u16 CRC16(u8 * Dat, int Len)
{
	u16 Poly = 0x8005;
	u16 CRC = 0xffff;
	u8 Zj = 0;
	
	for (int i = 0; i < Len; i++)
	{
//在这里加输入反转
		Zj = (Dat[i]);
		CRC ^= (Zj<< 8);
		for (int j = 0; j < 8; j++)
		{
			if (CRC & 0x8000)
			{
				CRC <<= 1;
				CRC ^= Poly;
			}
			else
			{
				CRC <<= 1;
			}
		}
	}
//在这里加输出反转
	return ((CRC)^0x0000);
}

如果需要反转输出,则在输出异或之前加上反转部分的代码即可

反转 的实现

u16 CRC_Reverse_16(u16 Dat)
{
	u16 Res = 0;
	u16 zj = Dat;
	for (int i = 0; i < 16; i++)
	{
		if (zj & 0x8000)
			Res += 1<<i;
		zj <<= 1;
	}
	return Res;
}
u8 CRC_Reverse_8(u8 Dat)
{
	u8 Res = 0;
	u8 zj = Dat;
	for (int i = 0; i < 8; i++)
	{
		if (zj & 0x80)
			Res += 1 << i;
		zj <<= 1;
	}
	return Res;
}

输入数据反转

方法1

可以直接在输入之前加上反转的代码即可,即上面加注释的位置

优点:编程简单,无需改架构

缺点:运算量大,会占用更多时间

方法2 

将上述代码改为向右移动,这样做之后输出也会自动反转,如需正向则需反转回来(这个运算量小)

注意:需要与寄存器的低8位进行异或(32位就是最低的8位)

例子代码

u16 CRC16_ModBus(u8 * Dat, int Len)
{
	u16 Poly = CRC_Reverse_16(0x8005);
	u16 CRC = 0xffff;
	u8 Zj = 0;

	for (int i = 0; i < Len; i++)
	{
		CRC ^= Dat[i];
		for (int j = 0; j < 8; j++)
		{
			if (CRC & 0x0001)
			{
				CRC >>= 1;
				CRC ^= Poly;
			}
			else
			{
				CRC >>= 1;
			}
		}
	}
	return (CRC ^ 0x0000);
}

这个是用来计算MODBUS的CRC校验的

即输入输出均反转

查表法

上述计算过程运算量相等较大,因为每移动一位均需要计算一次异或操作,适合上位机使用

下位机还是建议使用查表法,如果将1Byte的数据做成表,只需要之前的1/8的计算量

原理

异或操作的交换律和结合律

将1Byte数据直接进行消除,直接计算的原理类似,也是将异或值提前计算,只是在程序开跑之前就计算了,这个计算值就是表

表的生成

使用表是以空间换时间的操作方式

为了便于计算出表,我们将初始值设为0(也可以不为全0,只不过需要多一步异或而已,与输入的那Byte数据进行异或的部分必须为0),输入0-0xFF的1Byte数据进行CRC,输出值为表

生成MODBUS的表的例程

u16 CRC16_ModBus1(u8 Dat)
{
	u16 Poly = CRC_Reverse_16(0x8005);
	u16 CRC = 0xffff;
	u8 Zj = 0;

	for (int i = 0; i < Len; i++)
	{
		CRC ^= Dat[i];
		for (int j = 0; j < 8; j++)
		{
			if (CRC & 0x0001)
			{
				CRC >>= 1;
				CRC ^= Poly;
			}
			else
			{
				CRC >>= 1;
			}
		}
	}
	return (CRC ^ 0x0000);
}
int main()
{
    u8 i=0;
    for(i=0;i<=0xFF;i++)
    {
        printf("%X",CRC16_ModBus1(i));
        
    }
}

查表法代码

这是生成的表

const u16 T[256] =
{
	0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440,0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841,0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40,0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641,0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441,0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840,0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41,0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640,0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441,0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840,0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640,0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241,0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440,0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841,0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641,0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040
};

查表的代码和直接计算类似,只需要注意两点

  1. 每次移动8位
  2. 移动之前要与数据进行异或操作来生成表的序号(移动异或之后的低位就相当于直接计算的一个循环走完,重新输入数据了,这里是将8位数据直接消除,因此需要生成标号,这个标号仅使用即可,无需放入寄存器中,因为它马上就因为移位丢失了)

MODBUS的查表法代码文章来源地址https://www.toymoban.com/news/detail-743118.html

u16 CRC16_ModBus2(u8 *Dat, int Len)
{
	u16 CRC = 0xffff;//初始值
	u8 Zj;
	for (int i = 0; i < Len ; i++)
	{
		Zj = (CRC & 0xFF)^ Dat[i];//生成标号,一次将低8位数据消除
		CRC >>= 8;
		CRC ^= T[Zj];
	}
	return (CRC);
}

成品

typedef unsigned          char u8;
typedef unsigned short     int u16;

u16 CRC_Reverse_16(u16 Dat)
{
	u16 Res = 0;
	u16 zj = Dat;
	for (int i = 0; i < 16; i++)
	{
		if (zj & 0x8000)
			Res += 1<<i;
		zj <<= 1;
	}
	return Res;
}
u8 CRC_Reverse_8(u8 Dat)
{
	u8 Res = 0;
	u8 zj = Dat;
	for (int i = 0; i < 8; i++)
	{
		if (zj & 0x80)
			Res += 1 << i;
		zj <<= 1;
	}
	return Res;
}
u16 CRC16_ModBus_Forward(u8 * Dat, int Len)
{
	u16 Poly = 0x8005;
	u16 CRC = 0xffff;
	u8 Zj = 0;
	
	for (int i = 0; i < Len; i++)
	{
		Zj = CRC_Reverse_8(Dat[i]);
		CRC ^= (Zj<< 8);
		for (int j = 0; j < 8; j++)
		{
			if (CRC & 0x8000)
			{
				CRC <<= 1;
				CRC ^= Poly;
			}
			else
			{
				CRC <<= 1;
			}
		}
	}
	return (CRC_Reverse_16(CRC)^0x0000);
}
u16 CRC16_ModBus_Reverse(u8 * Dat, int Len)
{
	u16 Poly = CRC_Reverse_16(0x8005);
	u16 CRC = 0xffff;
	u8 Zj = 0;

	for (int i = 0; i < Len; i++)
	{
		CRC ^= Dat[i];
		for (int j = 0; j < 8; j++)
		{
			if (CRC & 0x0001)
			{
				CRC >>= 1;
				CRC ^= Poly;
			}
			else
			{
				CRC >>= 1;
			}
		}
	}
	return (CRC ^ 0x0000);
}
const u16 T[256] =
{
	0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440,0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841,0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40,0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641,0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441,0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840,0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41,0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640,0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441,0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840,0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640,0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241,0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440,0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841,0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641,0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040
};
u16 CRC16_ModBus_Table(u8 *Dat, int Len)
{
	u16 CRC = 0xffff;
	u8 Zj;
	for (int i = 0; i < Len ; i++)
	{
		Zj = (CRC & 0xFF)^ Dat[i];
		CRC >>= 8;
		CRC ^= T[Zj];
	}
	return (CRC);
}

到了这里,关于【基础知识】CRC(循环冗余校验)直接计算和查表法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • JAVA获取CRC(即循环冗余校验)

    前言:网上查了很多,CRC计算时,除数长度太长无法计算,所以写此文章。希望大家喜欢!     CRC(Cyclic Redundancy Check),即循环冗余校核,是一种根据网络数据包或电脑文件等数据产生简短固定位数 校核码 的快速算法。      CRC校验本质上是选取一个合适的 除数 ,要进行校

    2024年02月06日
    浏览(30)
  • 从原理到代码理解CRC循环冗余校验

    概述:本文详细介绍了CRC循环冗余计算的数学原理,算法中使用的参数说明,并以Modbus协议中的CRC-16算法为例,进行手算验证,同时提供LabVIEW和C语言的直接计算CRC-16 值的代码以及C的查表计算CRC-16代码和代码原理的说明。 初次接触CRC校验是因为项目需要上位机软件来记录P

    2024年02月04日
    浏览(31)
  • CRC循环冗余校验 (Cyclic Redundancy Check) 原理/电路实现/Verilog实现

    目录 1 什么是CRC循环冗余校验? 2 CRC校验的原理 2.1 多项式表示 2.2 模二 多项式除法 2.3 传输端  2.4 接收端 3 CRC码的产生 3.1 产生CRC码步骤 3.2 Verilog实现 4 电路实现原理—线性反馈移位寄存器 4.1 循环移位寄存器结构 4.2 最大长度移位寄存器  4.3 多项式除法电路(线性反馈移位

    2024年02月04日
    浏览(28)
  • CRC冗余校验的原理和FPGA实现思路

    CRC校验码,顾名思义是用于 校验 的。它可以用于检测数据传输过程中是否出现错误(某些位,或某几位,或者某块区域位错误),反正 可以知道数据出错了,但是不能纠错 。 CRC校验,本质上是模2除法求余。将发送信息 M 当做被除数,发送方和接收方共同约定一个除数 G

    2024年02月08日
    浏览(50)
  • 【运维知识高级篇】34道Shell编程练习题及答案(从基础到实战:基础+计算+判断+循环+控制与数组+实战进阶)

    ​本篇文章几乎涵盖了绝大部分的Shell语法练习,用一个个实战练习,巩固Shell的学习,话不多说,直接开始。 练习1:按照时间生成文件\\\"2018-05-22.log\\\"将每天的磁盘使用状态写入到对应日期的文件 练习2:统计Nginx日志中每个IP的访问量有多少,日志格式如下 练习3:写一个脚本

    2024年02月14日
    浏览(50)
  • 题解校验码—CRC循环校验码与海明校验码

    一个编码系统的码距是 任意两个码字 的最小距离。 例如个编码系统采用三位长度的二进制编码,若该系统有四种编码分别为:000,011,100,111,此编码系统中000与111的码距为3;011与000的码距为2;011与111的码距为1,则该编码系统的码距为1。 码距计算方法:两个编码按位异

    2024年02月04日
    浏览(32)
  • 计算机网络----CRC冗余码的运算

    冗余码是用于在数据链路层的通信链路和传输数据过程中可能会出错的一种检错编码方法(检错码)。 原理:发送发把数据划分为组,设每组 K 个比特,在其后添加供差错检验用的 n 位冗余码,( K+n )比特一起发送。 过程: 注意: 模二除法运算的过程相当于 异或 。 因为

    2024年02月12日
    浏览(39)
  • 解决灵科路由循环冗余校验错误排除操作指南

    本文为大家介绍的是由于路由器循环冗余校验错误,所造成POS故障的解决办法,循环冗余校验,其特点是:检错能力极强,开销小,易于用编码器及检测电路实现,从其检错能力来看,它所不能发现的错误的几率非常低。 一、组网环境 在灵科路由器的组网环境中,在个点使用

    2024年02月05日
    浏览(35)
  • JavaSE核心基础-循环-知识点

    1.循环概念 循环是在满足条件的情况下,反复的做同一件事。 Java语言中的循环语句有三种,分别是for语句、while语句和do-while语句。程序中需要循环处理时,程序员要根据实际问题,选择适当的循环语句。解决循环问题时一定要找到循环条件和循环操作。 2.for循环语句格式

    2024年02月22日
    浏览(30)
  • 探索MATLAB世界:掌握基础知识与实用技能(1. MATLAB环境与基本操作 2. 数据类型与变量 3. 条件与循环,1. 数据分析与统计 2. 图像处理与计算机视觉 3. 信号处理与控制系统)

    欢迎阅读本篇博客,我们将深入探讨MATLAB语言的基础知识与实用技能,旨在帮助初学者、初中级MATLAB程序员以及在校大学生系统地掌握这门强大的科学计算与数据可视化工具。 一、MATLAB的基础知识 1. MATLAB环境与基本操作 2. 数据类型与变量 3. 条件与循环 二、MATLAB的实用技能

    2024年03月12日
    浏览(49)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包