CRC校驗原理簡介及C代碼實現說明


1 原理

參考文檔:CRC校驗 (qq.com)

參考書籍:《計算機網絡(第7版)-謝希仁》

1.1 原理簡介

CRC是一種檢錯方法。

在發送端,先把數據划分為組,假定每組k個比特。現假定待傳送的數據M = 101001(k = 6)。CRC運算就是在數據M的后面添加供差錯檢測用的n位冗余碼,然后構成一個幀發送出去,一共發送(k + n)位。

數據:k比特

冗余碼:n比特

這n位冗余碼可用以下方法得出。在數據M后面添加n個0。得到的(k + n)位的數除以收發雙方事先商定的長度為(n + 1)位的除數p(多項式),得出商是Q而余數是R(n 位,比P 少一位,校驗碼)。

這個余數R就作為冗余碼拼接在數據M的后面發送出去。這種為了進行檢錯而添加的冗余碼常稱為幀檢驗序列FCS (Frame Check Sequence) 。

循環冗余檢驗CRC 和幀檢驗序列FCS 並不是同一個概念。CRC 是一種檢錯方法,而FCS 是添加在數據后面的冗余碼。

模二運算:也就是異或運算,相同異或為0,不同異或為1。

image-20210603123708607

在接收端把接收到的數據以幀為單位進行CRC檢驗:把收到的每一個幀都除以同樣的除數P(模2運算),然后檢查得到的余數R 。如果在傳輸過程中無差錯,那么經過CRC檢驗后得出的余數R肯定是0。

1.2 計算步驟

(1)先選擇CRC多項式,得到多項式的位寬。

例如:選擇G(x) = X4 + X3 + 1,對應的二進制編碼為:2b'11001,多項式位寬為4。

(2)選擇計算的數據。

(3)計算。

計算方式為:在要發送的數據幀(假設為m位)后面加上k-1位“0”(k:多項式位寬),然后使用模二運算得到余數,這個余數就是CRC校驗碼。

例如:多項式為G(x) = X4 + X3 + 1,計算的數據幀為:2b'10110011,計算過程如下:

得到的CRC校驗碼為:2b'0100。

2 代碼實現

參考鏈接:數據幀CRC32校驗算法實現 - 沒落騎士 - 博客園 (cnblogs.com)

在線生成工具:CRC Generation Tool - easics

還是以多項式為G(x) = X4 + X3 + 1,計算的數據幀為:2b'10110011來進行說明。

這里直接說實現,首先使用在線生成工具,得到一個VHDL或者Verilog的CRC校驗源碼。

image-20210603122129207

然后對下載的代碼進行一些改動即可。這里直接給出最后實現的C代碼。

#include <stdio.h>

#define int32_t  signed int
#define uint32_t unsigned int
#define uint8_t  unsigned char

#define GET_BIT_N_VAL(data, n)   (0x1 & (( *((data) + (n)/8) & (0x1 << ((n)%8)) ) >> ((n)%8)))

#define BITS_TO_BYTES_ARRAY(entry_key, entry_key_bits, bytes_array)   \
    do                                                                \
    {                                                                 \
        uint32_t i = 0;                                               \
        for(i = 0; i < (entry_key_bits); i++)                         \
        {                                                             \
            bytes_array[i] = GET_BIT_N_VAL((entry_key), i);           \
        }                                                             \
    } while (0)

#define HASH_KEY_WRITE_BITS 8

uint8_t crc_4_d8(const uint8_t *d) // only use the last one bit
{
    /*
    -- polynomial: x^4 + x^3 + 1
    -- data width: 8
    -- convention: the first serial bit is D[7]
    */
    int32_t i;
    uint8_t ret=0;
    uint8_t lfsr_c[4] = {0};
    uint8_t c[4] = {0};
    
    lfsr_c[0] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ d[0] ^ c[1] ^ c[3];
    lfsr_c[1] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[1] ^ c[0] ^ c[2];
    lfsr_c[2] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ d[2] ^ c[0] ^ c[1] ^ c[3];
    lfsr_c[3] = d[7] ^ d[6] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[0] ^ c[2] ^ c[3];
    
    for(i = 0; i < 4; i++) 
    {
        ret |= (lfsr_c[i]<<i);
    }
    return ret;
}


int main(void)
{
    uint8_t d = 0xb3;
    uint8_t ret = 0;
    uint8_t bytes_array[8] = {0};

    BITS_TO_BYTES_ARRAY(&d, HASH_KEY_WRITE_BITS, bytes_array);

    ret = crc_4_d8(bytes_array);

    printf("ret = 0x%x\n", ret);

    return 0;
}

編譯,運行,結果和之前手動計算的一致。

[grace@localhost] ~/c/crc
$ gcc test_crc.c 
[grace@localhost] ~/c/crc
$ ./a.out        
ret = 0x4


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM