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 多项式生成的表

在开始这个话题之前,我们得回个头来看看那几个概念中有个InputReflectedOutputReflected。干啥呢,吃饱没事干啊,算就算,还要翻转?呵呵!

其实,通过多项式生成表就可以生成正序和逆序,就是为了应付这个“翻转”的。举个例子以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)对于InputReflectedOutputReflected都是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)对于InputReflectedOutputReflected都是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最全总结相关推荐

  1. crc16,crc32校验

    crc16,crc32校验 -supermgr crc16,crc32校验是计算机中经常用到的东西, 本文介绍的程序,使用查表法来进行crc16,crc32的校验. //file: sp_crc.h ...

  2. CRC-16/CRC-32 程序代码

    CRC-16/CRC-32 程序代码 不久前写一程序时要用到 CRC-16 ,但找来找去只在 UDDF 里找到一个 Delphi 的 CRC-32 程序代码,而且是用查表法,虽然说查表法速度快,但 2 ...

  3. CRC8 CRC16 查表法

    转自:http://blog.chinaunix.net/uid-14114479-id-1988515.html CRC8查表法 /********************************* ...

  4. 最详细易懂的CRC-16校验原理(附源程序)

    from:http://www.openhw.org/chudonganjin/blog/12-08/230184_515e6.html 最详细易懂的CRC-16校验原理(附源程序) 1.循环校验码( ...

  5. Redis CRC16校验 1.原理概述

    CRC16校验 1.原理概述 来自简书:CRC16算法 来自github.io带图解说:CRC循环冗余校验 来自之乎专栏:CRC-16校验原理与说明 来自简书:CRC16算法 简书摘要: CRC16 ...

  6. 大厂面试八股文——计算机网络

    文章目录 RESTful REST的指导原则 资源 资源方法 REST和HTTP不一样!! ip地址.子网掩码. 单机服务器最大并发的TCP连接数到底是多少 带外数据和TCP紧急指针 linux主机同 ...

  7. hex文件格式解析_玩转Hex文件

    00 关于Hex Hex文件格式(本文讲的是Intel Hex)是好多好多年以前定义的格式,解析这文件的工具网上搜出来的也是一大摞一大摞的.记住,我们就别瞎折腾自己写一个了哦.我们要学会站在巨人的肩膀 ...

  8. 嵌入式开发<串口调试工具>

    嵌入式开发<串口调试工具> 前言 1,设备参数分类 2,设备参数修改 3,调试工具软件 一.软件界面 二.功能说明 1.串口设置 1)串口选择 2)自动换行 3)接收到文件 4)保存文件 ...

  9. 十六、基于FPGA的CRC校验设计实现

    1,CRC校验 循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保 ...

  10. arduino笔记34:nRF24l01模块使用 通信地址 数据通道 校验码 PID

    最近再arduino中文社区看到了一篇介绍nrf24l01基本原理的帖子,内容感觉蛮不错的,学习一下,记录一下学习笔记. 大部分内容都是Arduino中文社区的帖子,附上自己的一点点体会. 目录 一. ...

最新文章

  1. java 办公,中文JAVA技术网
  2. UIKit框架使用总结--看看你掌握了多少
  3. 临时整形变量溢出的问题
  4. zcmu-1182(大数相减)
  5. stm32 led屏控制卡_LED显示屏控制卡解析
  6. javascript事件处理程序
  7. 丰田pcs可以关闭吗_别只知道开不坏,现在的丰田还有这些厉害绝招!
  8. Python 俄罗斯方块, 基于pyqt5实现俄罗斯方块 --pyqt5 进阶
  9. CSE强契约模式常见问题和应对策略
  10. 布丁机器人APP响应超时_常见问题解答
  11. 浅谈微博营销如何吸引流量
  12. 如何给服务器设置邮件警报。
  13. 基于jQuery的响应式网站视频插件FitVids.js
  14. Python库-pandas详解
  15. angular2 路由ajax,angular2路由切换改变页面title的示例代码
  16. 二级python试题分值_2020年12月计算机二级各考试科目题型及分值比例
  17. 免费、可商用的素材网站
  18. chmod 777 授权无效
  19. css鼠标经过按钮变色6,像这种鼠标移过去会变色的按钮怎么做的
  20. 医药研发产业新平台在从化

热门文章

  1. chrome启动参数
  2. 博途编程语言切换_从一种编程语言切换到另一种:灵活的好处
  3. matlab经验分布函数 教程,经验分布函数.ppt
  4. 操作系统课后答案第五章
  5. 通信原理 | 信道容量
  6. 【历史上的今天】10 月 11 日:域名 baidu.com 问世;三星 Galaxy Note 7 爆炸门告一段落;图灵奖数据库先驱诞生
  7. seekbar 的用法
  8. AppFuse 安装
  9. PDF可以转换成CAD图纸吗?
  10. android 8.1.0编译以及卡刷包制作教程