CRC校验基本原理CRC8和CRC16

CRC定义:

CRC即循环冗余校验码,是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。

CRC校验原理:

其根本思想就是先在要发送的帧后面附加一个数(这个就是用来校验的校验码,但要注意,这里的数也是二进制序列的,下同),生成一个新帧发送给接收端。当然,这个附加的数不是随意的,它要使所生成的新帧能与发送端和接收端共同选定的某个特定数整除(注意,这里不是直接采用二进制除法,而是采用一种称之为“模2除法”)。到达接收端后,再把接收到的新帧除以(同样采用“模2除法”)这个选定的除数。因为在发送端发送数据帧之前就已通过附加一个数,做了“去余”处理(也就已经能整除了),所以结果应该是没有余数。如果有余数,则表明该帧在传输过程中出现了差错。

模2除法:

模2除法与算术除法类似,但每一位除的结果不影响其它位,即不向上一位借位,所以 实际上就是异或。在循环冗余校验码(CRC)的计算中有应用到模2除法。

CRC校验步骤:

CRC校验中有两个关键点,一是预先确定一个发送送端和接收端都用来作为除数的二进制比特串(或多项式),可以随机选择,也可以使用国际标准,但是最高位和最低位必须为1;二是把原始帧与上面计算出的除数进行模2除法运算,计算出CRC码

具体步骤:

  1. 选择合适的除数(最高位和最低位必须为1
  2. 看选定除数的二进制位数,然后再要发送的数据帧上面加上(这个位数-1)位的0,然后用新生成的帧以模2除法的方式除上面的除数,得到的余数就是该帧的CRC校验码。注意,余数的位数一定只比除数位数少一位,也就是CRC校验码位数比除数位数少一位,如果前面位是0也不能省略。
  3. 将计算出来的CRC校验码附加在原数据帧后面,构建成一个新的数据帧进行发送;最后接收端在以模2除法方式除以前面选择的除数,如果没有余数,则说明数据帧在传输的过程中没有出错。

CRC8的原理

在发送节点,根据要传送的m位二进制码序列,以一定的规则(CRC校验有不同的规则,在差错控制理论中称为“生成多项式”)产生一个校验用的n位校验码(CRC码),附在原始报文中(一般在报文的最后位置),构成一个新的二进制码序列数共m+n位,然后发送出去。在接收节点,根据报文信息和CRC码之间遵循的规则(即与发送时生成CRC校验码相同的规则)进行检验,校验采用计算机的模二除法(即生成多项式)做异或运算,进行异或运算时除数和被除数最高位对齐,进行按位异或运算,若最终的数据能被除尽,则传输正确;否则,传输错误。

CRC8标准生成多项式

CRC-8:x8+x5+x^4+1 0x31 (0x131)

CRC-8:x8+x2+x^1+1 0x07 (0x107)

CRC-8:x8+x6+x4+x3+x2+x1 0x5E (0x15E)

由于多项式的最高为都为1,并且在代码的CRC8计算中,最高位也是不使用的, 所以在多项式记录时都去掉了最高位

大小端概念理解

Intel(小端):低字节LSB在前,高字节MSB在后。Motorola(大端):高字节MSB在前,低字节LSB在后。
举个例子。比如,现在要传输车速信号,数据为0x1234。

Motorola:

​数据位 data[0] data[1]

数据值 0x12 0x34


data[0] = 0x1234 & 0x0FF00>> 8;

data[1] = 0x1234 & 0x000FF;

value = data[0]*256 + data[1];

Intel

​数据位 data[0] data[1]

数据值 0x34 0x12


data[0] = 0x1234 & 0x000FF;

data[1] = 0x1234 & 0x0FF00 >> 8;

value = data[0] + data[1]*256;

CRC计算过程

下面,我们以 x 8 + x 5 + x 4 + 1. x^8+x^5+x^4+1. x8+x5+x4+1.(0x31,二进制为:100110001)多项式为例,计算一个字节:0x11(二进制为:00010001)

计算过程:

  1. 需要计算的数据左移8位,移位后数据为:0001 0001 0000 000

  2. 先进行高9位异或,0001 0001 0000 0000,因为高9bit的最高bit为0,不需要进行异或,同理,接下来的两bit也是0,也不需要进行进行异或。这样处理后数据为:1 0001 0000 0000;

  3. 接下来最高位为1,需要进行异或操作了。当多项式最高位为1,才进行异或计算,异或后最高位为0,下次也不需要异或,这样需要采用代码计算的方式,就可以把最高位去掉,不需要异或,最后结果也是一样的。 对于上面的计算过程,我们计算过程如下
    1000100000000 001100010000 100001000 00110001 00111001 00110001 = 00111001 \sqrt[00110001]{\frac{\frac{\frac{\frac{1000100000000}{001100010000} }{100001000} }{00110001} }{00111001} } = 00111001 0011000100111001001100011000010000011000100001000100000000 =00111001
    采用代码实现的方式如下:

nsigned char crc_high_first(unsigned char ptr, unsigned char len)
{
  unsigned char i;
  unsigned char crc=0x00/ 计算的初始crc值 */
  while(len--)
  {
    crc ^= *ptr++;  /* 每次先与需要计算的数据异或,计算完指向下一数据 */
    for (i=8; i>0; --i)   /* 下面这段计算过程与计算一个字节crc一样 */
    { 
    if (crc & 0x80)
      crc = (crc << 1) ^ 0x31;
    else
      crc = (crc << 1);
    }
  }
  return (crc); 
}

查表法

上面的CRC计算是纯采用逻辑运行的方式,可以看出运行量还是挺大的。每一个字节都需要进行8次判断、移位、异或操作。如果采用查表法,可以大大减少计算量,先计算出0x00~0xFF每一个字节的crc校验结果,然后通过表来查出每个字节的crc结果,大大减少计算量。

static const unsigned char crc_table[] =
{
0x00,0x31,0x62,0x53,0xc4,0xf5,0xa6,0x97,0xb9,0x88,0xdb,0xea,0x7d,0x4c,0x1f,0x2e,0x43,0x72,0x21,0x10,0x87,0xb6,0xe5,0xd4,0xfa,0xcb,0x98,0xa9,0x3e,0x0f,0x5c,0x6d,0x86,0xb7,0xe4,0xd5,0x42,0x73,0x20,0x11,0x3f,0x0e,0x5d,0x6c,0xfb,0xca,0x99,0xa8,0xc5,0xf4,0xa7,0x96,0x01,0x30,0x63,0x52,0x7c,0x4d,0x1e,0x2f,0xb8,0x89,0xda,0xeb,0x3d,0x0c,0x5f,0x6e,0xf9,0xc8,0x9b,0xaa,0x84,0xb5,0xe6,0xd7,0x40,0x71,0x22,0x13,0x7e,0x4f,0x1c,0x2d,0xba,0x8b,0xd8,0xe9,0xc7,0xf6,0xa5,0x94,0x03,0x32,0x61,0x50,0xbb,0x8a,0xd9,0xe8,0x7f,0x4e,0x1d,0x2c,0x02,0x33,0x60,0x51,0xc6,0xf7,0xa4,0x95,0xf8,0xc9,0x9a,0xab,0x3c,0x0d,0x5e,0x6f,0x41,0x70,0x23,0x12,0x85,0xb4,0xe7,0xd6,0x7a,0x4b,0x18,0x29,0xbe,0x8f,0xdc,0xed,0xc3,0xf2,0xa1,0x90,0x07,0x36,0x65,0x54,0x39,0x08,0x5b,0x6a,0xfd,0xcc,0x9f,0xae,0x80,0xb1,0xe2,0xd3,0x44,0x75,0x26,0x17,0xfc,0xcd,0x9e,0xaf,0x38,0x09,0x5a,0x6b,0x45,0x74,0x27,0x16,0x81,0xb0,0xe3,0xd2,0xbf,0x8e,0xdd,0xec,0x7b,0x4a,0x19,0x28,0x06,0x37,0x64,0x55,0xc2,0xf3,0xa0,0x91,0x47,0x76,0x25,0x14,0x83,0xb2,0xe1,0xd0,0xfe,0xcf,0x9c,0xad,0x3a,0x0b,0x58,0x69,0x04,0x35,0x66,0x57,0xc0,0xf1,0xa2,0x93,0xbd,0x8c,0xdf,0xee,0x79,0x48,0x1b,0x2a,0xc1,0xf0,0xa3,0x92,0x05,0x34,0x67,0x56,0x78,0x49,0x1a,0x2b,0xbc,0x8d,0xde,0xef,0x82,0xb3,0xe0,0xd1,0x46,0x77,0x24,0x15,0x3b,0x0a,0x59,0x68,0xff,0xce,0x9d,0xac
}
unsigned char cal_crc_table(unsigned char *ptr, unsigned char len)
{
  unsigned char crc = 0x00;
  while (len--)
  {
    crc = crc_table[crc ^ *ptr++];
  }
  return (crc);
}

小端CRC计算

unsigned char cal_table_low_first(unsigned char value)
{
  unsigned char i, crc;
  crc = value;
  /* 同样需要计算8次 */
  for (i=8; i>0; --i)
  { 
    if (crc & 0x01)  /* 反序异或变成判断最低位是否为1 */
      /* 数据变成往右移位了 */
      /* 计算的多项式从0x31(0011 0001)变成了0x8C (1000 1100) */
      /* 多项式值,原来的最高位变成了最低位,原来的最低位变成最高位,8位数据高低位交换一下位置 */
      crc = (crc >> 1) ^ 0x8C;
    else
      crc = (crc >> 1);
  }
  return crc;
}

CRC16

除了CRC8校验外,还有CRC16,CRC32,再说下以下几种常见的CRC16标准,被用在各个规范中,算法原理基本一致,就是在数据的输入和输出有所差异,下将把这些标准的差异列出。
CRC16_CCITT:

多项式x16+x12+x^5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0x0000异或;

CRC16_CCITT_FALSE:

多项式x16+x12+x^5+1(0x1021),初始值0xFFFF,低位在后,高位在前,结果与0x0000异或;

CRC16_XMODEM:

多项式x16+x12+x^5+1(0x1021),初始值0x0000,低位在后,高位在前,结果与0x0000异或;

CRC16_X25:

多项式x16+x12+x^5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或;

CRC16_MODBUS:

多项式x16+x15+x^2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0x0000异或;

CRC16_IBM:

多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0x0000异或;CRC16_MAXIM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或;

CRC16_USB:

多项式x16+x15+x^2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0xFFFF异或;

CRC16的算法原理:

  1. 根据CRC16的标准选择初值CRCIn的值;

  2. 将数据的第一个字节与CRCIn高8位异或;

  3. 判断最高位,若该位为 0 左移一位,若为 1 左移一位再与多项式Hex码异或;

  4. 重复3直至8位全部移位计算结束;

  5. 重复将所有输入数据操作完成以上步骤,所得16位数即16位CRC校验码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值