103系列只有一個CRC
前言:
????????CRC(Cyclic Redundancy Check),即循環冗余校驗,是一種根據網絡數據包或電腦文件等數據產生簡短固定位數校核碼的快速算法,主要用來檢測或校核數據傳輸或者保存后可能出現的錯誤。CRC校驗的工作原理主要基于以下步驟:
- 選定一個標準除數(一個K位二進制數據串)。
- 在要發送的數據(m位)后面加上K-1位0,然后將這個新數(M+K-1位)以模2除法的方式除以上面這個標準除數,所得到的余數(余數必須比除數少且只少一位,不夠就補0)也就是該數據的CRC校驗碼。
- 將這個校驗碼附在原m位數據后面,構成新的M+K-1位數據,發送給接收端。
- 接收端將接收到的數據除以標準除數,如果余數為0則認為數據正確。
????????CRC校驗被廣泛應用于各種通信協議中,如以太網、USB、串口通信協議等,以及存儲介質中,如硬盤、光盤等。在數據傳輸和存儲過程中,由于各種原因(如磁場干擾、光盤劃傷等),數據可能會發生錯誤,CRC校驗可以有效地檢測并糾正這些錯誤。
????????此外,CRC校驗還常用于文件傳輸、數據庫和防篡改檢測中。在文件傳輸過程中,發送方會計算文件的CRC校驗碼,并將其與文件一同發送給接收方。接收方在接收到文件后,重新計算CRC校驗碼并與接收到的校驗碼進行比較,以判斷文件是否傳輸正確。在數據庫中,每當數據進行插入、更新或刪除操作時,會先計算CRC校驗碼,并將其與數據一同存儲在數據庫中。在讀取數據時,再次計算CRC校驗碼并與存儲的校驗碼進行比較,以檢測數據是否被篡改。在數據加密中,為了防止被黑客篡改,常常會使用CRC校驗技術。
????????總之,CRC校驗是一種非常有效的數據錯誤檢測和糾正技術,在各種數據傳輸和存儲場景中都有廣泛的應用。
一:CRC
CRC種類:CRC8,CRC16,CRC32
CRC8:校驗結果為1個字節。
CRC16:校驗結果為5個字節。
CRC32:校驗結果為4個字節。
在我們整個103系列中只有CRC32的硬件校驗。
初始值和多項式:2種通信在確定CRC校驗種類后,然后就是確定多項式和初值的一致。
對于我們整個103系列的來說這兩個都是固定的值。
多項式:
初值:
二:HAL的配置
?
三:代碼
A:硬件CRC單次校驗計算
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "CRC.h"uint32_t verify[4]={0x01020304,0x05060708,0x090A0B0C,0x0D0E0F00};int main(void)
{HAL_Init(); /* 初始化HAL庫 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 72Mhz */delay_init(72); /* 延時初始化 */LED_Init(); /* LED初始化 */Uart_Init(115200);CRC_Init();uint32_t inspection_data=HAL_CRC_Calculate(&CRC_Handle,verify,4);//因為采用的是硬件CRC32的校驗,返回的為4個字節。所以必須采用uint32_t //uint32_t 為4個字節。如果使用其他的那么返回值錯誤,不是4個字節。printf("%x\r\n",inspection_data);while (1){}
}#include "stm32f1xx_hal.h"CRC_HandleTypeDef CRC_Handle;void CRC_Init(void)
{CRC_Handle.Instance=CRC;HAL_CRC_Init(&CRC_Handle);
}void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{__HAL_RCC_CRC_CLK_ENABLE();}
HAL_CRC_Calculate()函數使用注意:
第二個參數:是按字節傳輸的的(一次4個字節),所以數據從傳輸檢驗必須為4字節的整數倍。
uint32_t pBuffer[]
:這是一個指向要計算CRC的數據的指針。這個數組包含了要進行CRC計算的數據。由于STM32的CRC算法通常是基于32位數據的,所以這里的數組元素類型通常為uint32_t
我們采用uint32_t
?類型的變量占用?4 個字節
第三個參數:
uint32_t BufferLength
:這個參數指定了pBuffer
數組中的元素數量,也就是要計算CRC的數據長度。這個值通常以32位為單位,表示有多少個32位數據需要進行CRC計算。
B:硬件CRC連續計算
????????
????????當我們的數據量太大的時候,一次性的計算對于我們來說有點困難,或者內存不夠。我們采用連續CRC校驗的方式(一次校驗一個字)。
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "CRC.h"uint32_t verify[5]={0x01020304,0x05060708,0x090A0B0C,0x0D0E0F00,0x01020304};int main(void)
{HAL_Init(); /* 初始化HAL庫 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 72Mhz */delay_init(72); /* 延時初始化 */LED_Init(); /* LED初始化 */Uart_Init(115200);CRC_Init();uint32_t inspection_data=HAL_CRC_Calculate(&CRC_Handle,verify,5);//因為采用的是硬件CRC32的校驗,返回的為4個字節。所以必須采用uint32_t //uint32_t 為4個字節。如果使用其他的那么返回值錯誤,不是4個字節。printf("單次校驗結果=%x\r\n",inspection_data);HAL_CRC_Calculate(&CRC_Handle,&verify[0],1);HAL_CRC_Accumulate(&CRC_Handle,&verify[1],1);HAL_CRC_Accumulate(&CRC_Handle,&verify[2],1);HAL_CRC_Accumulate(&CRC_Handle,&verify[3],1);uint32_t Continuous_check=HAL_CRC_Accumulate(&CRC_Handle,&verify[4],1);printf("連續校驗結果=%x\r\n",Continuous_check);while (1){}
}#include "stm32f1xx_hal.h"CRC_HandleTypeDef CRC_Handle;void CRC_Init(void)
{CRC_Handle.Instance=CRC;HAL_CRC_Init(&CRC_Handle);
}void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{__HAL_RCC_CRC_CLK_ENABLE();}
C:軟件CRC校驗計算
????????軟件的CRC校驗相對于我們硬件CRC的檢驗,來說非常靈活,初始值和多項式我們可以自己調節。
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "CRC.h"uint32_t verify[5]={0x01020304,0x05060708,0x090A0B0C,0x0D0E0F00};
uint8_t software_verify[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
uint32_t res;int main(void)
{HAL_Init(); /* 初始化HAL庫 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 72Mhz */delay_init(72); /* 延時初始化 */LED_Init(); /* LED初始化 */Uart_Init(115200);CRC_Init();uint32_t inspection_data=HAL_CRC_Calculate(&CRC_Handle,verify,4);//因為采用的是硬件CRC32的校驗,返回的為4個字節。所以必須采用uint32_t //uint32_t 為4個字節。如果使用其他的那么返回值錯誤,不是4個字節。printf("硬件單次校驗結果=%x\r\n",inspection_data);HAL_CRC_Calculate(&CRC_Handle,&verify[0],1);HAL_CRC_Accumulate(&CRC_Handle,&verify[1],1);HAL_CRC_Accumulate(&CRC_Handle,&verify[2],1);//HAL_CRC_Accumulate(&CRC_Handle,&verify[3],1);uint32_t Continuous_check=HAL_CRC_Accumulate(&CRC_Handle,&verify[3],1);printf("硬件連續校驗結果=%x\r\n",Continuous_check);uint32_t software_data= CRC32(software_verify,16,0XFFFFFFFF);printf("軟件單次校驗結果=%x\r\n",software_data);res=CRC32(&software_verify[0],4,0XFFFFFFFF);res=CRC32(&software_verify[4],4,res);res=CRC32(&software_verify[8],4,res);printf("軟件連續校驗結果=%x\r\n",CRC32(&software_verify[12],4,res));while (1){} }#include "stm32f1xx_hal.h"CRC_HandleTypeDef CRC_Handle;void CRC_Init(void)
{CRC_Handle.Instance=CRC;HAL_CRC_Init(&CRC_Handle);
}void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{__HAL_RCC_CRC_CLK_ENABLE();}/*** @brief 軟件CRC32校驗計算* * @param data:校驗的數據我們一次校驗一個字節* @param len: 檢驗數據的長度 * @param inti: 檢驗的初值* @retval 返回4個字節的校驗數據*/
uint32_t CRC32(uint8_t *data,uint16_t len,uint32_t init)
{//^=異或不同為1uint32_t poly=0x04C11DB7; //多項式 while(len--){ //data<<24:因為CRC32返回去的校驗值為4個字節;我們校驗的是一個字節;//所以把他左移3個字節到最高位。3*8=24也就是左移24位。init=init^(*data<<24);for(uint8_t i=0;i<8;i++){if((init&0x80000000)){init=(init<<1)^poly; }else{init=(init<<1);}}data++;}return init;
}
D:軟件CRC數據反轉
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "CRC.h"uint32_t verify[5]={0x01020304,0x05060708,0x090A0B0C,0x0D0E0F00};
uint8_t software_verify[20]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
uint32_t res;int main(void)
{HAL_Init(); /* 初始化HAL庫 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 72Mhz */delay_init(72); /* 延時初始化 */LED_Init(); /* LED初始化 */Uart_Init(115200);CRC_Init();uint32_t inspection_data=HAL_CRC_Calculate(&CRC_Handle,verify,4);//因為采用的是硬件CRC32的校驗,返回的為4個字節。所以必須采用uint32_t //uint32_t 為4個字節。如果使用其他的那么返回值錯誤,不是4個字節。printf("硬件單次校驗結果=%x\r\n",inspection_data);HAL_CRC_Calculate(&CRC_Handle,&verify[0],1);HAL_CRC_Accumulate(&CRC_Handle,&verify[1],1);HAL_CRC_Accumulate(&CRC_Handle,&verify[2],1);//HAL_CRC_Accumulate(&CRC_Handle,&verify[3],1);uint32_t Continuous_check=HAL_CRC_Accumulate(&CRC_Handle,&verify[3],1);printf("硬件連續校驗結果=%x\r\n",Continuous_check);uint32_t software_data= CRC32(software_verify,16,0XFFFFFFFF);printf("軟件單次校驗結果=%x\r\n",software_data);//軟件的輸入反轉+輸出反轉的時候不能使用連續的校驗;//因為在連續的過程種會不只一次的反轉輸出的結果(一直調用CRC32函數)//只有輸入反轉的時候可以使用// res=CRC32(&software_verify[0],4,0XFFFFFFFF);
// res=CRC32(&software_verify[4],4,res);
// res=CRC32(&software_verify[8],4,res);
// //res=CRC32(&software_verify[12],4,res);
// printf("軟件連續校驗結果=%x\r\n",CRC32(&software_verify[12],4,res));while (1){} }
#include "stm32f1xx_hal.h"uint8_t Inveruint8(uint8_t data);
uint32_t Inveruint32(uint32_t data);CRC_HandleTypeDef CRC_Handle;void CRC_Init(void)
{CRC_Handle.Instance=CRC;HAL_CRC_Init(&CRC_Handle);
}void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{__HAL_RCC_CRC_CLK_ENABLE();}/*** @brief 軟件CRC32校驗計算* * @param data:校驗的數據我們一次校驗一個字節* @param len: 檢驗數據的長度 * @param inti: 檢驗的初值* @retval 返回4個字節的校驗數據*/
uint32_t CRC32(uint8_t *data,uint16_t len,uint32_t init)
{//^=異或不同為1uint32_t poly=0x04C11DB7; //多項式 while(len--){ //data<<24:因為CRC32返回去的校驗值為4個字節;我們校驗的是一個字節;//所以把他左移3個字節到最高位。3*8=24也就是左移24位。init=init^(Inveruint8(*data)<<24); //輸入反轉for(uint8_t i=0;i<8;i++){if((init&0x80000000)){init=(init<<1)^poly; }else{init=(init<<1);}}data++;}return Inveruint32(init); //輸出反轉
}//數據反轉
uint8_t Inveruint8(uint8_t data)
{uint8_t i;uint8_t temp;temp=0;for(i=0;i<8;i++){if(data&(1<<i)){temp|=1<<(7-i);}}return temp;
}//數據反轉
uint32_t Inveruint32(uint32_t data)
{uint8_t i;uint32_t temp;temp=0;for(i=0;i<32;i++){if(data&(1<<i)){temp|=1<<(31-i);}}return temp;
}
E:軟件的CRC16和CRC8
CRC16
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "CRC.h"uint32_t verify[5]={0x01020304,0x05060708,0x090A0B0C,0x0D0E0F00};
uint8_t software_verify[20]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
uint32_t res;int main(void)
{HAL_Init(); /* 初始化HAL庫 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 72Mhz */delay_init(72); /* 延時初始化 */LED_Init(); /* LED初始化 */Uart_Init(115200);CRC_Init();uint32_t inspection_data=HAL_CRC_Calculate(&CRC_Handle,verify,4);//因為采用的是硬件CRC32的校驗,返回的為4個字節。所以必須采用uint32_t //uint32_t 為4個字節。如果使用其他的那么返回值錯誤,不是4個字節。printf("硬件單次校驗結果=%x\r\n",inspection_data);HAL_CRC_Calculate(&CRC_Handle,&verify[0],1);HAL_CRC_Accumulate(&CRC_Handle,&verify[1],1);HAL_CRC_Accumulate(&CRC_Handle,&verify[2],1);//HAL_CRC_Accumulate(&CRC_Handle,&verify[3],1);uint32_t Continuous_check=HAL_CRC_Accumulate(&CRC_Handle,&verify[3],1);printf("硬件連續校驗結果=%x\r\n",Continuous_check);uint16_t software_data= CRC16(software_verify,16,0XFFFF);printf("軟件單次校驗結果=%x\r\n",software_data);//軟件的輸入反轉+輸出反轉的時候不能使用連續的校驗;//因為在連續的過程種會不只一次的反轉輸出的結果(一直調用CRC32函數)//只有輸入反轉的時候可以使用// res=CRC32(&software_verify[0],4,0XFFFFFFFF);
// res=CRC32(&software_verify[4],4,res);
// res=CRC32(&software_verify[8],4,res);
// //res=CRC32(&software_verify[12],4,res);
// printf("軟件連續校驗結果=%x\r\n",CRC32(&software_verify[12],4,res));while (1){} }#include "stm32f1xx_hal.h"uint8_t Inveruint8(uint8_t data);
uint32_t Inveruint32(uint32_t data);
uint16_t Inveruint16(uint16_t data);
CRC_HandleTypeDef CRC_Handle;void CRC_Init(void)
{CRC_Handle.Instance=CRC;HAL_CRC_Init(&CRC_Handle);
}void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{__HAL_RCC_CRC_CLK_ENABLE();}/*** @brief 軟件CRC32校驗計算* * @param data:校驗的數據我們一次校驗一個字節* @param len: 檢驗數據的長度 * @param inti: 檢驗的初值* @retval 返回4個字節的校驗數據*/
uint32_t CRC32(uint8_t *data,uint16_t len,uint32_t init)
{//^=異或不同為1uint32_t poly=0x04C11DB7; //多項式 while(len--){ //data<<24:因為CRC32返回去的校驗值為4個字節;我們校驗的是一個字節;//所以把他左移3個字節到最高位。3*8=24也就是左移24位。init=init^(Inveruint8(*data)<<24); //輸入反轉for(uint8_t i=0;i<8;i++){if((init&0x80000000)){init=(init<<1)^poly; }else{init=(init<<1);}}data++;}return Inveruint32(init); //輸出反轉
}/*** @brief 軟件CRC16校驗計算* * @param data:校驗的數據我們一次校驗一個字節* @param len: 檢驗數據的長度 * @param inti: 檢驗的初值* @retval 返回4個字節的校驗數據*/
uint16_t CRC16(uint8_t *data,uint16_t len,uint16_t init)
{//^=異或不同為1uint32_t poly=0x8005; //多項式 while(len--){ init=init^(Inveruint8(*data)<<8); //輸入反轉for(uint8_t i=0;i<8;i++){if((init&0x8000)){init=(init<<1)^poly; }else{init=(init<<1);}}data++;}return Inveruint16(init); //輸出反轉
}//數據反轉---輸入
uint8_t Inveruint8(uint8_t data)
{uint8_t i;uint8_t temp;temp=0;for(i=0;i<8;i++){if(data&(1<<i)){temp|=1<<(7-i);}}return temp;
}//數據反轉----輸出
uint32_t Inveruint32(uint32_t data)
{uint8_t i;uint32_t temp;temp=0;for(i=0;i<32;i++){if(data&(1<<i)){temp|=1<<(31-i);}}return temp;
}//數據反轉
uint16_t Inveruint16(uint16_t data)
{uint8_t i;uint16_t temp;temp=0;for(i=0;i<16;i++){if(data&(1<<i)){temp|=1<<(15-i);}}return temp;
}
CRC8
/*** @brief 軟件CRC8校驗計算* * @param data:校驗的數據我們一次校驗一個字節* @param len: 檢驗數據的長度 * @param inti: 檢驗的初值* @retval 返回4個字節的校驗數據*/
uint8_t CRC8(uint8_t *data,uint16_t len,uint8_t init)
{//^=異或不同為1uint32_t poly=0x07; //多項式 while(len--){ init=init^(*data<<8); for(uint8_t i=0;i<8;i++){if((init&0x80)){init=(init<<1)^poly; }else{init=(init<<1);}}data++;}uint8_t software_data= CRC8(software_verify,16,0XFF);printf("軟件單次校驗結果=%x\r\n",software_data);