本文在我已在知乎发过地址-->addr

最近用到CRC-16/CCITT-FALSE算法校验,找了很多资料,
发现代码和线上校验的值对不上,所以花了时间深入了解其原理,并
将
CRC-8/CRC-8/ITU/ROHC/MAXIM
CRC-16/IBM/MAXIM/USB/MODBUS/CCITT/CCITT-FALSE/X25/XMODEM/DNP
CRC-32/MPEG-2
这些校验算法都实现了一遍,
大部分算法实现都有普通的遍历校验
全部算法都有查表法实现的代码,
每个算法都是独立单个函数

点击源码地址-->addr

1.什么是CRC

CRC:循环冗余校验
以CRC-32为例
是根据CRC-32的生成多项式x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
计算出任意字符或字符串的一个32位值
作用:用来核实数据传输/保存的正确性和完整性

2.什么生成多项式

以CRC-32的生成多项式x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+为例
其数学生成多项表达式如下GF(2)=k*x^32+k*x^31+....k*x^1+1CRC多项式的计算是以GF(2)(2元素伽罗瓦域)多项式算术为数学基础的GF(2):2元素伽罗瓦域,其加减法可认为是无进位的二进制加减法,这样的话相当于两个bit位的异或运算因为是2元素,x的系数K的取值为0和1
如此CRC-32的生成多项式可以转换为二进制(幂次对应二进制位的位置,系数对应1和0)====二进制==>10000100110000010001110110110111b====16进制==>0x84C11DB7
但是CRC-32的生成多项式的16进制表示法:0x04C11DB7
这是因为16进制表示法去除了最高次项,之所以这么写是由其计算方式决定,
这个后面会讲到。

3.CRC的计算原理

将要校验的数据看作一个多项式:NM
生成多项为G
校验值CRC=NM除G的余数

4.GF(2)多项式之间的计算

1.多项式之间的加减法即系数K之间进行模2算术执行,本质上就是二进制的异或运算,模2运算加减法都是一样的
例如多项式A=x^3+x^2+1多项式B=x^3+x^1+1A+B=:A和B x^3的系数都为1,异或结果K=0,K*x^3=0A的x^2的系数k=1,B的x^2的系数k=0,异或结果k=1 相加结果是K*x^2=1*x^2A的x^1的系数0,B的该位系数为1,相加结果为 1*x^1A和B的x^0的系数都为1,相加结果为0A+B=x^2+x 2.多项式之间的乘法与普通多项式一样,只是各项在相加时按照模2算术执行A*B=(x^3+x^2+1)(x^3+x^1+1)=(x^(3+3)+x^(3+1)+x^(3+0))+(x^(2+3)+x^(2+1)+x^(2+0))+(x^3+x^1+1) =(x^6+x^4+x^3)+(x^5+x^3+x^2)+(x^3+x^1+1) =x^6+x^5+x^4+x^3+x^3+x^2+x^1+13.多项式之间的除法与普通多项式一样,只是各项在相加时按照模2减法算术执行C=x^7 + x^6 + x^5 + x^2 + x
C/B=下图运算方式商为:X^4+X^3+1余数为:X^2+1  

5.以生成多项式x^3 + x + 1来探讨CRC的二进制数据的计算过程

将长度为m位的信息对应一个GF(2)的多项式M,该数据是从高位到低位传输的m=11100110====对应的多项式=====  M=x^7 + x^6 + x^5 + x^2 + x发送端和接收端约定了一个次数为r的GF(2)多项式G,G称为生成多项式
比如 G=1011=x^3 + x + 1  r=3  (CRC-8的r=8,CRC16的r=16)在m后面加上r个0的多项式NM
NM=m+r个0=11100110000=x^10+x^9+x^8+x^5+x^4NM与M的关系如下NM=M*x^r=(x^7 + x^6 + x^5 + x^2 + x)*x^3=x^10+x^9+x^8+x^5+x^4NM/G 获得的对应r长度的余数就是校验码了
计算步骤看下图注意:除数G应该按照生成多项式来转为二进制,而不是直接取它的16进制表示,因为16进制表示法去掉了最高次项 

发送端将m+R(校验码)发送到接收端
接收端可以根据NM以及G生成多项式计算出校验码比较
或者
接受端可以用(NM或运算R)/G 判断余数是否为0下面是一些生成多项式G
注意:G应该按照生成多项式来转为二进制,而不是直接取它的16进制表示,因为16进制表示法去掉了最高次项 

6.CRC8代码的几种实现方式
以CRC-8 多项式为x^8+x^2+x+1 16进制表示法 0x07 为例
方式一,以上面的竖式运算来算

#include "crc_8.h"
#include "stdio.h"
/*** 获得data 数据为1的最高位的位置* @param data* @return*/
uint8_t getMbsBitIndex(uint8_t data){uint8_t index;for (index=7;  index>0 ; index--) {if((0x01<<index)&data){return index;}}}/*** 通过生成式*          G的16进制表示法 0x07*          对应的多项式为 x^8+x^2+x+1===>0x107==>对应的二进制是==>100000111*          G的真实值=0x107*          r=8*          m=data*          NM:二进制的data在后面+r个0** 原理:将data<<8 获得NM*      NM/G 的余数即data的校验码,这里的除法为多项式除法,且取余的减法是模2运算,即二进制的异或*      通过G多项式对应的二进制为1的最高位与NM对应的二进制为1的最高位对齐进行异或运算获得的结果temp_value*      如果temp_value的二进制值为1的最高位>=r,也就是8,则将temp_value看作NM,继续与G进行对齐模2运算*      直到temp_value的二进制值为1的最高位<r ,此时temp_value就是最终的校验值了** @param data 单个字符校验* @return*/
uint8_t getCRC_8(uint8_t data) {uint16_t G=0x107;//1。先计算NM的值uint32_t NM=data<<8;
//    printf("NM=%X\n",NM);//2.计算出data为1的最高位uint8_t index=getMbsBitIndex(data);
//    printf("index=%d\n",index);//3.G为1的最高位与NM为1的最高位对齐uint32_t tG=(G<<(index));
//    printf("tG=%X\n",tG);//4.对NM与tG进行异或运算uint32_t temp_value=NM^tG;
//    printf("temp_value=%X\n",temp_value);//取temp_value的[15:8]uint8_t tm=((temp_value&0xFF00)>>8);//5.获取temp_value的为1的最高位index=getMbsBitIndex(tm);
//    printf("index=%d\n",index);//一直循环到 index==0,并且temp_value的[15:8]==0while (index!=0||tm){//G为1的最高位与NM为1的最高位对齐tG=(G<<(index));//对temp_value与tG进行异或运算temp_value=temp_value^tG;tm=((temp_value&0xFF00)>>8);//获取temp_value的为1的最高位index=getMbsBitIndex(tm);
//        printf("while_index=%d\n",index);}
//    printf("temp_value=%X\n",temp_value);return temp_value;
}

实现方式二,寄存器(内存模仿寄存器)移位方式

/*** 原理*    G=0x07; 用CRC-8的16进制表示法 而不是用生成表达式的值0x107*    NM=data<<8;*    先将NM的高8位加载到8位长度的内存temp_value中r**    如果内存中的最高位为1,将内存中的值左移一位,*    并将NM后面的位依次移入内存*    因为单个字节,后面全是8个0,左移自动补零相当于帮将NM后面的位自动移入该内存中了*    然后再与G进行异或并将结果保存在该内存中,这里的G=0x07,是因为这里将最高的1位移出,相当于除去了0x107的最高位**    如果内存中的最高位为0,将内存中的值左移一位*    并将NM后面的位依次移入内存*    因为单个字节,后面全是8个0,左移自动补零相当于帮将NM后面的位自动移入该内存中了**    直到NM的最低位也被加载到内存中,该内存中的值就是校验值** @param data* @return*/
uint8_t gencrc8_2(uint8_t data){uint8_t G=0x07;//这里可以直接用data加载到8位长度的内存中,之所以移位是对照公式的NM,便于理解uint16_t NM=data<<8;//把NM开头的8位加载到内存中uint8_t temp_value=(NM&0xFF00)>>8;//循环8次是为了将NM全部移动到8位内存中for (int i = 0; i <8; ++i) {//判断8位内存中的最高位是否是1uint8_t h=(temp_value&0x80);if(h==0x80){//如果是1,则左移1位,并与G进行异或,并保存在8位内存中temp_value=((temp_value<<1)&0xFF);temp_value=(temp_value^G);} else{//如果是0,则左移1位,不进行异或运算,并保存在8位内存中temp_value=((temp_value<<1)&0xFF);}}
//    printf("%X\n",temp_value);return temp_value;
}

方式三,使用查表法以空间换时间计算多个数据的校验值

//1.计算出0~0xFF的所有校验值并将其放入一个256的数组中
const uint8_t crc_table [256]={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,0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD,0x90,0x97,0x9E,0x99,0x8C,0x8B,0x82,0x85,0xA8,0xAF,0xA6,0xA1,0xB4,0xB3,0xBA,0xBD,0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,0xD5,0xD2,0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA,0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,0x8F,0x88,0x81,0x86,0x93,0x94,0x9D,0x9A,0x27,0x20,0x29,0x2E,0x3B,0x3C,0x35,0x32,0x1F,0x18,0x11,0x16,0x03,0x04,0x0D,0x0A,0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A,0x89,0x8E,0x87,0x80,0x95,0x92,0x9B,0x9C,0xB1,0xB6,0xBF,0xB8,0xAD,0xAA,0xA3,0xA4,0xF9,0xFE,0xF7,0xF0,0xE5,0xE2,0xEB,0xEC,0xC1,0xC6,0xCF,0xC8,0xDD,0xDA,0xD3,0xD4,0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,0x51,0x56,0x5F,0x58,0x4D,0x4A,0x43,0x44,0x19,0x1E,0x17,0x10,0x05,0x02,0x0B,0x0C,0x21,0x26,0x2F,0x28,0x3D,0x3A,0x33,0x34,0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63,0x3E,0x39,0x30,0x37,0x22,0x25,0x2C,0x2B,0x06,0x01,0x08,0x0F,0x1A,0x1D,0x14,0x13,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
};/*** /*** 查表法计算 crc*  生成多项式:x^8+x^2+x+1*  16进制表示:0x07*  初始值:0x00*  输入输出反转:False*** 该查表法原理*      以  E9AAEE为例*          先计算E9的crc值===>91*          先算E9AA =>gencrc8_temp(0xE9AA)==>3B*          恰好相当于E9的crc值异或AA:gencrc8_temp(0xE9)^AA==>3B*          接着以3B作为EE的高8位计算getcrc8_temp(3BEE)===>4F*          恰好相当于3B的crc值异或EE:gencrc8_temp(0x3B)^AA==>4F*          最后计算4F的crc gencrc8_temp(0x4F)===>EA 这个值机最终的CRC值**          数学原理*          NM=M*X^r*          E900=E9*X^8==>*                      (X^7+X6+X^5+X^3+1)*X8=X^15+X^14+x^13+X^11+^X8*          由因为求E9的校验值,其实就是求E900/G 的余数CRC1*          有因为只算高8位,所以E900/G和E9AA/G的商是相等的*          设E9为K,AA为L,K/G和AA/G的商为N*          E900=K*X^8*          GN+CRC1=K*X^8*          E9AA=K*X^8+L*          GN+CRC2=K*X^8+L*          K*X^8-CRC1=K*X^8+L-CRC2*          CRC2=K*X^8+L-K*X^8+CRC1*              =L+CRC1*         由因为模2运算的加减法相当于异或运算*         所以CRC2=CRC1^L*         E9AA的校验值CRC2相当于E900的CRC1^AA**对应的多项式为 x^8+x^2+x+1===>0x107==>对应的二进制是==>100000111* @param datas* @param len* @return*/uint8_t crc8(unsigned char * datas, uint16_t len) {uint8_t crc=0;for (int i = 0; i < len; ++i) {crc=(crc_table[crc]^datas[i]);
//            printf("table[%d]=%X  crc=%X\n",i,datas[i],crc);}return crc_table[crc];
}

CRC校验值是如何计算出来的?相关推荐

  1. crc 校验错误_资深工程师:图说CRC原理应用及STM32硬件CRC外设

    在嵌入式产品应用中,常常需要应对系统数据在存储或者传输过程中的完整性问题.所谓完整性是指数据在其生命周期中的准确性和一致性.这些数据可能存储在EEPROM/FLASH里,或者基于通信协议进行传输,它们 ...

  2. DS18B20 单总线多器件的ROM 搜索, ALARM 检测, CRC 校验 源码实现, 基于 STM32F103

    DS18B20使用5V供电, 数据线总线 DQ 经4.7k 上拉电阻到 5V, 连接到  B12 端口,  多个器件共用一条数据总线, (以前以为 ROM 搜索是穷举搜索, 但是 搜索1位需要 读两次 ...

  3. TPS929120的CRC校验的三种实现方法

    文章目录 CRC基础知识 CRC概念 CRC参数模型 常用的21个标准CRC参数模型: TPS929120的CRC值计算方式 CRC参数: 算法图: 计算方法一: 计算步骤: 实现代码为: 计算方法二 ...

  4. CRC校验原理的完整学习

    前言:1.想直接用CRC的可以直接看程序,想了解原理,或者写程序的原理的.可以看按顺序看整篇 2.模二除法没有深入研究,所以模二除法待验证,也不去验证了,我花了一个月的业余时间写.3.本文只进行了16 ...

  5. 【RE】3 CRC校验原理及实现

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

  6. CRC校验 串行 并行 长除 移位 查表 矩阵

    CRC校验的几种类型: 长除法,也叫直接计算法 移位寄存器,也叫线性移位 查表法 并行算法 一些有用的网页: CRC并行推导 https://blog.csdn.net/Old_Street/arti ...

  7. CRC校验查表法原理及实现(CRC-16)

    绪论 在网上浏览了很多关于CRC校验的文章,基本上都是针对CRC校验原理的阐述以及关于CRC校验查表法的实际应用以及具体软件实现方法. 至于查的表是怎么来的,软件为什么要这样实现很多文章并没有说明.本 ...

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

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

  9. crc16查表法c语言实现,CRC校验查表法原理及实现(CRC-16)

    绪论 在网上浏览了不少关于CRC校验的文章,基本上都是针对CRC校验原理的阐述以及关于CRC校验查表法的实际应用以及具体软件实现方法. 至于查的表是怎么来的,软件为何要这样实现不少文章并无说明.本篇文 ...

最新文章

  1. 全长转录组测序在植物中的应用研究进展 赵陆滟,曹绍玉,龙云树,张应华,许俊强
  2. namespace用法
  3. python课程价格-少儿编程培训机构哪家好,儿童编程价格多少钱?家长知多少
  4. 【java笔记】常用函数式接口(4):Funtion接口
  5. 护壁桩嵌入深度_钻孔灌注桩嵌岩深度最少多少
  6. 冲刺阶段—个人工作总结07
  7. 计算机网络(十三),Socket编程实现TCP和UDP
  8. 时空幻境的作者谈怎样做一款好游戏
  9. 前端布局篇之文字居中显示
  10. 学生总分排名的c语言程序,学生成绩分析及排名系统C语言程序设计课程设计实习报告.doc...
  11. 腾讯云服务器被攻击了怎么办?
  12. 编译liteos(ubuntu)
  13. VBS播放WMP文件
  14. 浙江大学计算机博士很难复试专业课,2017年浙江大学计算机考研_跨考_复试经验谈...
  15. react 之 prop-types
  16. python 网络爬虫——爬取小米应用商店排名前100App
  17. vip2-day22 MySQL 基础语句
  18. 【嵌入式linux】使用4G模块EC20自适应运营商和ppp拨号上网
  19. 结构体、共用体、位操作和枚举类型
  20. 【硬见小百科】一些硬件电路技术经验整理

热门文章

  1. 微信小程序与微信登陆的交互、微信登录、获取微信信息
  2. 软件系统设计步骤与原理
  3. 软件考试:89个系统相关的基本概念
  4. 让线程按顺序执行8种方法(转载)
  5. 干货培训 | 使用OBS进行直播导播和推流(上篇)
  6. Mysql 常用函数(40)- time_to_sec 函数
  7. Java编程入门之前篇
  8. 一文读懂Android View事件分发机制
  9. php 蓝奏网盘上传文件,蓝奏云盘客户端v2.6 支持文件夹和大文件上传了
  10. 无人机感知与规避技术综述