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)
{
//^=异或不同为1
uint32_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)
{
//^=异或不同为1
uint32_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)
{
//^=异或不同为1
uint32_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)
{
//^=异或不同为1
uint32_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)
{
//^=异或不同为1
uint32_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);