Koomee

一份C代码搞定所有CRC校验

0
阅读(1571)

一份C代码搞定所有CRC---by Koomee

写在前面:

 CRC校验有很多种,有CRC4/5/6/7/8/16/32,每一种的多项式也有很多种变化,

 * 并不是一成不变的;主要有这几个参数决定计算方法

 * 1>Poly:是多项式的值(Gx)

 * 2>Init: Init 的位数和Poly的位数相同,它的值为全0或者全F,

        当全为0时,在算法开始前对数据(这个数据是根据RefIn的值得到的)后面补上CRC位数个0,

        然后就可以进行后续计算了。

        当全为1时,表示在算法开始前对数据的前CRC位数(高位)先和对应位数个1进行异或

     (即:前CRC位数的值按位取反), 再在后面补上CRC位数个0,才进行后续计算。

 * 3>RefIn和Refout:它们要么全为False,要么全为True,

        当RefIn为False表示,输入的原始数据的每个字节的第7位作为最高有效位,

        第0为作为最低有效位,即正常计算即可

        当RefIn为True时,输入的原始数据的每个字节需要做个逆序的处理,

        注意:针对的每个字节,而不是整个数据,

       当Refout为False时,输出不做处理,

       当Refout为True,需要对输出数据做一次整个数据的逆序处理,

       注意:这里做的逆序和RefIn不同,它不是按字节逆序,而是整个逆序,

       以CRC-32为例来说明,最后的数据为32位,当Refout为True时,翻转如下

 *                   bit31-bit30-bit29........bit3-bit2-bit1-bit0

 *                -> bit0-bit1-bit2-bit3...........bit29-bit30-bit31

 * 4->XorOut:表示根据上面参数计算完后,和这个数再进行一次异或。

 以上叙述摘自本网站wuyage的博文。

看代码前先看看本站大牛写的这篇文章:《你真的明白CRC的计算过程吗》

地址:http://blog.chinaaet.com/wuyage/p/5100049902

弄明白CRC的计算输入输出条件后,来看看代码怎么实现。

下面看代码:

Filename:CRC_Calculate.c

#include <stdio.h>
#include <stdlib.h>
/*************************************************************************************************
写在前面: 代码前先看看本站大牛写的这篇文章 http://blog.chinaaet.com/wuyage/p/5100049902 
CRC校验有很多种,有CRC4/5/6/7/8/16/32,每一种的多项式也有很多种变化, 
* 并不是一成不变的;主要有这几个参数决定计算方法 
* 1>Poly:是多项式的值 * 
* 2>Init: Init 的位数和Poly的位数相同,它的值为全0或者全F, 
*           当全为0时,在算法开始前对数据(这个数据是根据RefIn的值得到的)后面补上CRC位数个0后就可以进行后续计算了。 
*           当全为1时,表示在算法开始前对数据的前CRC位数(高位)先和对应位数个1进行异或(即:前CRC位数的值按位取反), 
*           再在后面补上CRC位数个0,才进行后续计算。 
* 3>RefIn和Refout:它们要么全为False,要么全为True, 
*                 当RefIn为False表示,输入的原始数据的每个字节的第7位作为最高有效位,第0为作为最低有效位,即正常计算即可 
*                 当RefIn为True时,输入的原始数据的每个字节需要做个逆序的处理,注意:针对的每个字节,而不是整个数据, 
*                 当Refout为False时,输出不做处理,当 
*                 Refout为True,需要对输出数据做一次整个数据的逆序处理, 
*                注意:这里做的逆序和RefIn不同,它不是按字节逆序,而是整个逆序, 
*                以CRC-32为例来说明,最后的数据为32位,当Refout为True时,翻转如下 
*                   bit31-bit30-bit29........bit3-bit2-bit1-bit0 
*                -> bit0-bit1-bit2-bit3...........bit29-bit30-bit31 
* * 4->XorOut:表示根据上面参数计算完后,和这个数再进行一次异或。
 ***************************************************************************************************************************************
 ///Atention  at 64bit machine unsigned int is 64 bitwidth
 typedef unsigned char  u8;
 typedef unsigned short u16;
 typedef unsigned int u32;
 typedef unsigned long  u64;
 
 
 typedef struct uCRC_Parameter
 {	
      u8   BitWidth;       
      //4,5,6,7,8,16,32 or you define	
      u32  Poly;           //Polynomial  with  BitWidth	
      u32  Init_Value;     //BitWidth bit "0"or "1"	
      u8   Refin_Bool;     //same Time Refin=Refout  True Or False	
      u8   Refout_Bool;	
      u32  XorOut;         //Before last verify data to putout,shuld XOR with XorOut	
      u32  ValidBit;       //BitWidth bit 1
  }CRC_PARAM;
  
 static unsigned int CRC_table[256];
 
//位逆转
static unsigned int mf_Bitrev(u32 input, u8 bitwidth)
{    
    u8 i;    
    u32 var;    
    var = 0;    
    for(i=0; i<bitwidth; i++)    
    {        
         if(input & 0x01)        
         {            
             var |= 1<<(bitwidth - 1 - i);        
         }        
         input >>= 1;    
     }    
     return var;
}

void CRC_CreateTable(CRC_PARAM uCRC_PARAM)
{	
    u16 i;	
    u8 j;	
    u32 tmp_poly;	
    u32 tmp_CrcReg;	
    u32 tmp_MaxBit;	
    tmp_poly=uCRC_PARAM.Poly;	
    tmp_MaxBit=(1<<(uCRC_PARAM.BitWidth-1));	
    if(1==uCRC_PARAM.Refin_Bool)	
    {		
         tmp_poly = mf_Bitrev(tmp_poly, uCRC_PARAM.BitWidth);		
         for(i=0; i<256; i++)        
         {           
              tmp_CrcReg = i;           
              for (j=0; j<8; j++)           
              {              
                   if((tmp_CrcReg & 1)!=0)              
                   {				
                        tmp_CrcReg=tmp_poly ^ (tmp_CrcReg >> 1);
                   }			
                   else			
                   {				
                          tmp_CrcReg=(tmp_CrcReg >> 1);			
                   }           
               }          
               CRC_table[i] = (uCRC_PARAM.ValidBit&tmp_CrcReg);        
           }	
      }	
      else	
      {		
           if(uCRC_PARAM.BitWidth<8)        
           {            
                tmp_poly=uCRC_PARAM.Poly<<(8-uCRC_PARAM.BitWidth);
                tmp_MaxBit=(1<<7);
            }		
            for(i=0; i<256; i++)        
            {           
                tmp_CrcReg = (i<<uCRC_PARAM.BitWidth-8);           
                if(uCRC_PARAM.BitWidth<8)           
                {                
                  tmp_CrcReg = (i<<0);           
                }           
                for (j=0; j<8; j++)
                {              
                  if((tmp_CrcReg & tmp_MaxBit)!=0)              
                  {				
                    tmp_CrcReg=(tmp_poly ^ (tmp_CrcReg<<1)) ;			 
                  }			
                  else			
                  {				
                    tmp_CrcReg=(tmp_CrcReg << 1);			
                  }           
                }          
                CRC_table[i] = (uCRC_PARAM.ValidBit&tmp_CrcReg);        
            }	
      }
}

//计算
CRCu32 CRC_Calculate(CRC_PARAM uCRC_PARAM, u8* uInput, u16 len)
{    
     int i;    
     u8 index;    
     u32 tmp_crc;    
     u8  tmp_crcHB;    
     tmp_crc=uCRC_PARAM.Init_Value;    
     if(1==uCRC_PARAM.Refin_Bool)    
     {       
         for(i=0; i<len; i++)       
         {         
              index = (uCRC_PARAM.ValidBit&(uInput[i]^ tmp_crc));
              tmp_crc = (uCRC_PARAM.ValidBit&((tmp_crc >> 8) ^ CRC_table[index]));
         }       
         //tmp_crc=mf_Bitrev(tmp_crc, uCRC_PARAM.BitWidth);    
      }    
      else    
      {       
          if(uCRC_PARAM.BitWidth>8)       
          {         
                for(i=0; i<len; i++)          
                {             
                       tmp_crcHB=(tmp_crc>>(uCRC_PARAM.BitWidth-8));             
                       tmp_crc=(tmp_crc<<8);             
                       tmp_crc = (uCRC_PARAM.ValidBit&(tmp_crc  ^ CRC_table[tmp_crcHB ^ uInput[i]]));
                 }       
          }       
          else       
          {         
                if(uCRC_PARAM.BitWidth<8)         
                {             
                     tmp_crc=(tmp_crc<<(8-uCRC_PARAM.BitWidth));         
                }         
                for(i=0; i<len; i++)          
                {             
                     tmp_crc = CRC_table[tmp_crc ^ uInput[i]];          
                }       
          }
              
        }    
        if((uCRC_PARAM.BitWidth<8)&&(uCRC_PARAM.Refin_Bool==0))    
        {        
              return (tmp_crc>>(8-uCRC_PARAM.BitWidth))^uCRC_PARAM.XorOut;    
        }    
        else    
        {       
             return tmp_crc^uCRC_PARAM.XorOut;    
        }
}


int main()
{    
      int i;    
      unsigned int res;    
      CRC_PARAM tmp_CRC_Param;    
       
      unsigned char TestData[]= {0xAA,0xAA,0x06,0x01,0x01,0x03,0xFB,0xEC,0x88,0x99};    
      
      tmp_CRC_Param.BitWidth=7;    
      tmp_CRC_Param.Poly=0x09;    
      tmp_CRC_Param.Init_Value=0x00;    
      tmp_CRC_Param.Refin_Bool  = 0;    
      tmp_CRC_Param.Refout_Bool = 0;    
      tmp_CRC_Param.XorOut=0x00;    
      tmp_CRC_Param.ValidBit=0xFF;    
      
      CRC_CreateTable(tmp_CRC_Param);        
      for(i=0;i<256;i++)    
      {		
           if(i%8==0)		
           {			
               printf("\n");		
           }		
           printf(" 0x%08X",CRC_table[i]);	
      }	
      printf("\n");    
      
      res=CRC_Calculate(tmp_CRC_Param,TestData,9);    
      printf("\n CRC Result=%08X! \n",res);    
      printf("Hello world!\n");    
      return 0;
}

写在后面

有网友提问

问题1、为什么不直接计算,而是要先计算一个表/

     答:如果直接计算,则每计算一个数据字节,都要重复算Table的256次循环,时间复杂度增加。建议在MCU等的应用中直接将Table值拷贝,不计算,节省功耗嘛!当然看应用自己权衡,时间复杂度和空间复杂度。

问题2、为什么在CRC_CreateTable(CRC_PARAM uCRC_PARAM)函数和 

                         CRC_Calculate(CRC_PARAM uCRC_PARAM, u8* uInput, u16 len)函数中是先判断在for循环?

            代码冗余了好多,。为何不是一个For循环里面判断。

   答:我这里写这份代码的时候主要是用在单片机里的c代码,考虑了代码的执行效率,所以写成了先判断,后for循环,这里先判断后循环,看似增加了代码的复杂度,其实细看会发现,真正计算的时候只有一路For循环被执行。同事也是只执行一次判断跳转。如果改为一个for循环里面判断几个分支,则每一次循环都要if判断跳转,对于单片机来说这样的跳转指令执行效率不高。当然如果你愿意,也可以改成你喜欢的样子。


写在 最后:

     如果有疑问或这建议请发E-mail到Koomee77@qq.com