CRC8/CRC16/CRC32最全总结
CRC8/CRC16/CRC32最全总结
本文首发于“嵌入式软件实战派”。
循环冗余校验(英语:Cyclic redundancy check,通称“CRC”)是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。
Wikipedia
一句话:CRC是将数据计算出散列的方式,一般用于校验数据的完整性。它具有简单、执行效率高等特点。当然,你可以类比于Checksum,但比Checksum复杂些,防碰撞性更好些。
▍CRC的原理
CRC是基于除法的。实际的输入数据会被解释为一个长二进制位流(除数),再将其除以另一个固定二进制数(除数,即多项式)。该除法的其余部分就是校验值。
但是,现实要复杂一些。二进制数(除数和除数)不被视为普通整数值,而是被视为二进制多项式,其中实际位用作系数。
输入数据,看做一串二进制流,用多项式的方式表示为g(x),而除数是国际标准上的多项式,用h(x)表示。通过g(x)和h(x)做除法,即两者的异或运算,得出的结果,就是我们所说的CRC运算检验结果。
那么,这里有两个疑问:
问题1:多项式和二进制是什么关系?
例如,1x3 + 0x2 + 1x + 1可以表示为1011b,或者1011b表示为1x3 + 0x2 + 1x + 1,其中是0的位,即以0为系数乘以该项。
问题2:除法怎么做?
本质就是在做异或运算
我们来看看一个数据串11010011101100除以x3 + x + 1是怎样进行的?
注意,根据CRC长度,需要将这个数据串补0
整个运算过程是这样的:
实际上就是逐个bit移位做异或。
详见:https://en.wikipedia.org/wiki/Cyclic_redundancy_check
▍CRC的概念
实际上,人们根据不同的需要,做了很多种计算方式,主要差别在于CRC长度、多项式、初始值、结果是否需要异或、是否需要翻转等等。
首先,来看看几个概念:
Length: CRC的长度(按bit算,如8,16,32)
Name: CRC的名字,让人一看就知道这是哪种CRC
Polinomial: 多项式,通过该多项式来计算CRC
InitialValue: CRC的初始值
FinalXorValue: CRC结果做异或运算的值
InputReflected: 指示输出是否需要翻转
OutputReflected: 指示输出是否需要翻转
我将网上搜到的所有的CRC标准分类做了个汇总:
从上面的CRC名称可以看出,不同的算法是有不同用途的,这是国际常规的用法定义,其实如果是用户自己用也没特别要求,可以自由点。
▍CRC的实现
1. 根据多项式二进制数,逐位做异或
按照前面章节的“CRC原理”来做C语言实现,就通过数据移位和异或算得。以CRC8为例,代码如下
#define CRC_POLYNOMIAL_8 0x0C
uint8 crc_8(uint8 crc, uint8* pdata, uint32 len)
{for (uint32 i = 0; i < len; i++){crc ^= pdata[i];for (uint8 j = 0; j < 8; j++){if ((crc & 0x80u) > 0){crc = ( (uint8)(crc << 1u) ) ^ CRC_POLYNOMIAL_8;}else{crc <<= 1u;}}}return crc;
}
这个代码中有个if ((crc & 0x80u) > 0)要解释下,因为任意数与0做异或,结果是其本身。所以,数据为0时,不做异或。同理,CRC16、CRC32也可以按这种方法做计算,只是多项式和数据位数不一样而已,在此不累述了。
按移位做异或的方法,有个缺点,就是用了两层循环,效率不是那么的高,在嵌入式软件中,不是那么受欢迎。
那需要怎么优化算法呢?那就将多项式预先算个表出来,查表吧。
2. 将多项式(Polynomial)生成一个256长度的表,用查表法实现
2.1 多项式生成的表
在开始这个话题之前,我们得回个头来看看那几个概念中有个InputReflected和OutputReflected。干啥呢,吃饱没事干啊,算就算,还要翻转?呵呵!
其实,通过多项式生成表就可以生成正序和逆序,就是为了应付这个“翻转”的。举个例子以CRC8_ITU(Polynomial=0x07)看看这个表:
正序表为
const unsigned char crc8_itu__table[] =
{0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, // ...... 省略部分0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3,
};
逆序表为
const unsigned char crc8_itu_reversed_table[] =
{0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, // ...... 省略部分0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF,
};
2.2 通用查表法
网上有很多种查表发计算CRC,不尽相同。于是我做了个通用版,满足各种算法,其中用了宏定义的奇技淫巧。直接上代码:
#define DEF_CRC_FUNC(func, width, crcTable) \
uint##width func(const uint8 *pdata, int len, \
uint##width initial, uint##width finalXor, \
BOOL inputReflected, BOOL resultReflected) \
{ \uint##width crc = initial; \uint##width temp1 = 0, temp2 = 0, pos = 0; \const uint##width *pTable = crcTable; \for(int i = 0; i < len; i++) \{ \uint##width curByte = pdata[i]; \if(inputReflected) \{ \curByte = Reflect8(pdata[i]); \} \/* update the MSB of crc value with next input byte */ \temp1 = (crc ^ (curByte << (width - 8))); \/* this MSB byte value is the index into the lookup table */ \pos = (temp1 >> (width - 8)) & 0xFF; \/* shift out this index */ \temp2 = (temp1 << 8); \/* XOR-in remainder from lookup table using the calculated index */ \crc = (temp2 ^ pTable[pos]); \} \if(resultReflected) \{ \crc = Reflect##width(crc); \} \return (crc ^ finalXor); \
}
怎么用?
先定义,即定义对于位数算法的函数,通过预编译生成:
DEF_CRC_FUNC(gen_crc8, 8, crc8__table);
DEF_CRC_FUNC(gen_crc16_maxim, 16, crc16_maxim__table);
DEF_CRC_FUNC(gen_crc16_a, 16, crc16_a__table);
DEF_CRC_FUNC(gen_crc32_jamcrc, 32, crc32_jamcrc__table);
再调用,测试可以在main函数调用:
int main(void)
{const uint8 buf[6] = "123456";uint8 crc8 = gen_crc8(buf, 6, 0x00, 0x00, 0, 0);uint16 crc16_maxim = gen_crc16_maxim(buf, 6, 0x0000, 0xFFFF, 1, 1);uint16 crc16_a = gen_crc16_a(buf, 6, 0xC6C6, 0x0000, 1, 1);uint32 crc32_jamcrc = gen_crc32_jamcrc(buf, 6, 0xFFFFFFFF, 0x00000000, 1, 1);printf("crc8: %X\n", crc8);printf("crc16_maxim: %X\n", crc16_maxim);printf("crc16_a: %X\n", crc16_a);printf("crc32_jamcrc: %X\n", crc32_jamcrc);
}
这里还是有个问题,就那个“翻转”的问题,每个byte都做翻转,很浪费CPU资源啊。作为“优秀的”嵌入式软件开发人员,我们是追求卓越的算法,于是乎,就有了以下方式:
(1)对于InputReflected和OutputReflected都是False的,我们用正序表,以CRC8_ITU为例:
/*
CRC Info:
Name Polynomial Initial FinalXor InputReflected ResultReflected
CRC8_ITU 0x07 0x00 0x55 false false
*/
unsigned char crc8_itu(unsigned char crc, const unsigned char* buf, unsigned int len)
{for(unsigned int i = 0; i < len; i++){crc = crc8_itu__table[crc ^ buf[i]];}return crc^0x55;
}
(2)对于InputReflected和OutputReflected都是True的,我们用逆序表,以CRC8_EBU为例:
/*
CRC Info:
Name Polynomial Initial FinalXor InputReflected ResultReflected
CRC8_EBU 0x1D 0xFF 0x00 true true
*/
unsigned int crc8_ebu(unsigned int crc, const unsigned char* buf, unsigned int len)
{for(unsigned int i = 0; i < len; i++){crc = crc8_ebu_reversed_table[crc ^ buf[i]];}return crc^0x00;
}
▍CRC的源码
说了这么多,以上都是举例而已(其实直接将上面代码copy过去验证也是木有问题的,不要怀疑),上代码啊
Talk is cheap. Show me the code.
Linus Torvalds
我将上面表格中提到的所有CRC算法,都coding了,就像下图的这些
Name Polynomial Initial FinalXor InputReflected ResultReflected
CRC8 0x07 0x00 0x00 false false
CRC8_SAE_J1850 0x1D 0xFF 0xFF false false
CRC8_SAE_J1850_ZERO 0x1D 0x00 0x00 false false
CRC8_8H2F 0x2F 0xFF 0xFF false false
CRC8_CDMA2000 0x9B 0xFF 0x00 false false
CRC8_DARC 0x39 0x00 0x00 true true
CRC8_DVB_S2 0xD5 0x00 0x00 false false
CRC8_EBU 0x1D 0xFF 0x00 true true
CRC8_ICODE 0x1D 0xFD 0x00 false false
CRC8_ITU 0x07 0x00 0x55 false false
CRC8_MAXIM 0x31 0x00 0x00 true true
CRC8_ROHC 0x07 0xFF 0x00 true true
CRC8_WCDMA 0x9B 0x00 0x00 true true
CRC16_CCIT_ZERO 0x1021 0x0000 0x0000 false false
CRC16_ARC 0x8005 0x0000 0x0000 true true
CRC16_AUG_CCITT 0x1021 0x1D0F 0x0000 false false
CRC16_BUYPASS 0x8005 0x0000 0x0000 false false
CRC16_CCITT_FALSE 0x1021 0xFFFF 0x0000 false false
CRC16_CDMA2000 0xC867 0xFFFF 0x0000 false false
CRC16_DDS_110 0x8005 0x800D 0x0000 false false
CRC16_DECT_R 0x0589 0x0000 0x0001 false false
CRC16_DECT_X 0x0589 0x0000 0x0000 false false
CRC16_DNP 0x3D65 0x0000 0xFFFF true true
CRC16_EN_13757 0x3D65 0x0000 0xFFFF false false
CRC16_GENIBUS 0x1021 0xFFFF 0xFFFF false false
CRC16_MAXIM 0x8005 0x0000 0xFFFF true true
CRC16_MCRF4XX 0x1021 0xFFFF 0x0000 true true
CRC16_RIELLO 0x1021 0xB2AA 0x0000 true true
CRC16_T10_DIF 0x8BB7 0x0000 0x0000 false false
CRC16_TELEDISK 0xA097 0x0000 0x0000 false false
CRC16_TMS37157 0x1021 0x89EC 0x0000 true true
CRC16_USB 0x8005 0xFFFF 0xFFFF true true
CRC16_A 0x1021 0xC6C6 0x0000 true true
CRC16_KERMIT 0x1021 0x0000 0x0000 true true
CRC16_MODBUS 0x8005 0xFFFF 0x0000 true true
CRC16_X_25 0x1021 0xFFFF 0xFFFF true true
CRC16_XMODEM 0x1021 0x0000 0x0000 false false
CRC32 0x04C11DB7 0xFFFFFFFF 0xFFFFFFFF true true
CRC32_BZIP2 0x04C11DB7 0xFFFFFFFF 0xFFFFFFFF false false
CRC32_C 0x1EDC6F41 0xFFFFFFFF 0xFFFFFFFF true true
CRC32_D 0xA833982B 0xFFFFFFFF 0xFFFFFFFF true true
CRC32_MPEG2 0x04C11DB7 0xFFFFFFFF 0x00000000 false false
CRC32_POSIX 0x04C11DB7 0x00000000 0xFFFFFFFF false false
CRC32_Q 0x814141AB 0x00000000 0x00000000 false false
CRC32_JAMCRC 0x04C11DB7 0xFFFFFFFF 0x00000000 true true
CRC32_XFER 0x000000AF 0x00000000 0x00000000 false false
举个例子,CRC32_BZIP2.h文件是的算法是这样的:
真以为我一个一个写的吗,哈哈哈,不是。但我肯定不是在网上一个一个抄的,网上也找不到这么多。
还是那句话:机器能干的事,为啥还要人来干!
我找到了个规律,写了个脚本生成的,多项式的正序表、逆序表是生成的,连代码,我都是用脚本生成的。
这里篇幅有限,就不将代码全部贴在这了,如果你对这些算法感兴趣,关注“嵌入式软件实战派”,聊天界面输入并发送“CRC”获得下载链接,或者点击[链接]下载。后续适当的时候,我还会将这个生成源文件的脚本也分享给大家。
为了验证其可靠性,我还做了个测试代码(篇幅有限,下面截取的代码已省略了部分):
// TEST:Caculate the string "123456", result list below with different crc models:crc_assert(0xfd == crc8 (0x00, "123456", 6), "crc8" );// ...... 省略部分crc_assert(0x57 == crc8_rohc (0xFF, "123456", 6), "crc8_rohc" );crc_assert(0xab == crc8_wcdma (0x00, "123456", 6), "crc8_wcdma" );crc_assert(0x20e4 == crc16_ccit_zero (0x0000, "123456", 6), "crc16_ccit_zero" );crc_assert(0x29e4 == crc16_arc (0x0000, "123456", 6), "crc16_arc" );// ...... 省略部分crc_assert(0x32e4 == crc16_modbus (0xFFFF, "123456", 6), "crc16_modbus" );crc_assert(0xe672 == crc16_x_25 (0xFFFF, "123456", 6), "crc16_x_25" );crc_assert(0x20e4 == crc16_xmodem (0x0000, "123456", 6), "crc16_xmodem" );crc_assert(0x0972d361 == crc32 (0xFFFFFFFF, "123456", 6), "crc32" );// ...... 省略部分crc_assert(0xf036f1c2 == crc32_xfer (0x00000000, "123456", 6), "crc32_xfer" );
如果你怀疑我“监守自盗”,你还可以在网上找个在线计算的方式来验证算法的正确性,如:https://crccalc.com/
如果有其他想法和建议,请留言,我们一同探讨。
如果你喜欢我的文章,请扫码关注“嵌入式软件实战派”,我会分享更多技术干货。
CRC8/CRC16/CRC32最全总结相关推荐
- crc16,crc32校验
crc16,crc32校验 -supermgr crc16,crc32校验是计算机中经常用到的东西, 本文介绍的程序,使用查表法来进行crc16,crc32的校验. //file: sp_crc.h ...
- CRC-16/CRC-32 程序代码
CRC-16/CRC-32 程序代码 不久前写一程序时要用到 CRC-16 ,但找来找去只在 UDDF 里找到一个 Delphi 的 CRC-32 程序代码,而且是用查表法,虽然说查表法速度快,但 2 ...
- CRC8 CRC16 查表法
转自:http://blog.chinaunix.net/uid-14114479-id-1988515.html CRC8查表法 /********************************* ...
- 最详细易懂的CRC-16校验原理(附源程序)
from:http://www.openhw.org/chudonganjin/blog/12-08/230184_515e6.html 最详细易懂的CRC-16校验原理(附源程序) 1.循环校验码( ...
- Redis CRC16校验 1.原理概述
CRC16校验 1.原理概述 来自简书:CRC16算法 来自github.io带图解说:CRC循环冗余校验 来自之乎专栏:CRC-16校验原理与说明 来自简书:CRC16算法 简书摘要: CRC16 ...
- 大厂面试八股文——计算机网络
文章目录 RESTful REST的指导原则 资源 资源方法 REST和HTTP不一样!! ip地址.子网掩码. 单机服务器最大并发的TCP连接数到底是多少 带外数据和TCP紧急指针 linux主机同 ...
- hex文件格式解析_玩转Hex文件
00 关于Hex Hex文件格式(本文讲的是Intel Hex)是好多好多年以前定义的格式,解析这文件的工具网上搜出来的也是一大摞一大摞的.记住,我们就别瞎折腾自己写一个了哦.我们要学会站在巨人的肩膀 ...
- 嵌入式开发<串口调试工具>
嵌入式开发<串口调试工具> 前言 1,设备参数分类 2,设备参数修改 3,调试工具软件 一.软件界面 二.功能说明 1.串口设置 1)串口选择 2)自动换行 3)接收到文件 4)保存文件 ...
- 十六、基于FPGA的CRC校验设计实现
1,CRC校验 循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保 ...
- arduino笔记34:nRF24l01模块使用 通信地址 数据通道 校验码 PID
最近再arduino中文社区看到了一篇介绍nrf24l01基本原理的帖子,内容感觉蛮不错的,学习一下,记录一下学习笔记. 大部分内容都是Arduino中文社区的帖子,附上自己的一点点体会. 目录 一. ...
最新文章
- java 办公,中文JAVA技术网
- UIKit框架使用总结--看看你掌握了多少
- 临时整形变量溢出的问题
- zcmu-1182(大数相减)
- stm32 led屏控制卡_LED显示屏控制卡解析
- javascript事件处理程序
- 丰田pcs可以关闭吗_别只知道开不坏,现在的丰田还有这些厉害绝招!
- Python 俄罗斯方块, 基于pyqt5实现俄罗斯方块 --pyqt5 进阶
- CSE强契约模式常见问题和应对策略
- 布丁机器人APP响应超时_常见问题解答
- 浅谈微博营销如何吸引流量
- 如何给服务器设置邮件警报。
- 基于jQuery的响应式网站视频插件FitVids.js
- Python库-pandas详解
- angular2 路由ajax,angular2路由切换改变页面title的示例代码
- 二级python试题分值_2020年12月计算机二级各考试科目题型及分值比例
- 免费、可商用的素材网站
- chmod 777 授权无效
- css鼠标经过按钮变色6,像这种鼠标移过去会变色的按钮怎么做的
- 医药研发产业新平台在从化