CRC校驗
一:CRC概念
1.1、參考部落格
參考的教程如下:
1.2、什麼是CRC
CRC(Cyclic Redundancy Checksum)是一種糾錯技術,代表迴圈冗餘校驗和,可以認為在輸入端根據一定的規則計算出來CRC checksum,編組到message資訊中,傳送到接收端。接收端根據相同的規則解碼接收到的資訊,如果接收到的CRC checksum與預期值相同,則說明得到的資料正確,否則異常。
下面接收CRC checksum如何獲取:
這裡需要知道幾個組成部分或者說計算概念:多項式公式、多項式簡記式(poly)、資料寬度、初始值(init)、結果異或值、輸入值反轉、輸出值反轉、引數模型。
1.3、多項式公式與簡記式
對於CRC標準除數,一般使用多項式(或二項式)公式表示;
這裡以CRC8作為介紹校驗演算法過程,CRC16和CRC32同理。
CRC8標準生成多項式
CRC-8 x8+x5+x4+1 0x31(0x131)
CRC-8 x8+x2+x1+1 0x07(0x107)
CRC-8 x8+x6+x4+x3+x2+x1 0x5E(0x15E)
注:由於多項式的最高為都為1,並且在程式碼的crc8計算中,最高位也是不使用的,
所以在多項式記錄時都去掉了最高位。
實際計算也可以採用完整的多項式公式,但是這種會比較麻煩。所以我們實際程式碼使用的都是簡記式;
1.4、計算過程
以CRC16為例計算:
1.根據CRC16的標準選擇初始值(init)。
2.如果有輸入反轉,則將資料進行反轉(如果沒有輸入反轉,則跳過。預設的都是每一個位元組按位反轉)。
2.將反轉後資料的第一個位元組與初始值高8位異或。
3.判斷最高位,若該位為 0 左移一位,若為 1 左移一位再與多項式異或。
4.重複3直至8位全部移位計算結束。
5.重複將所有輸入資料操作完成以上步驟,所得16位數即16位CRC校驗碼。
6.如果有輸出反轉,則將CRC校驗碼進行反轉(如果沒有輸出反轉,則跳過。預設的都是整個資料按位反轉)。
7.將反轉後的CRC校驗碼與結果異或值得到最終的CRC校驗碼。
輸入反轉舉例
以輸入 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08為例說明輸入反轉的概念
反轉得到的值為:0x80 0x40 0xc0 0x20 0xa0 0x60 0xe0 0x10
輸出反轉舉例
0x5ceeac03輸出反轉值為0xc035773a
二:手算CRC
手算的時候,最高項保留
寫程式的時候,最高項不保留(優化執行次數)
三:程式碼分析
3.1、計算一個位元組
//CRC-8
//計算單個位元組
#include <iostream> //支援uint型別
uint8_t cal_table_high_first(uint8_t value)
{
uint8_t crc;
crc = value;
/* 資料往左移了8位,需要計算8次 */
for (int i =0 ; i< 8 ; i++)
{
//1000 0000 = 0x80
//判斷最高位是否為1
if (crc & 0x80)
{
//如果為1,先左移一位然後再與多項式x31異或
//0011 0001 = 0x31 x^8+x^5+x^4+1
//左移一位是為了簡化最高項x^8的計算
crc = (crc << 1) ^ 0x31; }
else
{
//如果為0,則不需要異或,整體資料左移一位
crc = (crc << 1);
}
}
return crc;
}
3.2、計算多個位元組
//CRC-8
//計算多個位元組
#include <iostream> //支援uint型別
uint8_t crc_high_first(uint8_t *ptr, int len)
{
uint8_t crc=0x00; /* 計算的初始crc值 */
while(len--)
{
//先把上一位元組與下一位元組異或
crc ^= *ptr++;
//下面的程式碼與計算一個位元組的一致
for (int i=0; i<8 ; i++)
{
if (crc & 0x80)
crc = (crc << 1) ^ 0x31;
else
crc = (crc << 1);
}
}
return (crc);
}
3.3、產生表
//生成表
//呼叫了上方的函式
#include <iostream> //支援uint型別
#include <stdio.h>
void create_crc_table(void)
{
int i = 0x00;
for (; i<=0xFF; i++)
{
if (0 == (i%16))
printf("\n");
//把每個位元組的CRC檢驗碼的結果儲存下來
printf("0x%.2x, ", cal_table_high_first (i));
}
}
3.4、查表方式生成CRC
//查表計算CRC
#include <iostream> //支援uint型別
uint8_t cal_crc_table(uint8_t *ptr, int len)
{
//初始化
uint8_t crc = 0x00;
while (len--)
{
crc = crc_table[crc ^ *ptr++];
//等價與 crc = crc_table[ crc ^ (*ptr) ];
// ptr++;
}
return crc;
}
3.5、反轉
uint32_t ReflectedData(uint32_t data, REFLECTED_MODE mode)
{
data = ((data & 0xffff0000) >> 16) | ((data & 0x0000ffff) << 16);
data = ((data & 0xff00ff00) >> 8) | ((data & 0x00ff00ff) << 8);
data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4);
data = ((data & 0xcccccccc) >> 2) | ((data & 0x33333333) << 2);
data = ((data & 0xaaaaaaaa) >> 1) | ((data & 0x55555555) << 1);
switch (mode)
{
case REF_32BIT:
return data;
case REF_16BIT:
return (data >> 16) & 0xffff;
case REF_8BIT:
return (data >> 24) & 0xff;
case REF_7BIT:
return (data >> 25) & 0x7f;
case REF_6BIT:
return (data >> 26) & 0x7f;
case REF_5BIT:
return (data >> 27) & 0x1f;
case REF_4BIT:
return (data >> 28) & 0x0f;
}
return 0;
}