C语言标准CRC-32校验函数
CRC-32校验产生4个字节长度的数据校验码,通过计算得到的校验码和获得的校验码比较,用于验证获得的数据的正确性。获得的校验码是随数据绑定获得。
CRC校验原理及标准CRC-8校验函数可参考:C语言标准CRC-8校验函数。这里介绍CRC-32的64位计算方式和简化的32位计算方式。
设计原理
设计原理仍然基于无符号64位整型为一个计算单元,当超过64位时,将前一个单元的计算余数,与后面的输入数据重新组成64位数据,再进行模二除法,以此类推,得到最后的CRC-32校验值(余数)。设计按照CRC计算基本原理来实现,以契合理解对照。
CRC-32校验函数
这里的校验码采用标准校验码x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1,对于其它类型的CRC-32校验码或有输入数据前处理或输出数据后处理的情况,相应的做代码简单调整即可。CRC-32校验函数如下:
#include #include uint32_t PY_CRC_32(uint8_t *di, uint32_t len){uint64_t crc_poly = 0x104C11DB7;//X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+1 total 33 effective bits. Computed total data shall be compensated 32-bit '0' before CRC computing.uint8_t *datain;uint64_t cdata = 0; //Computed total datauint64_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+4)/4;uint32_t cr=(len+4)%4;uint32_t j;datain = malloc(len+4);for(j=0;j<len;j++){datain[j]=di[j];}datain[len]=0; datain[len+1]=0; datain[len+2]=0; datain[len+3]=0;//Compensate 32-bit '0' for input dataif(len<=4) //Mount data for only one segment { for(j=0;j<=(len+3);j++) { cdata = (cdata<<8); cdata = cdata|datain[j]; } cn = 1; }else { if(cr==0) { cr = 8; cn--; } else if(cr==1) { cr = 5; } else if(cr==2) { cr = 6; } else if(cr==3) { cr = 7; } else; 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-32)); { data_t = data_t ^ crc_poly; } while((index!=0x5555)&&(index!=0xaaaa)) { for(uint8_t n=1;n<33;n++) { if ((data_t>>(32-n))&1) {rec = n;break;} if (n==32) rec=33; } if((index-32)<rec) { data_t = data_t<<(index-32); data_t |=(uint64_t)((cdata<<(64-(index-32)))>>(64-(index-32))); index = 0x5555; } else { for(uint8_t i=1;i<=rec;i++) { data_t = (data_t<<1)|((cdata>>(index-32-i))&1) ; } if(rec!= 33) { data_t = data_t ^ crc_poly; index -= rec; } else { data_t = 0; index_t = index-32-1; index = 0xaaaa; } } } if(index==0x5555) break; } else { index_t--; if(index_t<32) break; } } if(cn>0) //next segment {cdata = data_t&0x00ffffffff; for(uint8_t k=0;k<4;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 (uint32_t)data_t;}
CRC-32校验函数32位计算方式优化
在理解了和CRC32校验原理完全一致的代码实现后,不采用64位计算方式,则可以简化代码为32位计算方式,可得到相同的校验值结果:
#include #include uint32_t PY_CRC_32_S(uint8_t *di, uint32_t len){uint32_t crc_poly = 0x04C11DB7;//X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+1 total 32 effective bits without X^32. Computed total data shall be compensated 32-bit '0' before CRC computing.uint32_t clen = len+4;uint8_t cdata[clen] ;memcpy(cdata, di, len); cdata[len]=0; cdata[len+1]=0; cdata[len+2]=0; cdata[len+3]=0;uint32_t data_t =(((uint32_t)cdata[0]) << 24) +(((uint32_t)cdata[1]) << 16) + (((uint32_t)cdata[2]) << 8) + cdata[3]; //CRC registerfor (uint32_t i = 4; i < clen; i++){for (uint8_t j = 0; j <= 7; j++){if(data_t&0x80000000)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-32校验函数查表原理优化
CRC查表原理通过输入数据分段计算(CRC-32可按四字节/单字节分段)原理实现校验码的计算,查表法有如下特点:
- 当前输入数据段值异或当前的查表值,得到当前的CRC计算余数
- 当前查表值由前一计算余数与校验码按CRC校验过程计算得到,并保存为对应前一计算余数对应的表位值。
- 当前查表值的计算不受当前输入字段值影响,所以当前输入字段值为0且为最后字段时,当前查表值异或当前输入字段值不变,此时当前查表值即为CRC校验值结果。
- 由第2和3条可知,第2条在CRC校验过程计算时,移位补位时补0即可,也就不需要当前字段值进入移位补位过程。
CRC-32校验32位数据格式函数优化为如下代码,注意输入数据为32位数组:
uint32_t PY_CRC_32_T32(uint32_t *di, uint32_t len){uint32_t crc_poly = 0x04C11DB7;//X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+1 total 32 effective bits without X^32.uint32_t data_t = 0; //CRC registerfor(uint32_t i = 0; i < len; i++){data_t ^= di[i]; //32-bit datafor (uint8_t j = 0; j < 32; j++){if (data_t & 0x80000000)data_t = (data_t << 1) ^ crc_poly;elsedata_t <<= 1;}}return (data_t);}
CRC-32校验8位数据格式函数优化为如下代码,输入数据为8位数组:
uint32_t PY_CRC_32_T8(uint8_t *di, uint32_t len){uint32_t crc_poly = 0x04C11DB7;//X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+1 total 32 effective bits without X^32. uint32_t data_t = 0; //CRC registerfor(uint32_t i = 0; i < len; i++){data_t ^= di[i]<<24; //8-bit datafor (uint8_t j = 0; j < 8; j++){if (data_t & 0x80000000)data_t = (data_t << 1) ^ crc_poly;elsedata_t <<= 1;}}return (data_t);}
查表法对应输入数据分段为四字节时,采用如上几种方式任何一种,对四字节0~4294967295的输入数分别进行CRC-32校验,得到的各个校验值,也就得到查表法对应每个输入数值的查表值。因为这种表数据量太大不会被实际用。
查表法对应输入数据分段为单字节时,采用如上几种方式任何一种,对单字节0~255的输入数分别进行CRC-32校验,得到的各个校验值,也就得到查表法对应每个输入数值的查表值。
以上各种针对基本原理标准的校验函数计算结果相同。
CRC-32校验注意事项
实际应用中,由于输入数据前处理和输出数据后处理的不同,产生了不同的CRC应用标准,其中一些是一些知名厂家为自己的产品定义CRC校验函数。这些处理特性包括CRC寄存器初始值设置,数据字节位反转,数据字节高位还是低位优先进入计算,输出的整个数据是否按位反转,输出数据是否和一个数异或等。常见的一些CRC-32校验特性:
另外,ST公司STM32芯片硬件CRC32计算过程特性也和上述两种计算过程特性存在差别。而这几种因为都有前处理和后处理过程特性,所以和上面介绍的原理级定义的CRC32校验函数(无前后处理)计算出来的值不同,是在CRC32标准校验函数基础上调整得到各自的CRC32校验函数。
CRC-32常用(事实标准)校验函数
CRC-32常用(事实标准)检验函数针对能够从硬件上进行比特流实时计算而设计,即每个数据的一位进来后马上就能进行CRC-32,而不必收全数据再计算。因此对应的软件CRC-32存在一些特点:
- 初始值预设为0xFFFFFFFF
- 针对数据字节的低位先传输场景,因此数据字节的低位是高优先处理的
- 按照字节分段进行CRC-32计算,字节放在寄存器的低字节,因此字节最低位在左高由低的最右边一位,在进行CRC计算过程时,要从最低位/最右侧位置开始判断,移位时向右移出。因为校验码高冥端也要相应对齐,所以检验码也就要做倒位序,如0x04C11DB7(0000 0100 1100 0001 0001 1101 1011 0111)倒序为了0xEDB88320(1110 1101 1011 1000 1000 0011 0010 0000)
- 输出异或0xFFFFFFFF
CRC-32常用(事实标准)校验函数为反向算法(反向算法是从由右向左计算,也即计算过程中移位时,向右移出。):
uint32_t PY_CRC_32_M(uint8_t *di, uint32_t len){uint32_t crc_poly = 0xEDB88320;//Inversion bit sequence of 0x04C11DB7uint32_t data_t = 0xFFFFFFFF; //initial valuefor(uint32_t i=0; i<len; i++){data_t ^=di[i];for (int8_t j = 8; j > 0; --j){data_t = (data_t >> 1) ^ ((data_t & 1)" />: 0);}}return data_t ^ 0xFFFFFFFF;}
对”CRC32”的计算结果为:
通过网上工具验证:
CRC-32 STM32格式校验函数
常用CRC32校验函数来自反向CRC32算法最简化的设计思想,而STM32格式CRC32校验函数来自正向CRC32算法最简化的设计思想, 参见《C语言CRC-32 STM32格式校验函数》。
–End–