CRC16協定

2020-08-10 18:15:27

CRC碼由發送端計算,放置於發送資訊報文的尾部。接收資訊的裝置再重新計算接收到資訊報文的CRC,比較計算得到的CRC是否與接收到的相符,如果兩者不相符,則表明出錯。
校驗碼的計算多項式爲(X16 + X15 + X2 + 1)。具體CRC16碼的計算方法是:
        1.預置1個16位元的暫存器爲十六進制FFFF(即全爲1);稱此暫存器爲CRC暫存器;
        2.把第一個8位元二進制數據 (既通訊資訊幀的第一個位元組)與16位元的CRC暫存器的低8位元相互斥或,把結果放於CRC暫存器;
        3.把CRC暫存器的內容右移一 位(朝低位)用0填補最高位,並檢查右移後的移出位;
        4.如果移出位爲0:重複第3步(再次右移一位);
        如果移出位爲1:CRC暫存器與多項式A001(1010 0000 0000 0001)進行互斥或;(Modbus)
        5.重複步驟3和4,直到右移8次,這樣整個8位元數據全部進行了處理;
        6.重複步驟2到步驟5,進行通訊資訊幀下一個位元組的處理;
        7.將該通訊資訊幀所有位元組按上述步驟計算完成後,得到的16位元CRC暫存器的高、低位元組進行交換;
        8.最後得到的CRC暫存器內容即爲:CRC碼。

CRC16常見的標準有以下幾種,被用在各個規範中,其演算法原理基本一致,就是在數據的輸入和輸出有所差異,下邊把這些標準的差異列出,並給出C語言的演算法實現。

CRC16_CCITT:多項式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在後,結果與0x0000互斥或

CRC16_CCITT_FALSE:多項式x16+x12+x5+1(0x1021),初始值0xFFFF,低位在後,高位在前,結果與0x0000互斥或

CRC16_XMODEM:多項式x16+x12+x5+1(0x1021),初始值0x0000,低位在後,高位在前,結果與0x0000互斥或

CRC16_X25:多項式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在後,結果與0xFFFF互斥或

 

CRC16_MODBUS:多項式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在後,結果與0x0000互斥或

CRC16_IBM:多項式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在後,結果與0x0000互斥或

CRC16_MAXIM:多項式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在後,結果與0xFFFF互斥或

CRC16_USB:多項式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在後,結果與0xFFFF互斥或

 

模式

多項式

初始值

數據位序

結果處理

CRC16_CCITT

x16+x12+x5+1(0x1021)

0x0000

低位在前,高位在後

與0x0000互斥或

CRC16_CCITT_FALSE

x16+x12+x5+1(0x1021)

0xFFFF

低位在後,高位在前

與0x0000互斥或

CRC16_XMODEM

x16+x12+x5+1(0x1021)

0x0000

低位在後,高位在前

與0x0000互斥或

CRC16_X25

x16+x12+x5+1(0x1021)

0x0000

低位在後,高位在前

與0xFFFF互斥或

CRC16_ MODBUS

x16+x15+x2+1(0x8005)

0xFFFF

低位在前,高位在後

與0x0000互斥或

CRC16_ IBM

x16+x15+x2+1(0x8005)

0x0000

低位在前,高位在後

與0x0000互斥或

CRC16_ MAXIM

x16+x15+x2+1(0x8005)

0x0000

低位在前,高位在後

與0xFFFF互斥或

CRC16_ USB

x16+x15+x2+1(0x8005)

0xFFFF

低位在前,高位在後

與0xFFFF互斥或

多項式產生:
如x16+x12+x5+1
x16表示第16位元爲1,x5表示第5位爲1
(1 << 16) | (1 << 12) | (1 << 5) | (1) = 0x11021
但是CRC16只取低16位元,寫成16進位制數就是 0x1021

CRC16的演算法原理:

1.根據CRC16的標準選擇初值CRCIn的值。

2.將數據的第一個位元組與CRCIn高8位元互斥或。

3.判斷最高位,若該位爲 0 左移一位,若爲 1 左移一位再與多項式Hex碼互斥或。

4.重複3直至8位元全部移位計算結束。

5.重複將所有輸入數據操作完成以上步驟,所得16位元數即16位元CRC校驗碼。

根據演算法原理與標準要求就能簡單的寫出具體程式:

 

[cpp] view plain copy

  1. unsigned short CRC16_CCITT(unsigned char *puchMsg, unsigned int usDataLen)  
  2. {  
  3.   unsigned short wCRCin = 0x0000;  
  4.   unsigned short wCPoly = 0x1021;  
  5.   unsigned char wChar = 0;  
  6.     
  7.   while (usDataLen--)     
  8.   {  
  9.         wChar = *(puchMsg++);  
  10.         InvertUint8(&wChar,&wChar);  
  11.         wCRCin ^= (wChar << 8);  
  12.         for(int i = 0;i < 8;i++)  
  13.         {  
  14.           if(wCRCin & 0x8000)  
  15.             wCRCin = (wCRCin << 1) ^ wCPoly;  
  16.           else  
  17.             wCRCin = wCRCin << 1;  
  18.         }  
  19.   }  
  20.   InvertUint16(&wCRCin,&wCRCin);  
  21.   return (wCRCin) ;  
  22. }  
  23. unsigned short CRC16_CCITT_FALSE(unsigned char *puchMsg, unsigned int usDataLen)  
  24. {  
  25.   unsigned short wCRCin = 0xFFFF;  
  26.   unsigned short wCPoly = 0x1021;  
  27.   unsigned char wChar = 0;  
  28.     
  29.   while (usDataLen--)     
  30.   {  
  31.         wChar = *(puchMsg++);  
  32.         wCRCin ^= (wChar << 8);  
  33.         for(int i = 0;i < 8;i++)  
  34.         {  
  35.           if(wCRCin & 0x8000)  
  36.             wCRCin = (wCRCin << 1) ^ wCPoly;  
  37.           else  
  38.             wCRCin = wCRCin << 1;  
  39.         }  
  40.   }  
  41.   return (wCRCin) ;  
  42. }  
  43. unsigned short CRC16_XMODEM(unsigned char *puchMsg, unsigned int usDataLen)  
  44. {  
  45.   unsigned short wCRCin = 0x0000;  
  46.   unsigned short wCPoly = 0x1021;  
  47.   unsigned char wChar = 0;  
  48.     
  49.   while (usDataLen--)     
  50.   {  
  51.         wChar = *(puchMsg++);  
  52.         wCRCin ^= (wChar << 8);  
  53.         for(int i = 0;i < 8;i++)  
  54.         {  
  55.           if(wCRCin & 0x8000)  
  56.             wCRCin = (wCRCin << 1) ^ wCPoly;  
  57.           else  
  58.             wCRCin = wCRCin << 1;  
  59.         }  
  60.   }  
  61.   return (wCRCin) ;  
  62. }  
  63.   
  64. unsigned short CRC16_X25(unsigned char *puchMsg, unsigned int usDataLen)  
  65. {  
  66.   unsigned short wCRCin = 0xFFFF;  
  67.   unsigned short wCPoly = 0x1021;  
  68.   unsigned char wChar = 0;  
  69.     
  70.   while (usDataLen--)     
  71.   {  
  72.         wChar = *(puchMsg++);  
  73.         InvertUint8(&wChar,&wChar);  
  74.         wCRCin ^= (wChar << 8);  
  75.         for(int i = 0;i < 8;i++)  
  76.         {  
  77.           if(wCRCin & 0x8000)  
  78.             wCRCin = (wCRCin << 1) ^ wCPoly;  
  79.           else  
  80.             wCRCin = wCRCin << 1;  
  81.         }  
  82.   }  
  83.   InvertUint16(&wCRCin,&wCRCin);  
  84.   return (wCRCin^0xFFFF) ;  
  85. }  
  86.   
  87. unsigned short CRC16_MODBUS(unsigned char *puchMsg, unsigned int usDataLen)  
  88. {  
  89.   unsigned short wCRCin = 0xFFFF;  
  90.   unsigned short wCPoly = 0x8005;  
  91.   unsigned char wChar = 0;  
  92.     
  93.   while (usDataLen--)     
  94.   {  
  95.         wChar = *(puchMsg++);  
  96.         InvertUint8(&wChar,&wChar);  
  97.         wCRCin ^= (wChar << 8);  
  98.         for(int i = 0;i < 8;i++)  
  99.         {  
  100.           if(wCRCin & 0x8000)  
  101.             wCRCin = (wCRCin << 1) ^ wCPoly;  
  102.           else  
  103.             wCRCin = wCRCin << 1;  
  104.         }  
  105.   }  
  106.   InvertUint16(&wCRCin,&wCRCin);  
  107.   return (wCRCin) ;  
  108. }  
  109. unsigned short CRC16_IBM(unsigned char *puchMsg, unsigned int usDataLen)  
  110. {  
  111.   unsigned short wCRCin = 0x0000;  
  112.   unsigned short wCPoly = 0x8005;  
  113.   unsigned char wChar = 0;  
  114.     
  115.   while (usDataLen--)     
  116.   {  
  117.         wChar = *(puchMsg++);  
  118.         InvertUint8(&wChar,&wChar);  
  119.         wCRCin ^= (wChar << 8);  
  120.         for(int i = 0;i < 8;i++)  
  121.         {  
  122.           if(wCRCin & 0x8000)  
  123.             wCRCin = (wCRCin << 1) ^ wCPoly;  
  124.           else  
  125.             wCRCin = wCRCin << 1;  
  126.         }  
  127.   }  
  128.   InvertUint16(&wCRCin,&wCRCin);  
  129.   return (wCRCin) ;  
  130. }  
  131. unsigned short CRC16_MAXIM(unsigned char *puchMsg, unsigned int usDataLen)  
  132. {  
  133.   unsigned short wCRCin = 0x0000;  
  134.   unsigned short wCPoly = 0x8005;  
  135.   unsigned char wChar = 0;  
  136.     
  137.   while (usDataLen--)     
  138.   {  
  139.         wChar = *(puchMsg++);  
  140.         InvertUint8(&wChar,&wChar);  
  141.         wCRCin ^= (wChar << 8);  
  142.         for(int i = 0;i < 8;i++)  
  143.         {  
  144.           if(wCRCin & 0x8000)  
  145.             wCRCin = (wCRCin << 1) ^ wCPoly;  
  146.           else  
  147.             wCRCin = wCRCin << 1;  
  148.         }  
  149.   }  
  150.   InvertUint16(&wCRCin,&wCRCin);  
  151.   return (wCRCin^0xFFFF) ;  
  152. }  
  153. unsigned short CRC16_USB(unsigned char *puchMsg, unsigned int usDataLen)  
  154. {  
  155.   unsigned short wCRCin = 0xFFFF;  
  156.   unsigned short wCPoly = 0x8005;  
  157.   unsigned char wChar = 0;  
  158.     
  159.   while (usDataLen--)     
  160.   {  
  161.         wChar = *(puchMsg++);  
  162.         InvertUint8(&wChar,&wChar);  
  163.         wCRCin ^= (wChar << 8);  
  164.         for(int i = 0;i < 8;i++)  
  165.         {  
  166.           if(wCRCin & 0x8000)  
  167.             wCRCin = (wCRCin << 1) ^ wCPoly;  
  168.           else  
  169.             wCRCin = wCRCin << 1;  
  170.         }  
  171.   }  
  172.   InvertUint16(&wCRCin,&wCRCin);  
  173.   return (wCRCin^0xFFFF) ;  
  174. }  

[cpp] view plain copy

  1. void InvertUint8(unsigned char *dBuf,unsigned char *srcBuf)  
  2. {  
  3.     int i;  
  4.     unsigned char tmp[4];  
  5.     tmp[0] = 0;  
  6.     for(i=0;i< 8;i++)  
  7.     {  
  8.       if(srcBuf[0]& (1 << i))  
  9.         tmp[0]|=1<<(7-i);  
  10.     }  
  11.     dBuf[0] = tmp[0];  
  12.       
  13. }  
  14. void InvertUint16(unsigned short *dBuf,unsigned short *srcBuf)  
  15. {  
  16.     int i;  
  17.     unsigned short tmp[4];  
  18.     tmp[0] = 0;  
  19.     for(i=0;i< 16;i++)  
  20.     {  
  21.       if(srcBuf[0]& (1 << i))  
  22.         tmp[0]|=1<<(15 - i);  
  23.     }  
  24.     dBuf[0] = tmp[0];  
  25. }  
  26. void InvertUint32(unsigned int *dBuf,unsigned int *srcBuf)  
  27. {  
  28.     int i;  
  29.     unsigned int tmp[4];  
  30.   
  31.     tmp[0] = 0;  
  32.       
  33.     for(i=0;i< 32;i++)  
  34.     {  
  35.       if(srcBuf[0]& (1 << i))  
  36.         tmp[0]|=1<<(15 - i);  
  37.     }  
  38.     dBuf[0] = tmp[0];  
  39. }  

具體驗證使用這個工具,內含CRC演算法的計算,和後邊的部落格中提到的其他演算法的工具合集

加密解密演算法工具集

在這個基礎上也加入CRC32 的校驗演算法

[html] view plain copy

 

  1. //CRC32演算法:  
  2. unsigned int CRC32(unsigned char *puchMsg, unsigned int usDataLen)  
  3. {  
  4.   int i;  
  5.   unsigned int wCRCin = 0xFFFFFFFF;  
  6.   unsigned int wCPoly = 0x04C11DB7;  
  7.   unsigned int wChar = 0;  
  8.   while (usDataLen--)     
  9.   {  
  10.         wChar = *(puchMsg++);  
  11.         InvertUint8((unsigned char *)&wChar,(unsigned char *)&wChar);  
  12.         wCRCin ^= (wChar << 24);  
  13.         for(i = 0;i < 8;i++)  
  14.         {  
  15.           if(wCRCin & 0x80000000)  
  16.             wCRCin = (wCRCin << 1) ^ wCPoly;  
  17.           else  
  18.             wCRCin = wCRCin << 1;  
  19.         }  
  20.   }  
  21.   InvertUint32(&wCRCin,&wCRCin);  
  22.   return (wCRCin ^ 0xFFFFFFFF) ;  
  23. }  

對於CRC32可能還有其他的多項式和初始值和結果值是否需要互斥或以及輸入數據是否需要位序倒轉等要求在原始碼中修改