天天看點

C語言執行個體_CRC校驗算法

作者:DS小龍哥

一、CRC介紹

CRC(Cyclic Redundancy Check,循環備援校驗)是一種常用的錯誤檢測技術,用于驗證資料在傳輸或存儲過程中是否發生了錯誤。它通過對資料進行一系列計算和比較,生成一個校驗值,并将其附加到資料中。接收方可以使用相同的算法對接收到的資料進行校驗,然後與接收到的校驗值進行比較,進而确定資料是否存在錯誤。

CRC校驗通常用于以下方面:

(1)資料傳輸的可靠性:在資料通過媒體或網絡進行傳輸時,可能會發生噪聲、幹擾或其他傳輸錯誤。通過在資料中添加CRC校驗值,接收方可以檢測到傳輸過程中是否發生了錯誤,并采取相應措施,如請求重新發送資料。

(2)存儲媒體的完整性檢測:在存儲媒體上讀取或寫入資料時,可能會發生位翻轉、媒體故障等錯誤。通過在資料存儲時使用CRC校驗,可以在讀取資料時檢測到這些錯誤,并提供資料的完整性保證。

(3)網絡通信協定:許多網絡通信協定(如Ethernet、WiFi、USB等)使用CRC校驗作為資料幀的一部分,以確定傳輸的資料準确無誤。接收方在接收到資料幀後,使用CRC校驗來驗證資料的完整性。

在項目中,CRC校驗廣泛應用于各種通信系統、存儲系統和資料傳輸系統中。通過使用CRC校驗,可以提高資料的可靠性,并減少傳輸或存儲過程中的錯誤。它可以檢測到資料位級别的錯誤,并提供一定程度的資料完整性保證。CRC校驗在保障資料可靠性和完整性方面具有重要作用,特别是在對資料完整性有較高要求的應用場景中。

二、示例代碼

以下C語言代碼示範如何擷取一段資料的CRC校驗值:

#include <stdio.h>
#include <stdint.h>

// CRC校驗函數
uint16_t crc16(uint8_t *data, int length)
{
    uint16_t crc = 0xFFFF;
    
    for (int i = 0; i < length; i++)
    {
        crc ^= data[i];
        
        for (int j = 0; j < 8; j++)
        {
            if (crc & 1)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    
    return crc;
}

// 封裝的CRC校驗函數調用
uint16_t calculateCRC(uint8_t *data, int length)
{
    return crc16(data, length);
}

int main()
{
    uint8_t message[] = {0x01, 0x02, 0x03, 0x04, 0x05};
    int length = sizeof(message) / sizeof(message[0]);
    
    uint16_t crc = calculateCRC(message, length);
    printf("CRC: 0x%04X\n", crc);
    
    return 0;
}           

在上面代碼中,crc16 函數實作了CRC校驗的計算邏輯。采用了常用的CRC-16算法(0xA001多項式)。calculateCRC 函數是對 crc16 的封裝,用于調用CRC校驗函數并傳回校驗結果。

在 main 函數中,通過調用 calculateCRC 函數來計算給定資料的CRC校驗值,并将結果列印輸出。

代碼中的CRC校驗函數和封裝函數是基于無符号8位位元組和無符号16位整數的資料類型進行計算的。

三、案例:資料校驗

場景:在單片機通信裡,單片機需要向上位機發送一段資料。比如,存放在char buff[1024];這個數組裡。 需要封裝兩個函數,單片機端調用函數對這段資料進行CRC校驗,封裝校驗值,然後上位機收到資料之後驗證CRC,校驗資料是否傳輸正确。

3.1 發送方(封裝校驗值)

#include <stdio.h>
#include <stdint.h>

// CRC校驗函數
uint16_t crc16(uint8_t *data, int length)
{
    uint16_t crc = 0xFFFF;
    
    for (int i = 0; i < length; i++)
    {
        crc ^= data[i];
        
        for (int j = 0; j < 8; j++)
        {
            if (crc & 1)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    
    return crc;
}

// 封裝CRC校驗值到資料中
void appendCRC(uint8_t *data, int length)
{
    uint16_t crc = crc16(data, length);
    data[length] = crc & 0xFF; // 将低8位放入資料末尾
    data[length + 1] = crc >> 8; // 将高8位放入資料末尾的下一個位置
}

int main()
{
    uint8_t buff[1024] = {0x01, 0x02, 0x03, 0x04, 0x05}; // 原始資料
    int length = 5; // 資料長度
    
    // 在原始資料後追加CRC校驗值
    appendCRC(buff, length);
    
    // 輸出發送的資料(包括CRC校驗值)
    printf("發送的資料:");
    for (int i = 0; i < length + 2; i++)
    {
        printf("%02X ", buff[i]);
    }
    printf("\n");
    
    return 0;
}           

在發送方的代碼中,使用 appendCRC 函數将CRC校驗值追加到原始資料的末尾。

3.2 接收方(校驗資料)

#include <stdio.h>
#include <stdint.h>

// CRC校驗函數
uint16_t crc16(uint8_t *data, int length)
{
    uint16_t crc = 0xFFFF;
    
    for (int i = 0; i < length; i++)
    {
        crc ^= data[i];
        
        for (int j = 0; j < 8; j++)
        {
            if (crc & 1)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    
    return crc;
}

// 驗證CRC校驗值是否正确
int verifyCRC(uint8_t *data, int length)
{
    uint16_t crc = crc16(data, length - 2); // 去除資料末尾的CRC校驗值
    
    // 擷取接收到的CRC校驗值
    uint16_t receivedCRC = (data[length - 1] << 8) | data[length - 2];
    
    // 比較計算得到的CRC校驗值與接收到的CRC校驗值
    if (crc == receivedCRC)
    {
        return 1; // 校驗通過
    }
    else
    {
        return 0; // 校驗失敗
    }
}

int main()
{
    uint8_t receivedData[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0xC2, 0x45}; // 收到的資料(包括CRC校驗值)
    int length = sizeof(receivedData) / sizeof(receivedData[0]);
    
    // 驗證CRC校驗值是否正确
    int crcResult = verifyCRC(receivedData, length);
    
    if (crcResult)
    {
        printf("CRC校驗通過\n");
        // TODO: 進一步處理正确的資料
    }
    else
    {
        printf("CRC校驗失敗\n");
        // TODO: 處理校驗失敗的情況
    }
    
    return 0;
}           

在接收方的代碼中,使用 verifyCRC 函數驗證接收到的資料的CRC校驗值是否正确。如果校驗通過,可以執行進一步的資料處理操作;如果校驗失敗,可以進行異常處理。

示例中的CRC校驗函數是基于無符号8位位元組和無符号16位整數的資料類型進行計算的。可以根據實際需求進行适當修改,以适應不同的資料類型和CRC算法。