> 文章列表 > C语言标准CRC-16校验函数

C语言标准CRC-16校验函数

C语言标准CRC-16校验函数

C语言标准CRC-16校验函数

CRC-16校验产生2个字节长度的数据校验码,通过计算得到的校验码和获得的校验码比较,用于验证获得的数据的正确性。获得的校验码是随数据绑定获得。

CRC校验原理及标准CRC-8校验函数可参考:C语言标准CRC-8校验函数。这里介绍CRC-16的64位计算方式和简化的16位和8位计算方式。

设计原理

设计原理仍然基于无符号64位整型为一个计算单元,当超过64位时,将前一个单元的计算余数,与后面的输入数据重新组成64位数据,再进行模二除法,以此类推,得到最后的CRC-16校验值(余数)。设计按照CRC计算基本原理来实现,以契合理解对照。

CRC-16校验函数

这里的校验码采用标准校验码X^16 + X^15 + X^2 + 1,对于其它类型的CRC-16校验码或有输入数据前处理或输出数据后处理的情况,相应的做代码简单调整即可。CRC-16校验函数如下:

#include <stdio.h>
#include <stdlib.h>
uint16_t PY_CRC_16(uint8_t *di, uint32_t len)
{   uint32_t crc_poly = 0x00018005;  //X^16+X^15+X^2+1 total 17 effective bits. Computed total data shall be compensated 16-bit '0' before CRC computing.uint8_t *datain;uint64_t cdata = 0; //Computed total datauint32_t data_t = 0; //Process data of CRC computinguint16_t index_t = 63;  ///bit shifting index for initial '1' searchinguint16_t index = 63;    //bit shifting index for CRC computinguint8_t rec = 0; //bit number needed to be compensated for next CRC computinguint32_t cn=(len+2)/6;uint32_t cr=(len+2)%6;uint32_t j;datain = malloc(len+2);for(j=0;j<len;j++){datain[j] = di[j];}datain[len] = 0; datain[len+1] = 0;//Compensate 16-bit '0' for input dataif(len<=6)   //Mount data for only one segment{for(j=0;j<=(len+1);j++){cdata = (cdata<<8);cdata = cdata|datain[j];}cn = 1;}else{if(cr==0){cr = 6;}else if(cr==1){cr = 7;}else if(cr==2){cr = 8;}else{cn++;}for(j=0;j<cr;j++){cdata = (cdata<<8);cdata = cdata|datain[j];}}do{cn--;while(index_t>0){if( (cdata>>index_t)&1 ){index = index_t;index_t = 0;data_t |= (cdata>>(index-16));{data_t = data_t ^ crc_poly;}while((index!=0x5555)&&(index!=0xaaaa)){for(uint8_t n=1;n<17;n++){if ((data_t>>(16-n))&1) {rec = n;break;}if (n==16) rec=17;}if((index-16)<rec){data_t = data_t<<(index-16);data_t |=  (uint32_t)((cdata<<(64-(index-16)))>>(64-(index-16)));index = 0x5555;}else{for(uint8_t i=1;i<=rec;i++){data_t = (data_t<<1)|((cdata>>(index-16-i))&1) ;}if(rec!= 17){data_t = data_t ^ crc_poly;index -= rec;}else{data_t = 0;index_t = index-16-1;index = 0xaaaa;}}}if(index==0x5555) break;}else{index_t--;if(index_t<16) break;}}if(cn>0) //next segment{cdata = data_t&0x00ffff;for(uint8_t k=0;k<6;k++){cdata = (cdata<<8);cdata = cdata|datain[j++];}data_t = 0;index_t = 63;  ///bit shifting index for initial '1' searchingindex = 63;    //bit shifting index for CRC computingrec = 0; //bit number needed to be compensated for next CRC computing}}while(cn>0);free(datain);return (uint16_t)data_t;
}

CRC-16校验函数16位计算方式优化

在理解了和CRC16校验原理完全一致的代码实现后,不采用64位计算方式,则可以简化代码为16位计算方式,可得到相同的校验值结果:

#include <stdio.h>
#include <stdlib.h>
uint16_t PY_CRC_16_S(uint8_t *di, uint32_t len)
{uint16_t crc_poly = 0x8005;  //X^16+X^15+X^2+1 total 16 effective bits without X^16. Computed total data shall be compensated 16-bit '0' before CRC computing.uint32_t clen = len+2;uint8_t cdata[clen] ;memcpy(cdata, di, len); cdata[len]=0; cdata[len+1]=0;uint16_t data_t = (((uint16_t)cdata[0]) << 8) + cdata[1]; //CRC registerfor (uint32_t i = 2; i < clen; i++){for (uint8_t j = 0; j <= 7; j++){if(data_t&0x8000)data_t = ( (data_t<<1) | ( (cdata[i]>>(7-j))&0x01) ) ^ crc_poly;elsedata_t = ( (data_t<<1) | ( (cdata[i]>>(7-j))&0x01) ) ;}}return data_t;
}

CRC-16校验函数查表原理优化

CRC查表原理通过输入数据分段计算(CRC-16可按双字节/单字节分段)原理实现校验码的计算,查表法有如下特点:

  1. 当前输入数据段值异或当前的查表值,得到当前的CRC计算余数
  2. 当前查表值由前一计算余数与校验码按CRC校验过程计算得到,并保存为对应前一计算余数对应的表位值
  3. 当前查表值的计算不受当前输入字段值影响,所以当前输入字段值为0且为最后字段时,当前查表值异或当前输入字段值不变,此时当前查表值即为CRC校验值结果。
  4. 由第2和3条可知,第2条在CRC校验过程计算时,移位补位时补0即可,也就不需要当前字段值进入移位补位过程。

CRC-16校验16位数据格式函数优化为如下代码,注意输入数据为16位数组:

uint16_t PY_CRC_16_T16(uint16_t *di, uint32_t len)
{uint16_t crc_poly = 0x8005;  //X^16+X^15+X^2+1 total 16 effective bits without X^16. uint16_t data_t = 0; //CRC registeruint16_t cdata[len];for(uint32_t j=0;j<len;j++){cdata[j] = (di[j]>>8 | di[j]<<8);}cdata[0] ^= 0xffff;for(uint32_t i = 0; i < len; i++){data_t ^= cdata[i]; //16-bit datafor (uint8_t j = 0; j < 16; j++){if (data_t & 0x8000)data_t = (data_t << 1) ^ crc_poly;elsedata_t <<= 1;}}return (data_t);
}

CRC-16校验8位数据格式函数优化为如下代码,输入数据为8位数组:

uint16_t PY_CRC_16_T8(uint8_t *di, uint32_t len)
{uint16_t crc_poly = 0x8005;  //X^16+X^15+X^2+1 total 16 effective bits without X^16. uint16_t data_t = 0; //CRC registerfor(uint32_t i = 0; i < len; i++){data_t ^= di[i]<<8; //8-bit datafor (uint8_t j = 0; j < 8; j++){if (data_t & 0x8000)data_t = (data_t << 1) ^ crc_poly;elsedata_t <<= 1;}}return (data_t);
}

查表法对应输入数据分段为双字节时,采用如上几种方式任何一种,对双字节0~65535的输入数分别进行CRC-16校验,得到的各个校验值,也就得到查表法对应每个输入数值的查表值。
查表法对应输入数据分段为单字节时,采用如上几种方式任何一种,对单字节0~255的输入数分别进行CRC-16校验,得到的各个校验值,也就得到查表法对应每个输入数值的查表值。

CRC-16校验注意事项

实际应用中,由于输入数据前处理和输出数据后处理的不同,产生了不同的CRC应用标准,其中一些是一些知名厂家为自己的产品定义CRC校验函数。这些处理特性包括CRC寄存器初始值设置,数据字节位反转,数据字节高位还是低位优先进入计算,输出的整个数据是否按位反转,输出数据是否和一个数异或等。常见的一些CRC-16校验特性:

C语言标准CRC-16校验函数
–End–