CRC16的算法原理:
1、根据CRC16的标准选择初值CRCLn的值。
2、将数据的第一个字节与CRCLn的高8位异或。
3、判断最高位,若该位为0,左移1位,若为1左移一位再与多项式Hex码异或。
4、重复3直至8个位全部移位计算结束。
5、重复将所有输入数据操作完成以上步骤,所得的16位数即为16位CRC校验码。
常见的几组CRC公式:
1、CRC16/IBM 或 CRC16/ARC 或 CRC16/LHA:
公式:x16+x15+x2+1
宽度:16
Poly值:0x8005
初始值:0x0000
基准输入:true
基准输出:true
标志位:0x0000
2、CRC16/MAXIM:
公式:x16+x15+x2+1
宽度:16
Poly值:0x8005
初始值:0x0000
基准输入:true
基准输出:true
标志位:0xFFFF
3、CRC16/USB:
公式:x16+x15+x2+1
宽度:16
Poly值:0x8005
初始值:0xFFFF
基准输入:true
基准输出:true
标志位:0xFFFF
4、CRC16/MODBUS(最常见):
公式:x16+x15+x2+1
宽度:16
Poly值:0x8005
初始值:0x0000
基准输入:true
基准输出:true
标志位:0x0000
5、CRC16/CCITT 或 CRC-CCITT 或CRC16/CCITT-TRUE或 CRC16/KERMIT:
公式:x16+x15+x5+1
宽度:16
Poly值:0x1021
初始值:0x0000
基准输入:true
基准输出:true
标志位:0x0000
6、 CRC16/CCITT-FALSE:
公式:x16+x15+x5+1
宽度:16
Poly值:0x1021
初始值:0xFFFF
基准输入:false
基准输出:false
标志位:0x0000
7、CRC16/X25:
公式:x16+x15+x5+1
宽度:16
Poly值:0x1021
初始值:0x0000
基准输入:true
基准输出:true
标志位:0xFFFF
8、CRC16/XMODEM 或 CRC16/ZMODEM 或 CRC16/ACORN:
公式:x16+x15+x5+1
宽度:16
Poly值:0x1021
初始值:0x0000
基准输入:false
基准输出:false
标志位:0x0000
9、CRC16/DNP:
公式:x16+x13+x12+x11+x10+x8+x6+x5+x2+1
宽度:16
Poly值:0x3D65
初始值:0x0000
基准输入:true
基准输出:true
标志位:0xFFFF
使用:M-Bus, ect
关于数据位序:如果我们的机器是小端模式(低位在后,高位在前),而数据输入的要求是低位在前高位在后时,就需要对数据顺序进行反转。
多项式的值是如果计算的:
以多项式:x16 + x12 + x5 + 1 为例
x16表示第16位为1,x12表示第12位为1,x5表示第5位为1
(1<<16) | (1<<12) | (1<<5) | 1 = 1 0001 000 0010 0001 = 0x1 1021
CRC16只取上面计算值的低16位所以最终结果为0x1021。
C语言实现:以CRC16/CCITT 公式:x16+x15+x5+1
#define CRC_INIT 0xFFFF //CRC初始值
#define CRC_CODE 0x1021
#define BIT(num, x) ((num>>x) & 0x1)
/*16位 位逆转*/
unsigned short InvertUint16(unsigned short src){
unsigned short temp = 0;
for(int bit=0; bit<16; bit++){
if(src & (1<<bit))
temp |= 1<<(15-bit);
}
return temp;
}
/*@data 要计算的数据
* @init CRC16 初始值
* @poly CRC16 poly值
*@len 数据长度
* */
unsigned short CRC16(unsigned char * data,unsigned short init, unsigned short poly,unsigned int length){
unsigned short crc = init;
unsigned char one = 0;
while(length--){
one = InvertUint8(*data++);
crc ^= (one<<8); //与CRC的高8位异或
for(int bit=0; bit<8; bit++){
if(crc & 0x8000){ //最高位为1,左移一位与hex码异或
crc = (crc<<1)^poly;
}else{
crc = crc<<1;
}
}
}
return InvertUint16(crc); /*输出逆序返回*/
}
上面的CRC计算纯采用逻辑运行的方式,运行量比较大,每一个字节 都要进行8次判断、移位、或异或操作。可以采用查表法减少计算量,先计算出从0x00到0xff之间每一个字节的CRC校验结果,然后可以通过查表来查出每个字节的CRC结果,减少计算量。