博客專欄

EEPW首頁(yè) > 博客 > CRC16常見(jiàn)幾個(gè)標(biāo)準(zhǔn)的算法及C語(yǔ)言實(shí)現(xiàn)

CRC16常見(jiàn)幾個(gè)標(biāo)準(zhǔn)的算法及C語(yǔ)言實(shí)現(xiàn)

發(fā)布人:電子禪石 時(shí)間:2019-09-21 來(lái)源:工程師 發(fā)布文章
CRC碼由發(fā)送端計(jì)算,放置于發(fā)送信息報(bào)文的尾部。接收信息的設(shè)備再重新計(jì)算接收到信息報(bào)文的CRC,比較計(jì)算得到的CRC是否與接收到的相符,如果兩者不相符,則表明出錯(cuò)。
校驗(yàn)碼的計(jì)算多項(xiàng)式為(X16 + X15 + X2 + 1)。具體CRC16碼的計(jì)算方法是:
        1.預(yù)置1個(gè)16位的寄存器為十六進(jìn)制FFFF(即全為1);稱此寄存器為CRC寄存器;
        2.把第一個(gè)8位二進(jìn)制數(shù)據(jù) (既通訊信息幀的第一個(gè)字節(jié))與16位的CRC寄存器的低8位相異或,把結(jié)果放于CRC寄存器;
        3.把CRC寄存器的內(nèi)容右移一 位(朝低位)用0填補(bǔ)最高位,并檢查右移后的移出位;
        4.如果移出位為0:重復(fù)第3步(再次右移一位);
        如果移出位為1:CRC寄存器與多項(xiàng)式A001(1010 0000 0000 0001)進(jìn)行異或;(Modbus)
        5.重復(fù)步驟3和4,直到右移8次,這樣整個(gè)8位數(shù)據(jù)全部進(jìn)行了處理;
        6.重復(fù)步驟2到步驟5,進(jìn)行通訊信息幀下一個(gè)字節(jié)的處理;
        7.將該通訊信息幀所有字節(jié)按上述步驟計(jì)算完成后,得到的16位CRC寄存器的高、低字節(jié)進(jìn)行交換;
        8.最后得到的CRC寄存器內(nèi)容即為:CRC碼。

CRC16常見(jiàn)的標(biāo)準(zhǔn)有以下幾種,被用在各個(gè)規(guī)范中,其算法原理基本一致,就是在數(shù)據(jù)的輸入和輸出有所差異,下邊把這些標(biāo)準(zhǔn)的差異列出,并給出C語(yǔ)言的算法實(shí)現(xiàn)。

CRC16_CCITT多項(xiàng)式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,結(jié)果與0x0000異或

CRC16_CCITT_FALSE多項(xiàng)式x16+x12+x5+1(0x1021),初始值0xFFFF,低位在后,高位在前,結(jié)果與0x0000異或

CRC16_XMODEM多項(xiàng)式x16+x12+x5+1(0x1021),初始值0x0000,低位在后,高位在前,結(jié)果與0x0000異或

CRC16_X25多項(xiàng)式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,結(jié)果與0xFFFF異或


CRC16_MODBUS多項(xiàng)式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,結(jié)果與0x0000異或

CRC16_IBM多項(xiàng)式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,結(jié)果與0x0000異或

CRC16_MAXIM多項(xiàng)式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,結(jié)果與0xFFFF異或

CRC16_USB:多項(xiàng)式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,結(jié)果與0xFFFF異或

模式

多項(xiàng)式

初始值

數(shù)據(jù)位序

結(jié)果處理

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異或

多項(xiàng)式產(chǎn)生:
如x16+x12+x5+1
x16表示第16位為1,x5表示第5位為1
(1 << 16) | (1 << 12) | (1 << 5) | (1) = 0x11021
但是CRC16只取低16位,寫(xiě)成16進(jìn)制數(shù)就是 0x1021

CRC16的算法原理:

1.根據(jù)CRC16的標(biāo)準(zhǔn)選擇初值CRCIn的值。

2.將數(shù)據(jù)的第一個(gè)字節(jié)與CRCIn高8位異或。

3.判斷最高位,若該位為 0 左移一位,若為 1 左移一位再與多項(xiàng)式Hex碼異或。

4.重復(fù)3直至8位全部移位計(jì)算結(jié)束。

5.重復(fù)將所有輸入數(shù)據(jù)操作完成以上步驟,所得16位數(shù)即16位CRC校驗(yàn)碼。

根據(jù)算法原理與標(biāo)準(zhǔn)要求就能簡(jiǎn)單的寫(xiě)出具體程序:

unsigned short CRC16_CCITT(unsigned char *puchMsg, unsigned int usDataLen)  
{  
  unsigned short wCRCin = 0x0000;  
  unsigned short wCPoly = 0x1021;  
  unsigned char wChar = 0;  
    
  while (usDataLen--)     
  {  
        wChar = *(puchMsg++);  
        InvertUint8(&wChar,&wChar);  
        wCRCin ^= (wChar << 8);  
        for(int i = 0;i < 8;i++)  
        {  
          if(wCRCin & 0x8000)  
            wCRCin = (wCRCin << 1) ^ wCPoly;  
          else  
            wCRCin = wCRCin << 1;  
        }  
  }  
  InvertUint16(&wCRCin,&wCRCin);  
  return (wCRCin) ;  
}  
unsigned short CRC16_CCITT_FALSE(unsigned char *puchMsg, unsigned int usDataLen)  
{  
  unsigned short wCRCin = 0xFFFF;  
  unsigned short wCPoly = 0x1021;  
  unsigned char wChar = 0;  
    
  while (usDataLen--)     
  {  
        wChar = *(puchMsg++);  
        wCRCin ^= (wChar << 8);  
        for(int i = 0;i < 8;i++)  
        {  
          if(wCRCin & 0x8000)  
            wCRCin = (wCRCin << 1) ^ wCPoly;  
          else  
            wCRCin = wCRCin << 1;  
        }  
  }  
  return (wCRCin) ;  
}  
unsigned short CRC16_XMODEM(unsigned char *puchMsg, unsigned int usDataLen)  
{  
  unsigned short wCRCin = 0x0000;  
  unsigned short wCPoly = 0x1021;  
  unsigned char wChar = 0;  
    
  while (usDataLen--)     
  {  
        wChar = *(puchMsg++);  
        wCRCin ^= (wChar << 8);  
        for(int i = 0;i < 8;i++)  
        {  
          if(wCRCin & 0x8000)  
            wCRCin = (wCRCin << 1) ^ wCPoly;  
          else  
            wCRCin = wCRCin << 1;  
        }  
  }  
  return (wCRCin) ;  
}  
  
unsigned short CRC16_X25(unsigned char *puchMsg, unsigned int usDataLen)  
{  
  unsigned short wCRCin = 0xFFFF;  
  unsigned short wCPoly = 0x1021;  
  unsigned char wChar = 0;  
    
  while (usDataLen--)     
  {  
        wChar = *(puchMsg++);  
        InvertUint8(&wChar,&wChar);  
        wCRCin ^= (wChar << 8);  
        for(int i = 0;i < 8;i++)  
        {  
          if(wCRCin & 0x8000)  
            wCRCin = (wCRCin << 1) ^ wCPoly;  
          else  
            wCRCin = wCRCin << 1;  
        }  
  }  
  InvertUint16(&wCRCin,&wCRCin);  
  return (wCRCin^0xFFFF) ;  
}  
  
unsigned short CRC16_MODBUS(unsigned char *puchMsg, unsigned int usDataLen)  
{  
  unsigned short wCRCin = 0xFFFF;  
  unsigned short wCPoly = 0x8005;  
  unsigned char wChar = 0;  
    
  while (usDataLen--)     
  {  
        wChar = *(puchMsg++);  
        InvertUint8(&wChar,&wChar);  
        wCRCin ^= (wChar << 8);  
        for(int i = 0;i < 8;i++)  
        {  
          if(wCRCin & 0x8000)  
            wCRCin = (wCRCin << 1) ^ wCPoly;  
          else  
            wCRCin = wCRCin << 1;  
        }  
  }  
  InvertUint16(&wCRCin,&wCRCin);  
  return (wCRCin) ;  
}  
unsigned short CRC16_IBM(unsigned char *puchMsg, unsigned int usDataLen)  
{  
  unsigned short wCRCin = 0x0000;  
  unsigned short wCPoly = 0x8005;  
  unsigned char wChar = 0;  
    
  while (usDataLen--)     
  {  
        wChar = *(puchMsg++);  
        InvertUint8(&wChar,&wChar);  
        wCRCin ^= (wChar << 8);  
        for(int i = 0;i < 8;i++)  
        {  
          if(wCRCin & 0x8000)  
            wCRCin = (wCRCin << 1) ^ wCPoly;  
          else  
            wCRCin = wCRCin << 1;  
        }  
  }  
  InvertUint16(&wCRCin,&wCRCin);  
  return (wCRCin) ;  
}  
unsigned short CRC16_MAXIM(unsigned char *puchMsg, unsigned int usDataLen)  
{  
  unsigned short wCRCin = 0x0000;  
  unsigned short wCPoly = 0x8005;  
  unsigned char wChar = 0;  
    
  while (usDataLen--)     
  {  
        wChar = *(puchMsg++);  
        InvertUint8(&wChar,&wChar);  
        wCRCin ^= (wChar << 8);  
        for(int i = 0;i < 8;i++)  
        {  
          if(wCRCin & 0x8000)  
            wCRCin = (wCRCin << 1) ^ wCPoly;  
          else  
            wCRCin = wCRCin << 1;  
        }  
  }  
  InvertUint16(&wCRCin,&wCRCin);  
  return (wCRCin^0xFFFF) ;  
}  
unsigned short CRC16_USB(unsigned char *puchMsg, unsigned int usDataLen)  
{  
  unsigned short wCRCin = 0xFFFF;  
  unsigned short wCPoly = 0x8005;  
  unsigned char wChar = 0;  
    
  while (usDataLen--)     
  {  
        wChar = *(puchMsg++);  
        InvertUint8(&wChar,&wChar);  
        wCRCin ^= (wChar << 8);  
        for(int i = 0;i < 8;i++)  
        {  
          if(wCRCin & 0x8000)  
            wCRCin = (wCRCin << 1) ^ wCPoly;  
          else  
            wCRCin = wCRCin << 1;  
        }  
  }  
  InvertUint16(&wCRCin,&wCRCin);  
  return (wCRCin^0xFFFF) ;  
}

void InvertUint8(unsigned char *dBuf,unsigned char *srcBuf)  
{  
    int i;  
    unsigned char tmp[4];  
    tmp[0] = 0;  
    for(i=0;i< 8;i++)  
    {  
      if(srcBuf[0]& (1 << i))  
        tmp[0]|=1<<(7-i);  
    }  
    dBuf[0] = tmp[0];  
      
}  
void InvertUint16(unsigned short *dBuf,unsigned short *srcBuf)  
{  
    int i;  
    unsigned short tmp[4];  
    tmp[0] = 0;  
    for(i=0;i< 16;i++)  
    {  
      if(srcBuf[0]& (1 << i))  
        tmp[0]|=1<<(15 - i);  
    }  
    dBuf[0] = tmp[0];  
}  
void InvertUint32(unsigned int *dBuf,unsigned int *srcBuf)  
{  
    int i;  
    unsigned int tmp[4];  
  
    tmp[0] = 0;  
      
    for(i=0;i< 32;i++)  
    {  
      if(srcBuf[0]& (1 << i))  
        tmp[0]|=1<<(15 - i);  
    }  
    dBuf[0] = tmp[0];  
}

具體驗(yàn)證使用這個(gè)工具,內(nèi)含CRC算法的計(jì)算,和后邊的博客中提到的其他算法的工具合集

在這個(gè)基礎(chǔ)上也加入CRC32 的校驗(yàn)算法

/CRC32算法:  
unsigned int CRC32(unsigned char *puchMsg, unsigned int usDataLen)  
{  
  int i;  
  unsigned int wCRCin = 0xFFFFFFFF;  
  unsigned int wCPoly = 0x04C11DB7;  
  unsigned int wChar = 0;  
  while (usDataLen--)     
  {  
        wChar = *(puchMsg++);  
        InvertUint8((unsigned char *)&wChar,(unsigned char *)&wChar);  
        wCRCin ^= (wChar << 24);  
        for(i = 0;i < 8;i++)  
        {  
          if(wCRCin & 0x80000000)  
            wCRCin = (wCRCin << 1) ^ wCPoly;  
          else  
            wCRCin = wCRCin << 1;  
        }  
  }  
  InvertUint32(&wCRCin,&wCRCin);  
  return (wCRCin ^ 0xFFFFFFFF) ;  
}

對(duì)于CRC32可能還有其他的多項(xiàng)式和初始值和結(jié)果值是否需要異或以及輸入數(shù)據(jù)是否需要位序倒轉(zhuǎn)等要求在源碼中修改

*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。



關(guān)鍵詞:

相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉