CRC校驗代碼整理

CRC 既可以從高位算起,也可以從低位算起,算法略有不同。
1、CRC8,來源:其他人的程序

static byte CRC8(byte *u8_data,byte u8_len)
{
    byte i, j;
    byte u8_crc8;
    byte u8_poly;

    u8_crc8 = 0xFF;
    u8_poly = 0x1D;

    for (i = 0; i < u8_len; i++)
    {
        u8_crc8 ^= u8_data[i];

        for (j = 0; j < 8; j++)
        {
            if (u8_crc8 & 0x80)
            {
                u8_crc8 = (u8_crc8 << 1) ^ u8_poly;
            }
            else
            {
                u8_crc8 <<= 1;
            }
        }
    }    
    u8_crc8 ^= (byte)0xFF;
    return u8_crc8;
}

2、CRC8,來源:MAX21004陀螺儀的參考手冊
這裏寫圖片描述
Bit order: Bit 63 is the first one sent, bit 0 the last one.
BYTE0爲CRC校驗

/*****************************************************************
Begin of CRC Lookup Table
*****************************************************************/
unsigned char crctable [256] = {
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53,
0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB,
0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E,
0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76,
0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4,
0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C,
0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19,
0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1,
0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8,
0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D,
0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65,
0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7,
0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F,
0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A,
0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2,
0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75,
0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50,
0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2,
0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A,
0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F,
0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7,
0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66,
0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E,
0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43,
0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1,
0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09,
0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C,
0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4};
/*****************************************************************
End of CRC Lookup Table
*****************************************************************/
//! \brief crc8_poly1D calculates crc8 checksum
//! \param A 7 bytes long array whose CRC has to be computed. The 0 indexed byte is the most significant byte
//! \return an unsigned char that represents the CRC value
//! \pre Actual parameter contains at least 7 elements
//! \post
//! This function generates the CRC checksum for the 7 input bytes using
//! Polinomial \f$**x^8+x^4+x^3+x^2+1**\f$.*/
unsigned char crc8_poly1D (const unsigned char * p_data)
{
    unsigned char crc = 0xFF, i;
    for (i = 7; i > 0; --i)
    {
        // Every byte is xored with table value ‘addressed’ on the basis of its own value
        crc = crctable [crc ^ p_data [i]];
    } 
    return (~ crc); // final NOT
}

3、CRC16,來源:某器件用戶手冊

CRC16校驗和API  : UINT16 GetCRC16(UINT8 *pSource, UINT16 len)
/********************************************************************
* Function Name : GetCRC16
* Description : 計算CRC16值.
* Input : *pSource: 數據頭指針 len:數據長度
* Output : None
* Return : CRC16碼
*********************************************************************/
UINT16 GetCRC16(UINT8 *pSource, UINT16 len)
{
    UINT16 i;
    UINT16 result =0;
    for (i=0; i <len; i++)
    {
        result = (result << 8) ^ CRC16Table[(result >> 8) ^ (UINT8)*pSource++];
    }
    return result;
}
// CRC16校驗
//X16 + X12 + X5 + 1(1021) 餘式表
const UINT16 CRC16Table[256] = {
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
};

4、CRC16, 來源:http://blog.csdn.net/liyuanbhu/article/details/7882789

CRC算法的編程實現
說了這麼多總算到了核心部分了。從前面的介紹我們知道CRC校驗覈心就是實現無借位的除法運算。下面還是通過一個例子來說明如何實現CRC校驗。
假設我們的生成多項式爲:100110001(簡記爲0x31),也就是CRC-8
則計算步驟如下:
(1) 將CRC寄存器(8-bits,比生成多項式少1bit)賦初值0
(2) 在待傳輸信息流後面加入8個0
(3) While (數據未處理完)
(4) Begin
(5) If (CRC寄存器首位是1)
(6) reg = reg XOR 0x31
(7) CRC寄存器左移一位,讀入一個新的數據於CRC寄存器的0 bit的位置。
(8) End
(9) CRC寄存器就是我們所要求的餘數。

實際上,真正的CRC 計算通常與上面描述的還有些出入。這是因爲這種最基本的CRC除法有個很明顯的缺陷,就是數據流的開頭添加一些0並不影響最後校驗字的結果。這個問題很讓人惱火啊,因此真正應用的CRC 算法基本都在原始的CRC算法的基礎上做了些小的改動。
所謂的改動,也就是增加了兩個概念,第一個是“餘數初始值”,第二個是“結果異或值”。
所謂的“餘數初始值”就是在計算CRC值的開始,給CRC寄存器一個初始值。“結果異或值”是在其餘計算完成後將CRC寄存器的值在與這個值進行一下異或操作作爲最後的校驗值。
常見的三種CRC 標準用到個各個參數如下表。
這裏寫圖片描述
加入這些變形後,常見的算法描述形式就成了這個樣子了:
(1) 設置CRC寄存器,並給其賦值爲“餘數初始值”。
(2) 將數據的第一個8-bit字符與CRC寄存器進行異或,並把結果存入CRC寄存器。
(3) CRC寄存器向右移一位,MSB補零,移出並檢查LSB。
(4) 如果LSB爲0,重複第三步;若LSB爲1,CRC寄存器與0x31相異或。
(5) 重複第3與第4步直到8次移位全部完成。此時一個8-bit數據處理完畢。
(6) 重複第2至第5步直到所有數據全部處理完成。
(7) 最終CRC寄存器的內容與“結果異或值”進行或非操作後即爲CRC值。

示例性的C代碼如下所示,因爲效率很低,項目中如對計算時間有要求應該避免採用這樣的代碼。不過這個代碼已經比網上常見的計算代碼要好了,因爲這個代碼有一個crc的參數,可以將上次計算的crc結果傳入函數中作爲這次計算的初始值,這對大數據塊的CRC計算是很有用的,不需要一次將所有數據讀入內存,而是讀一部分算一次,全讀完後就計算完了。這對內存受限系統還是很有用的。

#define POLY        0x1021
/**
 * Calculating CRC-16 in 'C'
 * @para addr, start of data
 * @para num, length of data
 * @para crc, incoming CRC
 */
uint16_t crc16(unsigned char *addr, int num, uint16_t crc)
{
    int i;
    for (; num > 0; num--)              /* Step through bytes in memory */
    {
        crc = crc ^ (*addr++ << 8);     /* Fetch byte from memory, XOR into CRC top byte*/
        for (i = 0; i < 8; i++)             /* Prepare to rotate 8 bits */
        {
            if (crc & 0x8000)            /* b15 is set... */
                crc = (crc << 1) ^ POLY;    /* rotate and XOR with polynomic */
            else                          /* b15 is clear... */
                crc <<= 1;                  /* just rotate */
        }                             /* Loop for 8 bits */
        crc &= 0xFFFF;                  /* Ensure CRC remains 16-bit value */
    }                               /* Loop until num=0 */
    return(crc);                    /* Return updated CRC */
}

上面的算法對數據流逐位進行計算,效率很低。實際上仔細分析CRC計算的數學性質後我們可以多位多位計算,最常用的是一種按字節查表的快速算法。該算法基於這樣一個事實:計算本字節後的CRC碼,等於上一字節餘式CRC碼的低8位左移8位,加上上一字節CRC右移 8位和本字節之和後所求得的CRC碼。如果我們把8位二進制序列數的CRC(共256個)全部計算出來,放在一個表裏,編碼時只要從表中查找對應的值進行處理即可。
按照這個方法,可以有如下的代碼(這個代碼也不是我寫的,是我在Micbael Barr的書“Programming Embedded Systems in C and C++” 中找到的,同樣,我做了點小小的改動。):
頭文件:

/*
crc.h
*/

#ifndef CRC_H_INCLUDED
#define CRC_H_INCLUDED

/*
* The CRC parameters. Currently configured for CCITT.
* Simply modify these to switch to another CRC Standard.
*/
/*
#define POLYNOMIAL          0x8005
#define INITIAL_REMAINDER   0x0000
#define FINAL_XOR_VALUE     0x0000
*/
#define POLYNOMIAL          0x1021
#define INITIAL_REMAINDER   0xFFFF
#define FINAL_XOR_VALUE     0x0000

/*
#define POLYNOMIAL          0x1021
#define POLYNOMIAL          0xA001
#define INITIAL_REMAINDER   0xFFFF
#define FINAL_XOR_VALUE     0x0000
*/

/*
* The width of the CRC calculation and result.
* Modify the typedef for an 8 or 32-bit CRC standard.
*/
typedef unsigned short width_t;
#define WIDTH (8 * sizeof(width_t))
#define TOPBIT (1 << (WIDTH - 1))

/**
 * Initialize the CRC lookup table.
 * This table is used by crcCompute() to make CRC computation faster.
 */
void crcInit(void);

/**
 * Compute the CRC checksum of a binary message block.
 * @para message, 用來計算的數據
 * @para nBytes, 數據的長度
 * @note This function expects that crcInit() has been called
 *       first to initialize the CRC lookup table.
 */
width_t crcCompute(unsigned char * message, unsigned int nBytes);

#endif // CRC_H_INCLUDED

c文件:

/*
 *crc.c
 */

#include "crc.h"
/*
* An array containing the pre-computed intermediate result for each
* possible byte of input. This is used to speed up the computation.
*/
static width_t crcTable[256];

/**
 * Initialize the CRC lookup table.
 * This table is used by crcCompute() to make CRC computation faster.
 */
void crcInit(void)
{
    width_t remainder;
    width_t dividend;
    int bit;
    /* Perform binary long division, a bit at a time. */
    for(dividend = 0; dividend < 256; dividend++)
    {
        /* Initialize the remainder.  */
        remainder = dividend << (WIDTH - 8);
        /* Shift and XOR with the polynomial.   */
        for(bit = 0; bit < 8; bit++)
        {
            /* Try to divide the current data bit.  */
            if(remainder & TOPBIT)
            {
                remainder = (remainder << 1) ^ POLYNOMIAL;
            }
            else
            {
                remainder = remainder << 1;
            }
        }
        /* Save the result in the table. */
        crcTable[dividend] = remainder;
    }
} /* crcInit() */

/**
 * Compute the CRC checksum of a binary message block.
 * @para message, 用來計算的數據
 * @para nBytes, 數據的長度
 * @note This function expects that crcInit() has been called
 *       first to initialize the CRC lookup table.
 */
width_t crcCompute(unsigned char * message, unsigned int nBytes)
{
    unsigned int offset;
    unsigned char byte;
    width_t remainder = INITIAL_REMAINDER;
    /* Divide the message by the polynomial, a byte at a time. */
    for( offset = 0; offset < nBytes; offset++)
    {
        byte = (remainder >> (WIDTH - 8)) ^ message[offset];
        remainder = crcTable[byte] ^ (remainder << 8);
    }
    /* The final remainder is the CRC result. */
    return (remainder ^ FINAL_XOR_VALUE);
} /* crcCompute() */

上面代碼中crcInit() 函數用來計算crcTable,因此在調用 crcCompute 前必須先調用 crcInit()。不過,對於嵌入式系統,RAM是很緊張的,最好將 crcTable 提前算好,作爲常量數據存到程序存儲區而不佔用RAM空間。CRC 計算實際上還有很多內容可以介紹,不過對於一般的程序員來說,知道這些也就差不多了。

5、CRC32,來源:網絡
三、CRC-32的程序實現。 爲了提高編碼效率,在實際運用中大多采用查表法來完成CRC-32校驗,下面是產生CRC-32校驗嗎的子程序。

unsigned long  crc_32_tab[256]={ 
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,0x0edb8832,…, 0x5a05df1b, 0x2d02ef8d };//事先計算出的參數表,共有256項,未全部列出。  
unsigned long GenerateCRC32(char xdata * DataBuf,unsigned long  len) 
{  
    unsigned long oldcrc32;  
    unsigned long crc32;
    unsigned long oldcrc;
    unsigned  int charcnt;
    charcnt=0;
    while (len--)
    {
        t= (oldcrc32 >> 24) & 0xFF;   //要移出的字節的值
        oldcrc=crc_32_tab[t];         //根據移出的字節的值查表
        c=DataBuf[charcnt];          //新移進來的字節值
        oldcrc32= (oldcrc32 << 8) | c;   //將新移進來的字節值添在寄存器末字節中
        oldcrc32=oldcrc32^oldcrc;     //將寄存器與查出的值進行xor運算
        charcnt++;
    } 
    crc32=oldcrc32;
    return crc32;
}
參數表可以先在PC機上算出來,也可在程序初始化時完成。下面是用於計算參數表的c語言子程序,在Visual C++ 6.0下編譯通過。
#include <stdio.h> 
unsigned long int crc32_table[256]; 
unsigned long int ulPolynomial = 0x04c11db7; 
unsigned long int Reflect(unsigned long int ref, char ch)
{
    unsigned long int value(0);  // 交換bit0和bit7,bit1和bit6,類推
    for(int i = 1; i < (ch + 1); i++)
    {
        if(ref & 1)  value |= 1 << (ch - i); 
        ref >>= 1;
    }
    return value;
}  

init_crc32_table()
{
    unsigned long int crc,temp;  // 256個值
    for(int i = 0; i <= 0xFF; i++)
    {
        temp=Reflect(i, 8);
        crc32_table[i]= temp<< 24;
        for (int j = 0; j < 8; j++)
        {
            unsigned long int t1,t2;
            unsigned long int flag=crc32_table[i]&0x80000000;
            t1=(crc32_table[i] << 1);
            if(flag==0)  t2=0;
            else  t2=ulPolynomial;
            crc32_table[i] =t1^t2;
        }
        crc=crc32_table[i];
        crc32_table[i] = Reflect(crc32_table[i], 32);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章