最近在帮客户编写基于s32k144编写tps929120的软件驱动时,需要在发送数据的末端增加8bit的crc校验码。因为之前使用e522.49时,其数据手册直接提供了自身crc校验算法的代码,而tps929120的手册只告知了算法的crc多项式为 ,初始值为0xff,所以代码部分需要自己实现。
我查阅了tld7002,bd18331,mpq7225等led driver的数据手册,都是采用的crc校验算法,其中mpq7225的crc校验算法和tps929120是一样的。因为之前接触crc校验不多,特定将相关的学习过程记录下来,方便后面有需要时快速完成其他厂家的led driver软件驱动。
crc基础知识这一章节主要介绍crc概念和参数模型(来源于网络)
crc概念crc(cyclic redundancy checksum)是一种纠错技术,代表循环冗余校验和。
数据通信领域中最常用的一种差错校验码,其信息字段和校验字段长度可以任意指定,但要求通信双方定义的crc标准一致。主要用来检测或校验数据传输或者保存后可能出现的错误。它的使用方式可以说明如下图所示:
在数据传输过程中,无论传输系统的设计再怎么完美,差错总会存在,这种差错可能会导致在链路上传输的一个或者多个帧被破坏(出现比特差错,0变为1,或者1变为0),从而接受方接收到错误的数据。
为尽量提高接受方收到数据的正确率,在接收方接收数据之前需要对数据进行差错检测,当且仅当检测的结果为正确时接收方才真正收下数据。检测的方式有多种,常见的有 奇偶校验 、因特网校验和循环冗余校验等。
crc参数模型一个完整的crc参数模型应该包含以下信息: width,poly,init,refin,refout,xorout 。
name :参数模型名称。width :宽度,即生成的crc数据位宽,如crc-8,生成的crc为8位poly :十六进制多项式,省略最高位1,如 ,二进制为 1 0000 0111 ,省略最高位1,转换为十六进制为0x07。init :crc初始值,和width位宽一致。refin :true或false,在进行计算之前,原始数据是否翻转,如原始数据:0x34 = 0011 0100 ,如果refin为true,进行翻转之后为0010 1100 = 0x2crefout :true或false,运算完成之后,得到的crc值是否进行翻转,如计算得到的crc值:0x97 = 1001 0111 ,如果refout为true,进行翻转之后为11101001 = 0xe9。xorout :计算结果与此参数进行异或运算后得到最终的crc值,和width位宽一致。通常如果只给了一个多项式,其他的没有说明则: **init=0x00,refin=false,refout=false,xorout=0x00** 。
tps929120的crc值计算方式crc参数:根据数据手册,tps929120选择的是crc多项式为 ,初始值为0xff;所以poly为0x31,init为ff。由于uart的数据传输是lsb(先传输低位),所以refin为true。综上所述,tps929120的crc参数表为:
name = crc_tps929120width = 8poly = 0x31 = 0011 0001(最高位1已经省略)init = 0xffxorout = 0x00refin = truerefout = false算法图:tps929120数据手册提供了自身crc校验的算法图,如下所示:
因为tps929120要求第一个发送的数据0x55(用于同步)不参与计算,所以下面介绍的几种计算方式都不会将0x55纳入crc计算范围。
计算方法一:方法一按照tps929120提供的算法图进行实现。
计算步骤:结合tps929120数据手册提供的算法图,方法一的计算步骤如下所示:
1. 将0xff作为crc初始值。2. 当第一个输入字节的bit0到来时, 2.1 将初始值的bit7和输入值的bit0进行异或运算,得到的值保留,记作temp_bit; 2.2 将初始值的bit6代替原本的bit7; 2.3 将初始值的bit5代替原本的bit6; 2.4 将初始值的bit4和temp_bit进行异或运算,使用得到的值代替原本的bit5; 2.5 将初始值的bit3和temp_bit进行异或运算,使用得到的值代替原本的bit4; 2.6 将初始值的bit2代替原本的bit3; 2.7 将初始值的bit1代替原本的bit2; 2.8 将初始值的bit0代替原本的bit1; 2.9 使用temp_bit代替原本的bit0;3. 第一个输入字节的剩余7bit也是同样的操作,然后得到第一个输入字节的crc值。4. 第二个输入字节到来,使用上一次计算得到的crc值作为crc初始值,重复2,3步骤。5. 当最后一个输入字节计算完毕之后,就得到了最终的crc校验值。实现代码为:单个字节的crc值计算#define bit0 (0x01)#define bit1 (0x02)#define bit2 (0x04)#define bit3 (0x08)#define bit4 (0x10)#define bit5 (0x20)#define bit6 (0x40)#define bit7 (0x80)unsigned int crc_calculation(unsigned int crc_initial, unsigned int input_data){ unsigned int temp_bit, input_bit; /* store every bit value of input_data */ unsigned int bit0, bit1, bit2, bit3, bit4, bit5, bit6, bit7; /* store the input_data (byte) 's crc */ unsigned int crc=0; /* get every bit of crc initial value */ unsigned int i=0; bit0 = crc_initial & bit0; bit1 = (crc_initial & bit1)>>1; bit2 = (crc_initial & bit2)>>2; bit3 = (crc_initial & bit3)>>3; bit4 = (crc_initial & bit4)>>4; bit5 = (crc_initial & bit5)>>5; bit6 = (crc_initial & bit6)>>6; bit7 = (crc_initial & bit7)>>7; for(i=0; i> i) & 0x01; /* do input_bit xor bit7 */ temp_bit = input_bit ^ bit7; bit7 = bit6; bit6 = bit5; /* do bit4 xor temp_bit */ bit4 = bit4 ^ temp_bit; bit5 = bit4; /* do bit3 xor tmep_bit */ bit3 = bit3 ^ temp_bit; bit4 = bit3; bit3 = bit2; bit2 = bit1; bit1 = bit0; bit0 = temp_bit; } crc = (bit7<<7)|(bit6<<6)|(bit5<<5)|(bit4<<4)|(bit3<<3)|(bit2<<2)|(bit1<<1)|bit0; return crc;}命令帧的crc值计算unsigned int crc(unsigned int commandframe_withoutcrc[], unsigned int bytelength){ unsigned int j; unsigned int crctemp; for(j=0; j计算方法二:github上面有一套成熟的开源crc算法,如下,
github - whik/crc-lib-c: 基于c语言的crc校验库,包括常用的21个crc参数模型实现^[1]^
参考其中的 crc-8/maxim ,并根据tps929120的crc参数进行微调,得到的算法如下:
/* as uart transmit from lsb to msb, * invert the polynomial 0x31 (0011 0001) to 0x8c (1000 1100) */#define polynomialinv 0x8c #define lsb 0x01/* calculate crc of command frame */unsigned int crc_lut(unsigned int commandframe_withoutcrc[], unsigned int bytelength) { unsigned int remainder, k, j; /* assign the initial value 0xff */ remainder = 0xff; /* the first sync byte not engage crc calculation */ for(k=1;k计算方法三:方法三在方法二的基础上进行优化,将crc的计算部分(如下图)提前做成数组,涵盖0-255的crc计算结果,然后用到的时候直接查表。
for(i=0; i>1) ^ polynomialinv; } else { remainder = remainder>>1; } }最终的实现形式如下:
/* as uart transmit from lsb to msb, * invert the polynomial 0x31 (0011 0001) to 0x8c (1000 1100) */#define polynomialinv 0x8c #define lsb 0x01unsigned int crcarray[256];/* calculate and store the crc of data from 0x00 to 0xff */void crcinitial() { unsigned int k, j, remainder; for(k=0;k0;j--) { if(remainder & lsb) { /* right shift 1 bit and do the xor operation with 0x8c */ remainder = (remainder>>1) ^ polynomialinv; } else { remainder = remainder>>1; } } crcarray[k]=remainder; }}/* calculate crc of command frame */unsigned int crc_lut(unsigned int commandframe_withoutcrc[], unsigned int bytelength) { unsigned int remainder, tempdata, k; /* assign the initial value 0xff */ remainder = 0xff; /* the first sync byte not engage crc calculation */ for(k=1;k验证:使用一个数组{0x55,0x80,0x61,0x00}进行验证,其中0x55不参与crc计算,以计算方法一为例,添加打印的mian函数如下:
#include int main(){ unsigned int data[4] = {0x55,0x80,0x61,0x00}; unsigned int crc; for(int i = 0; i < 4; i++) { printf(%02x , data[i]); } printf(\\n); crc = crc(data,4); printf(method 1:crc result of tps929120 is %02x\\n, crc); return 0;}使用在线c语言编译器(菜鸟教程在线编辑器 (runoob.com) ^[2]^ )进行验证,计算结果如下图,为0x74:
使用在线crc计算工具进行验证,结果也是0x74:
使用tps929120自带的excel形式的crc计算工具进行验证,结果也是0x74:
其他两种方式经过验证也是一样的结果,有兴趣的读者可以使用在线c语言编辑器试一下。
耐高温300C热塑型TPI聚酰亚胺薄膜FILM
便携式自动气象站性能怎么样?
Rambus携手莱迪思开发下一代安全解决方案
文丘里管流量计的原理
紫光发力国产存储芯片“刁高”应用领军出击
TPS929120的CRC校验的三种实现方法
二极管符号大全及电路图形符号
爱立信确认在美国设工厂,全球产业链开始分化?
ARM计划将物联网服务业务分拆到软银
看完就明白为啥需要VLAN了
研究人员用锂换钠可减轻锂的财政和环境负担
“充电领导者”,IDT研讨会纪实!(续)
压力传感器的各种材料和方法
一加7ProDxOMark评分公布 与榜单前两名仅差一分
同样是BGA扇出,为什么别人设计出来的性能就是比你好!
GP8202实际应用的测评,张力控制器4-20mA控制
R汽车广州车展爆点:5G车预售
PCB八层板和“假八层板”有什么不同?
憋尿引起身体不适
敏源传感科技有限公司正式入驻ISweek工采网