赞
踩
CRC为循环冗余校验码,是一种常用的、具有检错、纠错能力的校验码。通常发送方在发送的数据之后,附上其CRC校验码。接收方收到数据后,也做同样的CRC校验,得到本地CRC校验码,并和接收到的CRC校验码比较,如果一致,则认为数据无误,如果不一致,则认为发收过程中出错。
CRC校验码是被计算数据和计算码的二进制模二除法的余数。二进制模二除法和二进制常规算法的区别是,模二除法在除法运算过程中采用模二减法,不产生借位,也就是0-1=1,0-0=0,1-1=0,1-0=1;而00-01=01,10-01=11。也就是按位异或的效果。
以MLX90614测温芯片的CRC-8校验为例,其计算码多项式为X^8 + X^2 + X^1 + 1
,也就是对应9个二进制位的数据,即100000111,第一位和最后一位都是1,也即0x107。在计算时,对于计算数据要计算到最后一位,因此实际计算时,计算数据要左移8位,再与0x107进行模二除法。
算法方面,要首先对计算数据进行从高位开始的第一个二进制“1”的位置搜索,因为模二除法从这里开始启动。
对于MLX90614读取的温度数据长度为5个字节,左移8位后就是6个字节,可以用64位无符号长整型装载。而9位的计算码可以用16位无符号短整型装载。
在算法计算过程中,要考虑前面一次计算后,余数已小于计算码时,需要给余数从计算数据中进行补位的各种情况,包括余数已为全0而计算还未完成的情况,所以每次补位的长度不一样。这里需要注意,补位是补到余数的后面,而不是和余数直接进行异或,而是补位后的余数再和计算码进行模二减法。
当计算到已无法通过补位继续进行计算的时候,则此余数就是最后的校验码。
MLX90614读操作的时序如下:
前面5个字节数据的校验码为PEC(第六个字节), 也即设计的校验函数PY_CRC_MLX90614_READ(0xb4, 0x07, 0xd2, 0x3a)计算结果为0x30。注意这里根据MLX90614输入数据特征在函数内部做了处理,输入变量是4个字节,实际对应是5个字节。
校验函数设计如下:
uint8_t PY_CRC_MLX90614_READ(uint8_t daddr, uint8_t Raddr, uint8_t dl, uint8_t dh) { //Written by Pegasus Yu 2022/02/22 uint64_t cdata = 0; //Computed total data uint16_t data_t = 0; //Process data of CRC computing uint16_t crc_poly = 0x0107; //X^8+X^2+X^1+1 total 9 effective bits. Computed total data shall be compensated 8-bit '0' before CRC computing from 9-1=8. uint16_t index_t = 47; ///bit shifting index for initial '1' searching uint16_t index = 47; //bit shifting index for CRC computing uint8_t rec = 0; //bit number needed to be compensated for next CRC computing cdata |= (((uint64_t)daddr)<<40); //device write address cdata |= (((uint64_t)Raddr)<<32); //register access address cdata |= (((uint64_t)(daddr+1))<<24); //device read address cdata |= (((uint64_t)dl)<<16); //data LSB cdata |= (((uint64_t)dh)<<8); //data HSB //8-bit '0' compensated into cdata so cdata involves 48 bits stored in 64-bit format. while(index_t>0) { if( (cdata>>index_t)&1 ) { index = index_t; index_t = 0; data_t |= (cdata>>(index-8)); { data_t = data_t ^ crc_poly; } while((index!=0x5555)&&(index!=0xaaaa)) { if ((data_t>>7)&1) rec = 1; else if ((data_t>>6)&1) rec = 2; else if ((data_t>>5)&1) rec = 3; else if ((data_t>>4)&1) rec = 4; else if ((data_t>>3)&1) rec = 5; else if ((data_t>>2)&1) rec = 6; else if ((data_t>>1)&1) rec = 7; else if ((data_t>>0)&1) rec = 8; else rec = 9; /// if((index-8)<rec) { data_t = data_t<<(index-8); data_t |= (uint16_t)((cdata<<(64-(index-8)))>>(64-(index-8))); index = 0x5555; } else { for(uint8_t i=1;i<=rec;i++) { data_t = (data_t<<1)|((cdata>>(index-8-i))&1) ; } if(rec!= 9) { data_t = data_t ^ crc_poly; index -= rec; } else { data_t = 0; index_t = index-8-1; index = 0xaaaa; } } } if(index==0x5555) break; } else { index_t--; if(index_t<8) break; } } return (uint8_t)data_t; }
如上面的算法原理解释,这里的简单校验函数只支持到7个字节的输入数据校验,如果计算数据太长,则需要进行升级设计,设计上可以按照64位进行分段,当高一段64位计算到尽头时,在下一段64位开始对余数进行位补,以使得模二减法能继续进行下去。直到最后一个64位段计算到尽头,则余数是CRC-8的8位校验码。
而当进行的是CRC-16或CRC-32校验码计算时,依然可升级上述算法进行实现,因为无符号64位宽度能够覆盖16位和32位的模二除法(减法)计算要求。这是本算法相对于其它用8位单字节宽度来分段计算数据的算法的特点。
–End–
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。