数据校验--CRC校验

工作原理:
    CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。
    循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。
    循环冗余校验码(CRC)的基本原理是:在K位信息码后再拼接R位的校验码,整个编码长度为N位,因此,这种编码也叫(N,K)码。
    对于一个给定的(N,K)码,可以证明存在一个最高次幂为N-K=R的多项式G(x)。根据G(x)可以生成K位信息的校验码,而G(x)叫做这个CRC码的生成多项式。 
    校验码的具体生成过程为:假设要发送的信息用多项式C(X)表示,将C(x)左移R位(可表示成C(x)*2^R),这样C(x)的右边就会空出R位,这就是校验码的位置。
    用 C(x)*2^R 除以生成多项式G(x)得到的余数就是校验码。
    任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和‘1’取值的多项式一一对应。例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为x5+x3+x2+x+1对应的代码101111。
基本概念:
    对应关系:
        多项式和二进制数有直接对应关系:X的最高幂次对应二进制数的最高位,以下各位对应多项式的各幂次,有此幂次项对应1,无此幂次项对应0。
        可以看出:X的最高幂次为R,转换成对应的二进制数有R+1位。
        多项式包括生成多项式G(X)和信息多项式C(X)。
        如生成多项式为G(X)=X4+X3+X+1, 可转换为二进制数码11011
        而发送信息位 101111,可转换为数据多项式为C(X)=X5+X3+X2+X+1。
    生成多项式:
        是接受方和发送方的一个约定,也就是一个二进制数,在整个传输过程中,这个数始终保持不变。
        在发送方,利用生成多项式对信息多项式做模2除生成校验码。在接收方利用生成多项式对收到的编码多项式做模2除检测和确定错误位置。
        应满足以下条件:
            A、生成多项式的最高位和最低位必须为1。
            B、当被传送信息(CRC码)任何一位发生错误时,被生成多项式做除后应该使余数不为0。
            C、不同位发生错误时,应该使余数不同。
            D、对余数继续做除,应使余数循环。
    校验码位数:
        CRC校验码位数 = 生成多项式位数 - 1。注意有些生成多项式的简记式中将生成多项式的最高位1省略了。
    生成步骤:
        1、将X的最高次幂为R的生成多项式G(X)转换成对应的R+1位二进制数。
        2、将信息码左移R位,相当于对应的信息多项式C(X)*2^R。
        3、用生成多项式(二进制数)对信息码做除,得到R位的余数(注意:这里的二进制做除法得到的余数其实是模2除法得到的余数,并不等于其对应十进制数做除法得到的余数。)。
        4、将余数拼到信息码左移后空出的位置,得到完整的CRC码。
        例:假设使用的生成多项式是G(X)=X3+X+1。4位的原始报文为1010,求编码后的报文。
        1、将生成多项式G(X)=X3+X+1转换成对应的二进制除数1011。
        2、此题生成多项式有4位(R+1)(注意:4位的生成多项式计算所得的校验码为3位,R为校验码位数),要把原始报文C(X)左移3(R)位变成1010 000
        3、用生成多项式对应的二进制数对左移3位后的原始报文进行模2除(高位对齐),相当于按位异或:
        1010000
        1011
        ————————————
        0001000
        1000
        1011
        ————————————
        0011
        得到的余位011,所以最终编码为:1010 011
生成方法:
    借助于模2除法则,其余数为校验字段。
    例如:信息字段代码为: 1011001;对应m(x)=x6+x4+x3+1
    假设生成多项式为:g(x)=x4+x3+1;则对应g(x)的代码为: 11001
    x^4*m(x)=x10+x8+x7+x4 对应的代码记为:10110010000(相当于1011001<<4);
    采用模2除法则: 得余数为: 1010(即校验字段为:1010)
    发送方:发出的传输字段为: 10110011010 信息字段+校验字段
    接收方:使用相同的生成码进行校验:接收到的字段/生成码(二进制除法)如果能够除尽,则正确,
    给出余数(1010)的计算步骤:
    除法没有数学上的含义,而是采用计算机的模二除法,即除数和被除数做异或运算。进行异或运算时除数和被除数最高位对齐,按位异或。
    10110010000
    11001
    ————————————
    01111010000
    1111010000
    11001
    ————————————
    0011110000
    11110000
    11001
    ————————————
    00111000
    111000
    11001
    ————————————
    001010
    1010
    则四位CRC校验码就为:1010。
    利用CRC进行检错的过程可简单描述为:在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的r位监督码(CRC码),附在原始信息后边,构成一个新的二进制码序列数共k+r位,
    后发送出去。在接收端,根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。这个规则,在差错控制理论中称为“生成多项式”。
位宽:
    有一点要特别注意:生成多项式经常会说到多项式的位宽(Width,简记为W),这个位宽不是多项式对应的二进制数的位数,而是二进制位数减1。比如CRC8中用到的位宽为8的生成多项式,
    其实对应得二进制数有九位:100110001。另外一点,多项式表示和二进制表示都很繁琐,交流起来不方便,因此,多用16进制简写法来表示,因为生成多项式的最高位肯定为1,
    最高位的位置由位宽可知,故在简记式中,将最高的1统一去掉了,如CRC32的生成多项式简记为04C11DB7实际上表示的是104C11DB7。


CRC算法的编程实现:
    STM32硬件实现:
        1. 开启CRC单元的时钟。RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE)
        2. 复位CRC模块(设置CRC_CR=0x01),这个操作把CRC余数初始化为0xFFFFFFFF  CRC_ResetDR(void) 
        3. 把要计算的数据按逐个地写入CRC_DR寄存器  
            uint32_t CRC_CalcCRC(uint32_t Data); 将一个数据写入CRC_DR寄存器,返回值为计算结果。
            uint32_t CRC_CalcBlockCRC(uint32_t pBuffer[], uint32_t BufferLength);计算一个数组的CRC 值。
        4. 写完所有的数据字后,从CRC_DR寄存器读出计算的结果
        例程:
        #include "stm32f10x.h"
        #define BUFFER_SIZE    114
        static const uint32_t DataBuffer[BUFFER_SIZE] =
          {
            0x00001021, 0x20423063, 0x408450a5, 0x60c670e7, 0x9129a14a, 0xb16bc18c,
            0xd1ade1ce, 0xf1ef1231, 0x32732252, 0x52b54294, 0x72f762d6, 0x93398318,
            0xa35ad3bd, 0xc39cf3ff, 0xe3de2462, 0x34430420, 0x64e674c7, 0x44a45485,
            0xa56ab54b, 0x85289509, 0xf5cfc5ac, 0xd58d3653, 0x26721611, 0x063076d7,
            0x569546b4, 0xb75ba77a, 0x97198738, 0xf7dfe7fe, 0xc7bc48c4, 0x58e56886,
            0x78a70840, 0x18612802, 0xc9ccd9ed, 0xe98ef9af, 0x89489969, 0xa90ab92b,
            0x4ad47ab7, 0x6a961a71, 0x0a503a33, 0x2a12dbfd, 0xfbbfeb9e, 0x9b798b58,
            0xbb3bab1a, 0x6ca67c87, 0x5cc52c22, 0x3c030c60, 0x1c41edae, 0xfd8fcdec,
            0xad2abd0b, 0x8d689d49, 0x7e976eb6, 0x5ed54ef4, 0x2e321e51, 0x0e70ff9f,
            0xefbedfdd, 0xcffcbf1b, 0x9f598f78, 0x918881a9, 0xb1caa1eb, 0xd10cc12d,
            0xe16f1080, 0x00a130c2, 0x20e35004, 0x40257046, 0x83b99398, 0xa3fbb3da,
            0xc33dd31c, 0xe37ff35e, 0x129022f3, 0x32d24235, 0x52146277, 0x7256b5ea,
            0x95a88589, 0xf56ee54f, 0xd52cc50d, 0x34e224c3, 0x04817466, 0x64475424,
            0x4405a7db, 0xb7fa8799, 0xe75ff77e, 0xc71dd73c, 0x26d336f2, 0x069116b0,
            0x76764615, 0x5634d94c, 0xc96df90e, 0xe92f99c8, 0xb98aa9ab, 0x58444865,
            0x78066827, 0x18c008e1, 0x28a3cb7d, 0xdb5ceb3f, 0xfb1e8bf9, 0x9bd8abbb,
            0x4a755a54, 0x6a377a16, 0x0af11ad0, 0x2ab33a92, 0xed0fdd6c, 0xcd4dbdaa,
            0xad8b9de8, 0x8dc97c26, 0x5c644c45, 0x3ca22c83, 0x1ce00cc1, 0xef1fff3e,
            0xdf7caf9b, 0xbfba8fd9, 0x9ff86e17, 0x7e364e55, 0x2e933eb2, 0x0ed11ef0
          };
        __IO uint32_t CRCValue = 0;
        /**
          * @brief  Main program.
          * @param  None
          * @retval None
          */
        int main(void)
        {
          /*!< At this stage the microcontroller clock setting is already configured, 
               this is done through SystemInit() function which is called from startup
               file (startup_stm32f10x_xx.s) before to branch to application main.
               To reconfigure the default setting of SystemInit() function, refer to
               system_stm32f10x.c file
             */       
          /* Enable CRC clock */
          RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
          /* Compute the CRC of "DataBuffer" */
          CRCValue = CRC_CalcBlockCRC((uint32_t *)DataBuffer, BUFFER_SIZE);
          while (1)
          {
          }
        }


    软件编程(CRC32):
    #define poly 0xEDB88320
    int crc32(char *addr, int num, int crc)
    {
        int i;

        for (; num>0; num--)              /* Step through bytes in memory */
        {
          crc = crc ^ *addr++;            /* Fetch byte from memory, XOR into CRC */
          for (i=0; i<8; i++)             /* Prepare to rotate 8 bits */
          {
            if (crc & 1)                  /* b0 is set... */
              crc = (crc >> 1) ^ poly;    /* rotate and XOR with ZIP polynomic */
            else                          /* b0 is clear... */
              crc >>= 1;                  /* just rotate */
            }                             /* Loop for 8 bits */
        }                                 /* Loop until num=0 */
          return(crc);                    /* Return updated CRC */
    }

猜你喜欢

转载自blog.csdn.net/tyustli/article/details/84956683