CRC校驗原理及代碼實現(一)

網上的有關CRC的原理和代碼實現的文章,數不勝數,但比較全的好像還沒看到;此文是集百家之智慧,加之自己的使用經驗和理解匯聚此文,讓你快速理解和使用CRC,當然不足之處歡迎評論區吐槽。

1. CRC是什麼 ?

循環冗餘校驗(Cyclic Redundancy Check, CRC)是一種根據網絡數據包或計算機文件等數據產生簡短固定位數校驗碼的一種信道編碼技術,主要用來檢測或校驗數據傳輸或者保存後可能出現的錯誤。它是利用除法及餘數的原理來作錯誤偵測的。

---- 來自百度百科

哦,他是生成一段數據包校驗碼的一種算法,用於發送和接收方的數據校驗,減少數據幀出錯概率;

那他的實現算法是什麼樣的,有統一的官方標準嗎,還是有百花齊放? 有,有官方的算法標準

2. CRC標準算法

官方的標準算法鏈接還沒找到,這裏是匯聚網友的智慧,大家發的幾乎都是這套,公認的應該就沒錯了,有找到的歡迎評論區留言。

CRC標準算法模型的幾個參數:

  • 寬度(width):校驗碼所佔的比特位,比如我們常說的CRC16,寬度就是16;

  • 多項式(poly):參與計算的一個常量數值;

  • 初始值(init):開始計算時,CRC的預設數值;

  • 是否翻轉(ref):輸入或輸出數據的比特位順序是否翻轉,比如0x55翻轉爲0xAA;

  • XOROUT:常量數值,輸出結果與此數值異或得到最終結果,爲0時,無需異或;

可見如果發送數據和接收數據是兩個代碼組件,不同的工程師在寫,CRC的算法模型參數還要溝通使用一致哦

常見CRC標準算法模型參數表

名稱 多項式(poly) 初始值(init) 是否翻轉(ref) XOROUT
CRC16_CCITT X^16+X^12+X^5+1 (0x1021) 0x0000 true 0x0000
CRC16_CCITT_FALSE X^16+X^12+X^5+1 (0x1021) 0xFFFF false 0x0000
CRC16_MODBUS X^16+X^15+X^2+1 (0x8005) 0xFFFF true 0x0000
CRC16_XMODEM X^16+X^12+X^5+1 (0x1021) 0x0000 false 0x0000

這裏的多項式X^16+X^12+X^5+1怎麼轉成0x1021的呢,(1 << 16) | (1 << 12) | (1 << 5) | 1 = 0x1021

3. CRC16標準算法 C代碼實現

這裏說明一種通用統一的實現方法,所有的算法模型按上述參數都可以使用此函數實現;

​
static void invert_uint8(unsigned char *dstBuf, unsigned char *srcBuf)
{
    unsigned char i,tmp = 0;
    for ( i = 0; i < 8; i++){
        if (srcBuf[0] & (1 << i)){
            tmp |= 1 << (7-i);
        }
    }
    *dstBuf = tmp;
}
​
static void invert_uint16(unsigned short *dstBuf, unsigned short *srcBuf)
{
    unsigned short i,tmp = 0;
    for ( i = 0; i < 16; i++){
        if (srcBuf[0] & (1 << i)){
            tmp |= 1 << (15-i);
        }
    }
    *dstBuf = tmp;
}
​
/*****************************************************************
 * @Function: iot_calculate_crc16
 * @Description: 計算CRC16值的算法實現,適配所有CRC16的協議
 * @Param: ref_flag,數據位序翻轉,true-低位在前,高位在後
 * @Return: void
 *****************************************************************/
unsigned short iot_calculate_crc16(unsigned char *data,unsigned int size,const unsigned short crc_poly,unsigned short init_value,unsigned char ref_flag)
{    
    unsigned short crc_reg = init_value,tmp = 0;
    unsigned char j,byte = 0;
    
    while (size--){
        byte = *(data++);
        if (ref_flag)
            invert_uint8(&byte,&byte);
        crc_reg ^= byte << 8;
        for ( j = 0; j < 8; j++){
            tmp = crc_reg & 0x8000;
            crc_reg <<= 1;
            if (tmp)
                crc_reg ^= crc_poly;
        }
    }
    
    if (ref_flag)
        invert_uint16(&crc_reg,&crc_reg);
    
    // 這裏XOROUT數值固定爲0x0000
    return crc_reg;  // 等價於 return (crc_reg ^ 0X0000);
}
// 按CRC所用協議直接調用如下宏函數即可,這裏只列出部分常用的
\#define IOT_CRC16_CCITT_FALSE(data,size) iot_calculate_crc16(data,size,0x1021,0xFFFF,0)
\#define IOT_CRC16_CCITT(data,size) iot_calculate_crc16(data,size,0x1021,0x0000,1)
\#define IOT_CRC16_MODBUS(data,size) iot_calculate_crc16(data,size,0x8005,0xFFFF,1)
\#define IOT_CRC16_XMODEM(data,size) iot_calculate_crc16(data,size,0x1021,0x0000,0)
\#define IOT_CRC16_YMODEM(data,size) iot_calculate_crc16(data,size,0x1021,0x0000,0)

看了上述CRC16的,CRC32的實現應該也就很清晰了吧

好了,本文就寫到這裏,希望對你的項目有些幫助,後面(二)將分享CRC校驗碼的碰撞概率如何,分享之前大可以放心使用,比和校驗的碰撞概率肯定低得多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章