在嵌入式开发中,经常使用到CRC校验算法,用于校验通信数据和存储器数据。之前只是使用,对CRC原理及各种CRC算法的区别并无研究。参考网络上各位大神的文章和资料,从嵌入式软件开发的角度学习了下CRC校验算法,作个总结记录。
参考资料:
CRC校验手算及直观演示

一、简介

循环冗余校验(Cyclic Redundancy Check, CRC),是数据通信中最常采用的一种数据校验方式。与其他校验算法(如累加和校验)相似,CRC校验也是根据原始数据计算出特定的校验值作为冗余码附加在原始数据后用于数据差错校验。不同的是CRC使用模2运算(所谓模2运算,就是数据除2去余数)的方法,根据校验数据计算出校验码。

CRC校验可以理解为就是一个p位二进制数据序列后附加一个r位二进制校验码,从而构成一个总长为n = p + r位的二进制序列。CRC校验就是使用多项式模2运算:按异或运算,即相同为0,不同为1,可以简单理解为不考虑进位和借位的二进制加减运算,校验数据的二进制数据流作为多项式的系数,而多项式系数作为除数,而运算后的余数作为校验码。

以CRC7-MMC算法(初值为0x00,多项式为X8 X3 + X0)为例,CRC校验计算数据“0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88”过程如下所示:

// 在计算前,要在数据流前添加初值,数据流后要根据结果CRC的BIT数扩展相应比特个0。
000000000010001001000100011001101000100010101010110011001110111100010000000000100010010000000000010001100110100010001010101011001100111011110001000000000010001001000001011101000100010101010110011001110111100010000000000100010010011001100100010101010110011001110111100010000000000100010010100010110001010101011001100111011110001000000000010001001000000100001010101011001100111011110001000000000010001001000011000101011001100111011110001000000000010001001010011000110011001110111100010000000000100010010001000111001100111011110001000000000010001001000001110110011101111000100000000001000100101100101111011110001000000000010001001010000101101111000100000000001000100100001100101111000100000000001000100101000010110001000000000010001001000011001000100000000001000100101000001100000000001000100100001010000000000010001001001010010000001000100100101101000010001001001111010010001001

由上计算过程可知,数据“0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88”的CRC-MMC校验的计算结果为:0x7D。

二、不同的CRC校验算法

CRC校验算法,根据校验结果的位数,可以分为CRC-4,CRC-7,CRC-8,CRC-16,CRC-32,CRC-64等算法;根据CRC计算的多项式、初值、输入是否反转、输出是否反转、输出异或值,又可以分为多种算法,如:CRC-16/CCITT,CRC-16/IBM。相同宽度的CRC算法,初值,多项式等信息不同,其校验的碰撞几率也不会不同(通俗的讲,就是校验结果的可靠性,CRC校验算法,存在一定的几率会出现校验数据不同,但校验结果相同的情况)。不同CRC校验算法模型的碰撞几率可以参考相关理论研究及论文。嵌入式中常用CRC校验算法模型有:

Name Width Poly Init RefIn RefOut XorOut Note
CRC-16 16 0x8005 0x0000 True True 0x0000
CRC-16/CCITT 16 0x1021 0x0000 True True 0x0000
CRC-16/CCITT-FALSE 16 0x1021 0x0000 False False 0x0000

三、CRC校验算法代码实现

在嵌入式应用中,实现CRC校验算法有多种方式。在部分硬件平台上,存在硬件CRC模块,因此可以使用硬件CRC算法。硬件无法提供CRC计算的平台,也可以根据CRC原理,使用软件实现CRC算法。便于理解CRC原理,可以使用直接法实现CRC算法。而在实际应用,为提高CRC计算速度,通常使用查表法实现CRC算法。

1、直接计算法

实现算法参考网络相关代码,进行整理并验证,可直接使用,存在一些细节还没想通(如CRC4、5、6、7的计算方法),有兴趣的可以一起讨论下。

crc.c

#include "crc.h"
#include <stdio.h>typedef enum {REF_4BIT = 4,REF_5BIT = 5,REF_6BIT = 6,REF_7BIT = 7,REF_8BIT = 8,REF_16BIT = 16,REF_24BIT = 24,REF_32BIT = 32
}REFLECTED_MODE;uint32_t ReflectedData(uint32_t data, REFLECTED_MODE mode)
{data = ((data & 0xffff0000) >> 16) | ((data & 0x0000ffff) << 16);data = ((data & 0xff00ff00) >> 8) | ((data & 0x00ff00ff) << 8);data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4);data = ((data & 0xcccccccc) >> 2) | ((data & 0x33333333) << 2);data = ((data & 0xaaaaaaaa) >> 1) | ((data & 0x55555555) << 1);switch (mode){case REF_32BIT:return data;case REF_24BIT:return (data >> 8) & 0xffffff;case REF_16BIT:return (data >> 16) & 0xffff;case REF_8BIT:return (data >> 24) & 0xff;case REF_7BIT:return (data >> 25) & 0x7f;case REF_6BIT:return (data >> 26) & 0x7f;case REF_5BIT:return (data >> 27) & 0x1f;case REF_4BIT:return (data >> 28) & 0x0f;}return 0;
}uint8_t CheckCrc4(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,const uint8_t *buffer, uint32_t length)
{uint8_t i;uint8_t crc;if (refIn == true){crc = init;poly = ReflectedData(poly, REF_4BIT);while (length--){crc ^= *buffer++;for (i = 0; i < 8; i++){if (crc & 0x01){crc >>= 1;crc ^= poly;}else{crc >>= 1;}}}return crc ^ xorOut;}else{crc = init << 4;poly <<= 4;while (length--){crc ^= *buffer++;for (i = 0; i < 8; i++){if (crc & 0x80){crc <<= 1;crc ^= poly;}else{crc <<= 1;}}}return (crc >> 4) ^ xorOut;}
}uint8_t CheckCrc5(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,const uint8_t *buffer, uint32_t length)
{uint8_t i;uint8_t crc;if (refIn == true){crc = init;poly = ReflectedData(poly, REF_5BIT);while (length--){crc ^= *buffer++;for (i = 0; i < 8; i++){if (crc & 0x01){crc >>= 1;crc ^= poly;}else{crc >>= 1;}}}return crc ^ xorOut;}else{crc = init << 3;poly <<= 3;while (length--){crc ^= *buffer++;for (i = 0; i < 8; i++){if (crc & 0x80){crc <<= 1;crc ^= poly;}else{crc <<= 1;}}}return (crc >> 3) ^ xorOut;}
}uint8_t CheckCrc6(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,const uint8_t *buffer, uint32_t length)
{uint8_t i;uint8_t crc;if (refIn == true){crc = init;poly = ReflectedData(poly, REF_6BIT);while (length--){crc ^= *buffer++;for (i = 0; i < 8; i++){if (crc & 0x01){crc >>= 1;crc ^= poly;}else{crc >>= 1;}}}return crc ^ xorOut;}else{crc = init << 2;poly <<= 2;while (length--){crc ^= *buffer++;for (i = 0; i < 8; i++){if (crc & 0x80){crc <<= 1;crc ^= poly;}else{crc <<= 1;}}}return (crc >> 2) ^ xorOut;}
}uint8_t CheckCrc7(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,const uint8_t *buffer, uint32_t length)
{uint8_t i;uint8_t crc;if (refIn == true){crc = init;poly = ReflectedData(poly, REF_7BIT);while (length--){crc ^= *buffer++;for (i = 0; i < 8; i++){if (crc & 0x01){crc >>= 1;crc ^= poly;}else{crc >>= 1;}}}return crc ^ xorOut;}else{crc = init << 1;poly <<= 1;while (length--){crc ^= *buffer++;for (i = 0; i < 8; i++){if (crc & 0x80){crc <<= 1;crc ^= poly;}else{crc <<= 1;}}}return (crc >> 1) ^ xorOut;}
}uint8_t CheckCrc8(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,const uint8_t *buffer, uint32_t length)
{uint32_t i = 0;uint8_t crc = init;while (length--){if (refIn == true){crc ^= ReflectedData(*buffer++, REF_8BIT);}else{crc ^= *buffer++;}for (i = 0; i < 8; i++){if (crc & 0x80){crc <<= 1;crc ^= poly;}else{crc <<= 1;}}}if (refOut == true){crc = ReflectedData(crc, REF_8BIT);}return crc ^ xorOut;
}uint16_t CheckCrc16(uint16_t poly, uint16_t init, bool refIn, bool refOut, uint16_t xorOut,const uint8_t *buffer, uint32_t length)
{uint32_t i = 0;uint16_t crc = init;while (length--){if (refIn == true){crc ^= ReflectedData(*buffer++, REF_8BIT) << 8;}else{crc ^= (*buffer++) << 8;}for (i = 0; i < 8; i++){if (crc & 0x8000){crc <<= 1;crc ^= poly;}else{crc <<= 1;}}}if (refOut == true){crc = ReflectedData(crc, REF_16BIT);}return crc ^ xorOut;
}uint32_t CheckCrc24(uint32_t poly, uint32_t init, bool refIn, bool refOut, uint32_t xorOut,const uint8_t *buffer, uint32_t length)
{uint32_t i = 0;uint32_t crc = init;while (length--){if (refIn == true){crc ^= ReflectedData(*buffer++, REF_8BIT) << 16;}else{crc ^= (*buffer++) << 16;}for (i = 0; i < 8; i++){if (crc & 0x800000){crc <<= 1;crc ^= poly;}else{crc <<= 1;}}}if (refOut == true){crc = ReflectedData(crc, REF_24BIT);}return (crc ^ xorOut) & 0xffffff;
}uint32_t CheckCrc32(uint32_t poly, uint32_t init, bool refIn, bool refOut, uint32_t xorOut,const uint8_t *buffer, uint32_t length)
{uint32_t i = 0;uint32_t crc = init;while (length--){if (refIn == true){crc ^= ReflectedData(*buffer++, REF_8BIT) << 24;}else{crc ^= (*buffer++) << 24;}for (i = 0; i < 8; i++){if (crc & 0x80000000){crc <<= 1;crc ^= poly;}else{crc <<= 1;}}}if (refOut == true){crc = ReflectedData(crc, REF_32BIT);}return crc ^ xorOut;
}uint32_t CrcCheck(CRC_Type crcType, const uint8_t *buffer, uint32_t length)
{switch (crcType.width){case 4:return CheckCrc4(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,crcType.xorOut, buffer, length);case 5:return CheckCrc5(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,crcType.xorOut, buffer, length);case 6:return CheckCrc6(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,crcType.xorOut, buffer, length);case 7:return CheckCrc7(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,crcType.xorOut, buffer, length);case 8:return CheckCrc8(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,crcType.xorOut, buffer, length);case 16:return CheckCrc16(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,crcType.xorOut, buffer, length);case 24:return CheckCrc24(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,crcType.xorOut, buffer, length);case 32:return CheckCrc32(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,crcType.xorOut, buffer, length);}return 0;
}

crc.h

#ifndef __CRC_H__
#define __CRC_H__#include <stdint.h>
#include <stdbool.h>typedef struct {uint8_t width;uint32_t poly;uint32_t init;bool refIn;bool refOut;uint32_t xorOut;
}CRC_Type;uint32_t CrcCheck(CRC_Type crcType, const uint8_t *buffer, uint32_t length);#endif

main.c

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "crc.h"#define LENGTH    8
const uint8_t data[3][LENGTH] = {{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 },{ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 },{ 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f } };typedef struct {CRC_Type crcType;uint32_t result[3];
}CRC_Test;CRC_Test crc4_ITU = { { 4, 0x03, 0x00, true, true, 0x00 }, { 0x0f, 0x0a, 0x0e } };
CRC_Test crc5_EPC = { { 5, 0x09, 0x09, false, false, 0x00 }, { 0x00, 0x0c, 0x17 } };
CRC_Test crc5_ITU = { { 5, 0x15, 0x00, true, true, 0x00 }, { 0x16, 0x0a, 0x17 } };
CRC_Test crc5_USB = { { 5, 0x05, 0x1f, true, true, 0x1f }, { 0x10, 0x09, 0x17 } };
CRC_Test crc6_ITU = { { 6, 0x03, 0x00, true, true, 0x00 }, { 0x1d, 0x30, 0x00 } };
CRC_Test crc7_MMC = { { 7, 0x09, 0x00, false, false, 0x00 }, { 0x57, 0x30, 0x5b } };
CRC_Test crc8 = { { 8, 0x07, 0x00, false, false, 0x00 }, { 0x3e, 0xe1, 0x36 } };
CRC_Test crc8_ITU = { { 8, 0x07, 0x00, false, false, 0x55 }, { 0x6b, 0xb4, 0x63 } };
CRC_Test crc8_ROHC = { { 8, 0x07, 0xff, true, true, 0x00 }, { 0x6b, 0x78, 0x93 } };
CRC_Test crc8_MAXIM = { { 8, 0x31, 0x00, true, true, 0x00 }, { 0x83, 0x60, 0xa9 } };
CRC_Test crc16_IBM = { { 16, 0x8005, 0x0000, true, true, 0x0000 }, { 0xc4f0, 0x2337, 0xa776 } };
CRC_Test crc16_MAXIM = { { 16, 0x8005, 0x0000, true, true, 0xffff }, { 0x3b0f, 0xdcc8, 0x5889 } };
CRC_Test crc16_USB = { { 16, 0x8005, 0xffff, true, true, 0xffff }, { 0x304f, 0xd788, 0x53c9 } };
CRC_Test crc16_MODBUS = { { 16, 0x8005, 0xffff, true, true, 0x0000 }, { 0xcfb0, 0x2877, 0xac36 } };
CRC_Test crc16_CCITT = { { 16, 0x1021, 0x0000, true, true, 0x0000 }, { 0xeea7, 0xfe7c, 0x7919 } };
CRC_Test crc16_CCITT_FALSE = { { 16, 0x1021, 0xffff, false, false, 0x0000 }, { 0x4792, 0x13a7, 0xb546 } };
CRC_Test crc16_X25 = { { 16, 0x1021, 0xffff, true, true, 0xffff }, { 0x6dd5, 0x7d0f, 0xfa6a } };
CRC_Test crc16_XMODEM = { { 16, 0x1021, 0x0000, false, false, 0x0000 }, { 0x76ac, 0x2299, 0x8478 } };
CRC_Test crc16_DNP = { { 16, 0x3D65, 0x0000, true, true, 0xffff }, { 0x7bda, 0x0535, 0x08c4 } };
CRC_Test crc24 = { { 24, 0x864cfb, 0x000000, false, false, 0x000000 }, { 0x4c9612, 0x352d9f, 0x1c3c32 } };
CRC_Test crc32 = { { 32, 0x04c11db7, 0xffffffff, true, true, 0xffffffff }, { 0x3fca88c5, 0xe0631a53, 0xa4051a26 } };
CRC_Test crc32_MPEG2 = { { 32, 0x4c11db7, 0xffffffff, false, false, 0x00000000 }, { 0x14dbbdd8, 0x6509b4b6, 0xcb09d294 } };void CrcTest(CRC_Test crcTest)
{uint32_t i;for (i = 0; i < 3; i++){printf("%08x\t%08x\r\n", CrcCheck(crcTest.crcType, data[i], LENGTH), crcTest.result[i]);}printf("\r\n");
}int main(void)
{CrcTest(crc4_ITU);CrcTest(crc5_EPC);CrcTest(crc5_ITU);CrcTest(crc5_USB);CrcTest(crc6_ITU);CrcTest(crc7_MMC);CrcTest(crc8);CrcTest(crc8_ITU);CrcTest(crc8_ROHC);CrcTest(crc8_MAXIM);CrcTest(crc16_IBM);CrcTest(crc16_MAXIM);CrcTest(crc16_USB);CrcTest(crc16_MODBUS);CrcTest(crc16_CCITT);CrcTest(crc16_CCITT_FALSE);CrcTest(crc16_X25);CrcTest(crc16_XMODEM);CrcTest(crc16_DNP);CrcTest(crc24);CrcTest(crc32);CrcTest(crc32_MPEG2);return 0;
}

四、注意

  • 不同的CRC算法,对00H或FFH数据流的计算结果不一样,部分算法存在校验结果也为00H或FFH的情况(也就意味着存储空间处于初始化状态时:全0或全1,CRC校验反而是正确的),在应用中需要注意避免。
  • 对数据流的CRC校验进行检查时,可以将原数据流和CRC校验结果一起再做CRC校验,若CRC校验正确则计算结果为0。利用这个特性可简化CRC验证的软件实现(对CRC校验结果又进行异或非0的CRC算法不适用)。

CRC校验算法及实现相关推荐

  1. 【FPGA】CRC校验算法从数学原理到代码实现

    老规矩,转b站 [[FPGA]CRC校验算法从数学原理到代码实现-哔哩哔哩]

  2. CRC校验算法的数学原理(上)

    介绍   CRC是Cyclic Redundancy Check的缩写,用中文来讲,就是 循环冗余校验.是一种通过对数据产生固定位数校验码以备侦测数据错误的数据校验技术,主要用来侦测数据传输错误,也可 ...

  3. modbus c语言校验算法,Modbus CRC校验算法

    终于找到了 Modbus CRC 校验算法 算法一: unsigned int calccrc(uchar crcbuf,uint crc) { uchar i; crc=crc ^ crcbuf; ...

  4. c# 如何编写CRC校验算法

    在C#中编写CRC校验算法,可以使用以下步骤: 首先需要确定CRC校验的多项式和初始值.根据具体的需求和应用场景,选择合适的CRC多项式和初始值. 定义一个计算CRC校验值的函数,函数的输入参数为待校 ...

  5. CRC-16原理及通用的16位CRC校验算法代码

    CRC-16原理及通用的16位CRC校验算法代码 循环冗余码校验英文名称为Cyclical Redundancy Check,简称CRC.它是利用除法及余数的原理来作错误侦测(Error Detect ...

  6. CRC校验算法的解析,暨对网上的CRC详解的补充

    一.CRC的形象理解 本文面向对CRC校验有一定基础的读者,如果你不懂,请戳这里.维基百科还有图解版的. 在CRC的具体实现中,如果要计算CRC的数据很长,一般都会用到寄存器,用来保存当前的计算到的C ...

  7. 16位CRC校验算法,16进制crc校验

    在CRC计算时只用8个数据位,起始位及停止位,如有奇偶校验位也包括奇偶校验位,都不参与CRC计算. CRC计算方法是: 1. 加载一值为0XFFFF的16位寄存器,此寄存器为CRC寄存器. 2. 把第 ...

  8. LabVIEW实现CRC校验

    目录 1.内部控件计算CRC校验 2.公式节点计算CRC校验 CRC(循环冗余校验),是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存 ...

  9. crc16算法php实现,关于实现CRC16校验算法的两个函数

    之前在2017年2月份发布过一个主题,也是关于CRC16的,现在我直接把函数写出来,希望能帮到有需要的朋友.对于在线监测设备数据传输标准来说,CRC16校验算法有基于通用modbus协议的,有基于环保 ...

  10. Adler-32校验算法

    Adler-32校验算法 Adler-32是Mark Adler发明的校验和算法,和32位CRC校验算法一样,都是保护数据防止意外更改的算法,但是这个算法较容易被伪造,所以是不安全的保护措施.但是比C ...

最新文章

  1. 无限的hypotheses 变成有限的dichotomies
  2. C语言实现数组Array(附完整源码)
  3. modbus软件开发实战指南_C++核心准则?GSL:指南支持库
  4. H.264中IDR帧和I帧区别
  5. Vue mixins学习
  6. 欢迎各位小伙伴们关注我的公众号,可以随时跟我在微信上进行交流
  7. springcloud之gateway服务网关
  8. Mujoco 2D hopper
  9. 洛谷1498-谢尔宾斯基三角形-python-(递归)
  10. BUUCTF Misc wp大合集(1)
  11. C. Minimum Ties
  12. Python小测验(01)
  13. vue脚手架安装报错,终极方法
  14. 整理总结:深入浅出统计学——排列与组合
  15. Ruby+Selenium demo
  16. 成人高考 计算机英语作文,「2017成人高考高起专英语作文」求成人考试英语作文4篇!...
  17. 软件工程McCabe环路复杂度计算,自环情况详解。
  18. bootStrap 教程 文档
  19. mtk9652和mt9638选哪个好
  20. leetcode刷题记录2:进度64, 2021.10.23

热门文章

  1. 2015年计算机模块在线,2015年工程师职称计算机考试Excel 2003模块题库.doc
  2. 缺少比较器,运放来救场!(运放当做比较器电路记录)
  3. php解析视频_YY神曲视频PHP解析调用代码
  4. 记账系统推荐金蝶精斗云_金蝶精斗云好用的免费的财务做账软件有哪些?
  5. 用海康威视摄像头做图像处理问题
  6. PHP常用函数性能对比
  7. Unity3d Network 局域网多人对战之游戏大厅
  8. idea svn回退版本_mac下使用svn通过终端回退版本到某个版本号 - 博客频道 - CSDN.NET...
  9. 服装智能制造开启服装企业信息化建设
  10. CentOS 迁移SVN以及可视化管理工具iF.SVNAdmin