CRC校验原理及其使用
目录
何为CRC
为什么需要校验
为什么是CRC
CRC的缺点
目录
何为CRC
为什么需要校验
为什么是CRC
CRC的缺点
如何进行CRC校验
校验标准式是什么玩意?
常见的CRC校验
CRC校验计算过程
CRC校验代码参考
代码解读
生成CRC8校验表的代码
CRC检验网站
如何进行CRC校验
校验标准式是什么玩意?
常见的CRC校验
生成CRC校验的代码
何为CRC
CRC,循环冗余校验码,本质就是一个校验码,用于检测通讯数据是否正确。常见的还有奇偶校验,校验和(所有数据相加)、LRC校验、异或校验、MD5校验等。
为什么需要校验
数据传输过程中,由于其他干扰的存在,有概率会出现数据传输错误的现象。为了识别接收的数据是否正确,我们需要在数据传输完成后增加一个校验数据,接收端按照校验规则计算出(所接收数据的)校验码,与发送端的校验码进行对比,两个校验码不相等说明数据传输出错,反之,通讯正确。
为什么是CRC
数据校验本身没有优略之分,CRC可以很大程度上识别出数据传输。举个不恰当的例子,奇偶校验可能识别出百分之五十的数据传输错误,CRC可能可以识别到百分之九十;检测的成本也比较低(占用的资源相对少),从性价比上来看,CRC是个很好的校验法。
CRC的缺点
循环冗余校验法检验不出来的错的情况:收到的位串虽然是错误的,但是恰巧能被生成多项式整除,这个时候检测不出来
如何进行CRC校验
在了解了CRC是什么之后,还需要了解怎么做。
CRC校验的逻辑是将需要校验的数据与校验标准式进行异或运算,也就是模2除法。不懂什么叫模2除法根本没关系,只要理解什么叫异或运算就可以了。
校验标准式是什么玩意?
本质就是一个用于异或的值。这个东西的存在的意义就是规范接收端与发送端的异或值。理论上,式子越复杂,通讯错误的识别率就越高。下面的表格就是常见的CRC校验标准式,摘自链接
有没有被标准式的复杂给震慑住?不用怕,它们就是纸老虎,看懂后,你会发现它们连纸老虎都不如。
式子中x的次方只是标位数,后面的计算根本就不需要考虑。
比如 对应的异或值就是 0001 0011 也就是0x13;
是第四位,
是第一位,1就是第零位(
)
再看一个
,对应的异或值就是 0010 1001 也就是0x29;
是第五位,
是第三位,1就是第零位(
)
看了几个例子清楚怎么找异或值了吧,这个标准式就是为了得到异或值,没有其他的用处了,是不是很简单。
还要注意的一点就是,需要根据CRC校验位数对异或值进行取低位,舍高位。
比如 ,对应的异或值是1 0011 0001,也就是0x131,但我们用的是CRC8的话,只需要取低八位,也就是0x31就行;
常见的CRC校验
常见的CRC校验有CRC8,CRC16,CRC32,其中CRC8与CRC16用于通讯在嵌入式行业比较常用。
目前我就掌握了CRC8的使用,我看到CRC16,CRC32是只取低8位进行计算,原理上可能也差不多吧。
CRC校验计算过程
摘自计算过程
计算过程就是进行异或运算,首先先对数据进行补0处理(CRC8就补8位),然后进行异或计算。计算过程了解一下就行,写程序又不用你来手算,了解原理就好。
CRC校验代码参考
代码取自 原代码地址
#include "crcLib.h"/* Name: CRC-4/ITU x4+x+1* Poly: 0x03* Init: 0x00* Refin: True* Refout: True* Xorout: 0x00* Note:*/
uint8_t crc4_itu(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0x0C;// 0x0C = (reverse 0x03)>>(8-4)elsecrc = (crc >> 1);}}return crc;
}/* Name: CRC-5/EPC x5+x3+1* Poly: 0x09* Init: 0x09* Refin: False* Refout: False* Xorout: 0x00* Note:*/
uint8_t crc5_epc(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0x48; // Initial value: 0x48 = 0x09<<(8-5)while(length--){crc ^= *data++; // crc ^= *data; data++;for ( i = 0; i < 8; i++ ){if ( crc & 0x80 )crc = (crc << 1) ^ 0x48; // 0x48 = 0x09<<(8-5)elsecrc <<= 1;}}return crc >> 3;
}/* Name: CRC-5/ITU x5+x4+x2+1* Poly: 0x15* Init: 0x00* Refin: True* Refout: True* Xorout: 0x00* Note:*/
uint8_t crc5_itu(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0x15;// 0x15 = (reverse 0x15)>>(8-5)elsecrc = (crc >> 1);}}return crc;
}/* Name: CRC-5/USB x5+x2+1* Poly: 0x05* Init: 0x1F* Refin: True* Refout: True* Xorout: 0x1F* Note:*/
uint8_t crc5_usb(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0x1F; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0x14;// 0x14 = (reverse 0x05)>>(8-5)elsecrc = (crc >> 1);}}return crc ^ 0x1F;
}/* Name: CRC-6/ITU x6+x+1* Poly: 0x03* Init: 0x00* Refin: True* Refout: True* Xorout: 0x00* Note:*/
uint8_t crc6_itu(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0x30;// 0x30 = (reverse 0x03)>>(8-6)elsecrc = (crc >> 1);}}return crc;
}/* Name: CRC-7/MMC x7+x3+1* Poly: 0x09* Init: 0x00* Refin: False* Refout: False* Xorout: 0x00* Use: MultiMediaCard,SD,ect.*/
uint8_t crc7_mmc(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for ( i = 0; i < 8; i++ ){if ( crc & 0x80 )crc = (crc << 1) ^ 0x12; // 0x12 = 0x09<<(8-7)elsecrc <<= 1;}}return crc >> 1;
}/* Name: CRC-8 x8+x2+x+1* Poly: 0x07* Init: 0x00* Refin: False* Refout: False* Xorout: 0x00* Note:*/
uint8_t crc8(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for ( i = 0; i < 8; i++ ){if ( crc & 0x80 )crc = (crc << 1) ^ 0x07;elsecrc <<= 1;}}return crc;
}/* Name: CRC-8/ITU x8+x2+x+1* Poly: 0x07* Init: 0x00* Refin: False* Refout: False* Xorout: 0x55* Alias: CRC-8/ATM*/
uint8_t crc8_itu(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for ( i = 0; i < 8; i++ ){if ( crc & 0x80 )crc = (crc << 1) ^ 0x07;elsecrc <<= 1;}}return crc ^ 0x55;
}/* Name: CRC-8/ROHC x8+x2+x+1* Poly: 0x07* Init: 0xFF* Refin: True* Refout: True* Xorout: 0x00* Note:*/
uint8_t crc8_rohc(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0xFF; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0xE0; // 0xE0 = reverse 0x07elsecrc = (crc >> 1);}}return crc;
}/* Name: CRC-8/MAXIM x8+x5+x4+1* Poly: 0x31* Init: 0x00* Refin: True* Refout: True* Xorout: 0x00* Alias: DOW-CRC,CRC-8/IBUTTON* Use: Maxim(Dallas)'s some devices,e.g. DS18B20*/
uint8_t crc8_maxim(uint8_t *data, uint16_t length)
{uint8_t i;uint8_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; i++){if (crc & 1)crc = (crc >> 1) ^ 0x8C; // 0x8C = reverse 0x31elsecrc >>= 1;}}return crc;
}/* Name: CRC-16/IBM x16+x15+x2+1* Poly: 0x8005* Init: 0x0000* Refin: True* Refout: True* Xorout: 0x0000* Alias: CRC-16,CRC-16/ARC,CRC-16/LHA*/
uint16_t crc16_ibm(uint8_t *data, uint16_t length)
{uint8_t i;uint16_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0xA001; // 0xA001 = reverse 0x8005elsecrc = (crc >> 1);}}return crc;
}/* Name: CRC-16/MAXIM x16+x15+x2+1* Poly: 0x8005* Init: 0x0000* Refin: True* Refout: True* Xorout: 0xFFFF* Note:*/
uint16_t crc16_maxim(uint8_t *data, uint16_t length)
{uint8_t i;uint16_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0xA001; // 0xA001 = reverse 0x8005elsecrc = (crc >> 1);}}return ~crc; // crc^0xffff
}/* Name: CRC-16/USB x16+x15+x2+1* Poly: 0x8005* Init: 0xFFFF* Refin: True* Refout: True* Xorout: 0xFFFF* Note:*/
uint16_t crc16_usb(uint8_t *data, uint16_t length)
{uint8_t i;uint16_t crc = 0xffff; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0xA001; // 0xA001 = reverse 0x8005elsecrc = (crc >> 1);}}return ~crc; // crc^0xffff
}/* Name: CRC-16/MODBUS x16+x15+x2+1* Poly: 0x8005* Init: 0xFFFF* Refin: True* Refout: True* Xorout: 0x0000* Note:*/
uint16_t crc16_modbus(uint8_t *data, uint16_t length)
{uint8_t i;uint16_t crc = 0xffff; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0xA001; // 0xA001 = reverse 0x8005elsecrc = (crc >> 1);}}return crc;
}/* Name: CRC-16/CCITT x16+x12+x5+1* Poly: 0x1021* Init: 0x0000* Refin: True* Refout: True* Xorout: 0x0000* Alias: CRC-CCITT,CRC-16/CCITT-TRUE,CRC-16/KERMIT*/
uint16_t crc16_ccitt(uint8_t *data, uint16_t length)
{uint8_t i;uint16_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0x8408; // 0x8408 = reverse 0x1021elsecrc = (crc >> 1);}}return crc;
}/* Name: CRC-16/CCITT-FALSE x16+x12+x5+1* Poly: 0x1021* Init: 0xFFFF* Refin: False* Refout: False* Xorout: 0x0000* Note:*/
uint16_t crc16_ccitt_false(uint8_t *data, uint16_t length)
{uint8_t i;uint16_t crc = 0xffff; //Initial valuewhile(length--){crc ^= (uint16_t)(*data++) << 8; // crc ^= (uint6_t)(*data)<<8; data++;for (i = 0; i < 8; ++i){if ( crc & 0x8000 )crc = (crc << 1) ^ 0x1021;elsecrc <<= 1;}}return crc;
}/* Name: CRC-16/X25 x16+x12+x5+1* Poly: 0x1021* Init: 0xFFFF* Refin: True* Refout: True* Xorout: 0XFFFF* Note:*/
uint16_t crc16_x25(uint8_t *data, uint16_t length)
{uint8_t i;uint16_t crc = 0xffff; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0x8408; // 0x8408 = reverse 0x1021elsecrc = (crc >> 1);}}return ~crc; // crc^Xorout
}/* Name: CRC-16/XMODEM x16+x12+x5+1* Poly: 0x1021* Init: 0x0000* Refin: False* Refout: False* Xorout: 0x0000* Alias: CRC-16/ZMODEM,CRC-16/ACORN*/
uint16_t crc16_xmodem(uint8_t *data, uint16_t length)
{uint8_t i;uint16_t crc = 0; // Initial valuewhile(length--){crc ^= (uint16_t)(*data++) << 8; // crc ^= (uint16_t)(*data)<<8; data++;for (i = 0; i < 8; ++i){if ( crc & 0x8000 )crc = (crc << 1) ^ 0x1021;elsecrc <<= 1;}}return crc;
}/* Name: CRC-16/DNP x16+x13+x12+x11+x10+x8+x6+x5+x2+1* Poly: 0x3D65* Init: 0x0000* Refin: True* Refout: True* Xorout: 0xFFFF* Use: M-Bus,ect.*/
uint16_t crc16_dnp(uint8_t *data, uint16_t length)
{uint8_t i;uint16_t crc = 0; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0xA6BC; // 0xA6BC = reverse 0x3D65elsecrc = (crc >> 1);}}return ~crc; // crc^Xorout
}/* Name: CRC-32 x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1* Poly: 0x4C11DB7* Init: 0xFFFFFFF* Refin: True* Refout: True* Xorout: 0xFFFFFFF* Alias: CRC_32/ADCCP* Use: WinRAR,ect.*/
uint32_t crc32(uint8_t *data, uint16_t length)
{uint8_t i;uint32_t crc = 0xffffffff; // Initial valuewhile(length--){crc ^= *data++; // crc ^= *data; data++;for (i = 0; i < 8; ++i){if (crc & 1)crc = (crc >> 1) ^ 0xEDB88320;// 0xEDB88320= reverse 0x04C11DB7elsecrc = (crc >> 1);}}return ~crc;
}/* Name: CRC-32/MPEG-2 x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1* Poly: 0x4C11DB7* Init: 0xFFFFFFF* Refin: False* Refout: False* Xorout: 0x0000000* Note:*/
uint32_t crc32_mpeg_2(uint8_t *data, uint16_t length)
{uint8_t i;uint32_t crc = 0xffffffff; // Initial valuewhile(length--){crc ^= (uint32_t)(*data++) << 24;// crc ^=(uint32_t)(*data)<<24; data++;for (i = 0; i < 8; ++i){if ( crc & 0x80000000 )crc = (crc << 1) ^ 0x04C11DB7;elsecrc <<= 1;}}return crc;
}
代码解读
代码其实没什么难度,计算需要三个变量,异或值,异或初始值,数组(包含了数组名+长度),两个循环(字符串循环,位循环)。异或值与异或初始值由选取的协议确定,数组就是要进行校验的数据。
crc ^= *data++; //是先取*data[0]进行异或,运行完后才*data+1;
生成CRC8校验表的代码
取自生成CRC8校验表
//到https://c.runoob.com/compile/11/中生成
//或者 http://c.jsrun.net/
#include <stdio.h>/* 本地调用此函数计算出所有 CRC-8 校验码 */
//正序
unsigned char Cal_CRC8(const unsigned char data)
{unsigned char i, crc;crc = data;/* 数据往左移了8位,需要计算8次 */for (i = 8; i > 0; i--) {/* 判断最高位是否为1 */if(crc & 0x80) {/* 最高位为1,不需要异或,往左移一位,然后与0x2f异或 *//* 0x12f(多项式:x8 + x5 + x3 + x2 + x + 1, 100101111),最高位不需要异或,直接去掉 *///按照实际情况,更改这个部分crc = (crc << 1) ^ 0x2f;} else {/* 最高位为0时,不需要异或,整体数据往左移一位 */crc = (crc << 1);}}return crc;
}//需要反序的就调用这个函数
unsigned char cal_table_low_first(unsigned char value)
{unsigned char i, crc;crc = value;
/* 同样需要计算8次 */for (i=8; i>0; --i){ if (crc & 0x01) /* 反序异或变成判断最低位是否为1 *//* 数据变成往右移位了 *//* 计算的多项式从0x31(0011 0001)变成了0x8C (1000 1100) */
/* 多项式值,原来的最高位变成了最低位,原来的最低位变成最高位,8位数据高低位交换一下位置 */crc = (crc >> 1) ^ 0x8C;elsecrc = (crc >> 1);}return crc;
}int main()
{unsigned char j = 0;int count = 1;for(unsigned int i = 0; i < 256; i++) {j = Cal_CRC8(i);if(count % 16) {count++;printf("0x%x, ", j);} else {count++;printf("0x%x,\\n", j);}}return 0;
}
CRC检验网站
IP33.COM
使用这个网站的时候需要注意一下是正序和反序。
就先这样吧,有发现不正确的地方,还望指出改正