STM32學習筆記--CRC校驗

工作原理:
    CRC即循環冗餘校驗碼(Cyclic Redundancy Check):是數據通信領域中最常用的一種查錯校驗碼,其特徵是信息字段和校驗字段的長度可以任意選定。
    循環冗餘檢查(CRC)是一種數據傳輸檢錯功能,對數據進行多項式計算,並將得到的結果附在幀的後面,接收設備也執行類似的算法,以保證數據傳輸的正確性和完整性。
    循環冗餘校驗碼(CRC)的基本原理是:在K位信息碼後再拼接R位的校驗碼,整個編碼長度爲N位,因此,這種編碼也叫(N,K)碼。
    對於一個給定的(N,K)碼,可以證明存在一個最高次冪爲N-K=R的多項式G(x)。根據G(x)可以生成K位信息的校驗碼,而G(x)叫做這個CRC碼的生成多項式。 
    校驗碼的具體生成過程爲:假設要發送的信息用多項式C(X)表示,將C(x)左移R位(可表示成C(x)*2^R),這樣C(x)的右邊就會空出R位,這就是校驗碼的位置。
    用 C(x)*2^R 除以生成多項式G(x)得到的餘數就是校驗碼。
    任意一個由二進制位串組成的代碼都可以和一個係數僅爲‘0’和‘1’取值的多項式一一對應。例如:代碼1010111對應的多項式爲x6+x4+x2+x+1,而多項式爲x5+x3+x2+x+1對應的代碼101111。
基本概念:
    對應關係:
        多項式和二進制數有直接對應關係:X的最高冪次對應二進制數的最高位,以下各位對應多項式的各冪次,有此冪次項對應1,無此冪次項對應0。
        可以看出:X的最高冪次爲R,轉換成對應的二進制數有R+1位。
        多項式包括生成多項式G(X)和信息多項式C(X)。
        如生成多項式爲G(X)=X4+X3+X+1, 可轉換爲二進制數碼11011
        而發送信息位 101111,可轉換爲數據多項式爲C(X)=X5+X3+X2+X+1。
    生成多項式:
        是接受方和發送方的一個約定,也就是一個二進制數,在整個傳輸過程中,這個數始終保持不變。
        在發送方,利用生成多項式對信息多項式做模2除生成校驗碼。在接收方利用生成多項式對收到的編碼多項式做模2除檢測和確定錯誤位置。
        應滿足以下條件:
            A、生成多項式的最高位和最低位必須爲1。
            B、當被傳送信息(CRC碼)任何一位發生錯誤時,被生成多項式做除後應該使餘數不爲0。
            C、不同位發生錯誤時,應該使餘數不同。
            D、對餘數繼續做除,應使餘數循環。
    校驗碼位數:
        CRC校驗碼位數 = 生成多項式位數 - 1。注意有些生成多項式的簡記式中將生成多項式的最高位1省略了。
    生成步驟:
        1、將X的最高次冪爲R的生成多項式G(X)轉換成對應的R+1位二進制數。
        2、將信息碼左移R位,相當於對應的信息多項式C(X)*2^R。
        3、用生成多項式(二進制數)對信息碼做除,得到R位的餘數(注意:這裏的二進制做除法得到的餘數其實是模2除法得到的餘數,並不等於其對應十進制數做除法得到的餘數。)。
        4、將餘數拼到信息碼左移後空出的位置,得到完整的CRC碼。
        例:假設使用的生成多項式是G(X)=X3+X+1。4位的原始報文爲1010,求編碼後的報文。
        1、將生成多項式G(X)=X3+X+1轉換成對應的二進制除數1011。
        2、此題生成多項式有4位(R+1)(注意:4位的生成多項式計算所得的校驗碼爲3位,R爲校驗碼位數),要把原始報文C(X)左移3(R)位變成1010 000
        3、用生成多項式對應的二進制數對左移3位後的原始報文進行模2除(高位對齊),相當於按位異或:
        1010000
        1011
        ————————————
        0001000
        1000
        1011
        ————————————
        0011
        得到的餘位011,所以最終編碼爲:1010 011
生成方法:
    藉助於模2除法則,其餘數爲校驗字段。
    例如:信息字段代碼爲: 1011001;對應m(x)=x6+x4+x3+1
    假設生成多項式爲:g(x)=x4+x3+1;則對應g(x)的代碼爲: 11001
    x^4*m(x)=x10+x8+x7+x4 對應的代碼記爲:10110010000(相當於1011001<<4);
    採用模2除法則: 得餘數爲: 1010(即校驗字段爲:1010)
    發送方:發出的傳輸字段爲: 10110011010 信息字段+校驗字段
    接收方:使用相同的生成碼進行校驗:接收到的字段/生成碼(二進制除法)如果能夠除盡,則正確,
    給出餘數(1010)的計算步驟:
    除法沒有數學上的含義,而是採用計算機的模二除法,即除數和被除數做異或運算。進行異或運算時除數和被除數最高位對齊,按位異或。
    10110010000
    11001
    ————————————
    01111010000
    1111010000
    11001
    ————————————
    0011110000
    11110000
    11001
    ————————————
    00111000
    111000
    11001
    ————————————
    001010
    1010
    則四位CRC校驗碼就爲:1010。
    利用CRC進行檢錯的過程可簡單描述爲:在發送端根據要傳送的k位二進制碼序列,以一定的規則產生一個校驗用的r位監督碼(CRC碼),附在原始信息後邊,構成一個新的二進制碼序列數共k+r位,
    後發送出去。在接收端,根據信息碼和CRC碼之間所遵循的規則進行檢驗,以確定傳送中是否出錯。這個規則,在差錯控制理論中稱爲“生成多項式”。
位寬:
    有一點要特別注意:生成多項式經常會說到多項式的位寬(Width,簡記爲W),這個位寬不是多項式對應的二進制數的位數,而是二進制位數減1。比如CRC8中用到的位寬爲8的生成多項式,
    其實對應得二進制數有九位:100110001。另外一點,多項式表示和二進制表示都很繁瑣,交流起來不方便,因此,多用16進制簡寫法來表示,因爲生成多項式的最高位肯定爲1,
    最高位的位置由位寬可知,故在簡記式中,將最高的1統一去掉了,如CRC32的生成多項式簡記爲04C11DB7實際上表示的是104C11DB7。


CRC算法的編程實現:
    STM32硬件實現:
        1. 開啓CRC單元的時鐘。RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE)
        2. 復位CRC模塊(設置CRC_CR=0x01),這個操作把CRC餘數初始化爲0xFFFFFFFF  CRC_ResetDR(void) 
        3. 把要計算的數據按逐個地寫入CRC_DR寄存器  
            uint32_t CRC_CalcCRC(uint32_t Data); 將一個數據寫入CRC_DR寄存器,返回值爲計算結果。
            uint32_t CRC_CalcBlockCRC(uint32_t pBuffer[], uint32_t BufferLength);計算一個數組的CRC 值。
        4. 寫完所有的數據字後,從CRC_DR寄存器讀出計算的結果
        例程:
        #include "stm32f10x.h"
        #define BUFFER_SIZE    114
        static const uint32_t DataBuffer[BUFFER_SIZE] =
          {
            0x00001021, 0x20423063, 0x408450a5, 0x60c670e7, 0x9129a14a, 0xb16bc18c,
            0xd1ade1ce, 0xf1ef1231, 0x32732252, 0x52b54294, 0x72f762d6, 0x93398318,
            0xa35ad3bd, 0xc39cf3ff, 0xe3de2462, 0x34430420, 0x64e674c7, 0x44a45485,
            0xa56ab54b, 0x85289509, 0xf5cfc5ac, 0xd58d3653, 0x26721611, 0x063076d7,
            0x569546b4, 0xb75ba77a, 0x97198738, 0xf7dfe7fe, 0xc7bc48c4, 0x58e56886,
            0x78a70840, 0x18612802, 0xc9ccd9ed, 0xe98ef9af, 0x89489969, 0xa90ab92b,
            0x4ad47ab7, 0x6a961a71, 0x0a503a33, 0x2a12dbfd, 0xfbbfeb9e, 0x9b798b58,
            0xbb3bab1a, 0x6ca67c87, 0x5cc52c22, 0x3c030c60, 0x1c41edae, 0xfd8fcdec,
            0xad2abd0b, 0x8d689d49, 0x7e976eb6, 0x5ed54ef4, 0x2e321e51, 0x0e70ff9f,
            0xefbedfdd, 0xcffcbf1b, 0x9f598f78, 0x918881a9, 0xb1caa1eb, 0xd10cc12d,
            0xe16f1080, 0x00a130c2, 0x20e35004, 0x40257046, 0x83b99398, 0xa3fbb3da,
            0xc33dd31c, 0xe37ff35e, 0x129022f3, 0x32d24235, 0x52146277, 0x7256b5ea,
            0x95a88589, 0xf56ee54f, 0xd52cc50d, 0x34e224c3, 0x04817466, 0x64475424,
            0x4405a7db, 0xb7fa8799, 0xe75ff77e, 0xc71dd73c, 0x26d336f2, 0x069116b0,
            0x76764615, 0x5634d94c, 0xc96df90e, 0xe92f99c8, 0xb98aa9ab, 0x58444865,
            0x78066827, 0x18c008e1, 0x28a3cb7d, 0xdb5ceb3f, 0xfb1e8bf9, 0x9bd8abbb,
            0x4a755a54, 0x6a377a16, 0x0af11ad0, 0x2ab33a92, 0xed0fdd6c, 0xcd4dbdaa,
            0xad8b9de8, 0x8dc97c26, 0x5c644c45, 0x3ca22c83, 0x1ce00cc1, 0xef1fff3e,
            0xdf7caf9b, 0xbfba8fd9, 0x9ff86e17, 0x7e364e55, 0x2e933eb2, 0x0ed11ef0
          };
        __IO uint32_t CRCValue = 0;
        /**
          * @brief  Main program.
          * @param  None
          * @retval None
          */
        int main(void)
        {
          /*!< At this stage the microcontroller clock setting is already configured, 
               this is done through SystemInit() function which is called from startup
               file (startup_stm32f10x_xx.s) before to branch to application main.
               To reconfigure the default setting of SystemInit() function, refer to
               system_stm32f10x.c file
             */       
          /* Enable CRC clock */
          RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
          /* Compute the CRC of "DataBuffer" */
          CRCValue = CRC_CalcBlockCRC((uint32_t *)DataBuffer, BUFFER_SIZE);
          while (1)
          {
          }
        }


    軟件編程(CRC32):
    #define poly 0xEDB88320
    int crc32(char *addr, int num, int crc)
    {
        int i;

        for (; num>0; num--)              /* Step through bytes in memory */
        {
          crc = crc ^ *addr++;            /* Fetch byte from memory, XOR into CRC */
          for (i=0; i<8; i++)             /* Prepare to rotate 8 bits */
          {
            if (crc & 1)                  /* b0 is set... */
              crc = (crc >> 1) ^ poly;    /* rotate and XOR with ZIP polynomic */
            else                          /* b0 is clear... */
              crc >>= 1;                  /* just rotate */
            }                             /* Loop for 8 bits */
        }                                 /* Loop until num=0 */
          return(crc);                    /* Return updated CRC */
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章