admin 管理员组

文章数量: 1184232

1.CRC的校验原理

循环冗余校验(CRC)计算单元是根据固定的生成多项式得到任一32位全字的CRC计算结果。在其他的应用中, CRC技术主要应用于核实数据传输或者数据存储的正确性和完整性。标准EN/IEC 60335-1即提供了一种核实闪存存储器完整性的方法。 CRC计算单元可以在程序运行时计算出软件的标识,之后与在连接时生成的参考标识比较,然后存放在指定的存储器空间。那么首先来看看CRC校验原理。

2.基本原理

CRC检验原理实际上就是在一个p位二进制数据序列之后附加一个r位二进制检验码(序列),从而构成一个总长为n=p+r位的二进制序列;附加在数据序列之后的这个检验码与数据序列的内容之间存在着某种特定的关系。如果因干扰等原因使数据序列中的某一位或某些位发生错误,这种特定关系就会被破坏。因此,通过检查这一关系,就可以实现对数据正确性的检验。

3.几个基本概念

1、帧检验序列FCS(Frame Check Sequence):为了进行差错检验而添加的冗余码。
2、多项式模2运行:实际上是按位异或(Exclusive OR)运算,即相同为0,相异为1,也就是不考虑进位、借位的二进制加减运算。如:10011011 + 11001010 = 01010001。
3、生成多项式(generator polynomial):当进行CRC检验时,发送方与接收方需要事先约定一个除数,即生成多项式,一般记作G(x)。生成多项式的最高位与最低位必须是1。常用的CRC码的生成多项式有:

13.1.1 CRC检验码的计算

设信息字段为K位,校验字段为R位,则码字长度为N(N=K+R)。设双方事先约定了一个R次多项式g(x),则CRC码:

V(x)=A(x)g(x)=xRm(x)+r(x)

其中: m(x)为K次信息多项式, r(x)为R-1次校验多项式。

这里r(x)对应的代码即为冗余码,加在原信息字段后即形成CRC码。

r(x)的计算方法为:在K位信息字段的后面添加R个0,再除以g(x)对应的代码序列,得到的余数即为r(x)对应的代码(应为R-1位;若不足,而在高位补0)。

计算示例:
设需要发送的信息为M = 1010001101,产生多项式对应的代码为P = 110101,R=5。在M后加5个0,然后对P做模2除法运算,得余数r(x)对应的代码:01110。故实际需要发送的数据是101000110101110。

13.1.2 错误检测

当接收方收到数据后,用收到的数据对P(事先约定的)进行模2除法,若余数为0,则认为数据传输无差错;若余数不为0,则认为数据传输出现了错误,由于不知道错误发生在什么地方,因而不能进行自动纠正,一般的做法是丢弃接收的数据。

【注】几点说明:
1、CRC是一种常用的检错码,并不能用于自动纠错。
2、只要经过严格的挑选,并使用位数足够多的除数 P,那么出现检测不到的差错的概率就很小很小。
3、仅用循环冗余检验 CRC 差错检测技术只能做到无差错接受(只是非常近似的认为是无差错的),并不能保证可靠传输。

13.2 STM32中的CRC

所有的STM32芯片都内置了一个硬件的CRC计算模块,可以很方便地应用到需要进行通信的程序中

● 使用CRC-32(以太网)多项式:0x4C11DB7
─ X32 + X26 + X23 + X22 + X16 + X12 + X11 + X10 + X8 + X7 + X4 + X2 + X +1
● 一个32位数据寄存器用于输入 / 输出
● CRC计算时间:4个AHB时钟周期(HCLK)
● 通用8位寄存器(可用于存放临时数据),这个CRC计算模块使用常见的、在以太网中使用的计算多项式:

4.CRC功能描述

CRC计算单元含有1个32位数据寄存器:
● 对该寄存器进行写操作时,作为输入寄存器,可以输入要进行CRC计算的新数据。
● 对该寄存器进行读操作时,返回上一次CRC计算的结果。
每一次写入数据寄存器,其计算结果是前一次CRC计算结果和新计算结果的组合(对整个32位字进行CRC计算,而不是逐字节地计算)

使用这个内置CRC模块的方法非常简单,既首先复位CRC模块(设置CRC_CR=0x01),这个操作把CRC计算的余数初始化为0xFFFFFFFF;然后把要计算的数据按每32位分割为一组数据字,并逐个地把这组数据字写入CRC_DR寄存器(既下图中的绿色框),写完所有的数据字后,就可以从CRC_DR寄存器读出计算的结果。

5.STM32Cube生成工程

我们在串口的例子的基础上进行配置。
串口通信(HAL库)

6.看官方说如何使用这个驱动程序

使用__HAL_RCC_CRC_CLK_ENABLE()启用CRC AHB时钟
(+)初始化CRC计算器
(+)指定生成多项式(外设默认值或非默认值)
(++)指定初始化值(外设默认值或非默认值)
(++)指定输入数据格式
(++)指定输入或输出数据反转模式
(+)使用HAL_CRC_Accumulate() 函数计算输入数据缓冲区的CRC值,以先前计算的CRC作为初始化Value
(+)使用HAL_CRC_Calculate() 函数计算输入数据缓冲区的CRC值,从定义的初始化值(默认或非默认)开始计算CRC

/* USER CODE BEGIN Header *//**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  *//* USER CODE END Header *//* Includes ------------------------------------------------------------------*/#include"main.h"#include"crc.h"#include"usart.h"#include"gpio.h"/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */#include"stdio.h"/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*//* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*//* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */#defineBUFFER_SIZE114/* 私有变量 ------------------------------------------------------------------*/staticconstuint32_t aDataBuffer[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 uwCRCValue =0;/* Expected CRC Value */uint32_t uwExpectedCRCValue =0x379E9F06;/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/voidSystemClock_Config(void);/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */#ifdef__GNUC__/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */#definePUTCHAR_PROTOTYPEint__io_putchar(int ch)#else#definePUTCHAR_PROTOTYPEintfputc(int ch, FILE *f)#endif/* __GNUC__ *//**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{/* Place your implementation of fputc here *//* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,0xFFFF);return ch;}intfgetc(FILE * f){uint8_t ch =0;HAL_UART_Receive(&huart1,(uint8_t*)&ch,1,0xffff);return ch;}/* USER CODE END 0 *//**
  * @brief  The application entry point.
  * @retval int
  */intmain(void){/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_CRC_Init();/* USER CODE BEGIN 2 *//* 进行冗余循环校验,获取校验码*/
  uwCRCValue =HAL_CRC_Accumulate(&hcrc,(uint32_t*)aDataBuffer, BUFFER_SIZE);/* 校验码与正确的校验结果对比 */if(uwCRCValue != uwExpectedCRCValue){/* Wrong CRC value */printf("CRC循环冗余校验结果出错!!!\n");}else{/* Right CRC value */printf("CRC循环冗余校验结果正确!!!\n");printf("32-bit CRC 校验码为:%d\n",uwCRCValue);}/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while(1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */}/**
  * @brief System Clock Configuration
  * @retval None
  */voidSystemClock_Config(void){
  RCC_OscInitTypeDef RCC_OscInitStruct ={0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct ={0};/** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if(HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2)!= HAL_OK){Error_Handler();}}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */voidError_Handler(void){/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state *//* USER CODE END Error_Handler_Debug */}#ifdefUSE_FULL_ASSERT/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */voidassert_failed(uint8_t*file,uint32_t line){/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */}#endif/* USE_FULL_ASSERT *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

值得注意的是ST官方提供的CRC校验有两个函数,函数原型如下:

/**
  * @brief  Compute the 32-bit CRC value of a 32-bit data buffer
  *         starting with the previously computed CRC as initialization value.
  * @param  hcrc CRC handle
  * @param  pBuffer pointer to the input data buffer.
  * @param  BufferLength input data buffer length (number of uint32_t words).
  * @retval uint32_t CRC (returned value LSBs for CRC shorter than 32 bits)
  */uint32_tHAL_CRC_Accumulate(CRC_HandleTypeDef *hcrc,uint32_t pBuffer[],uint32_t BufferLength){uint32_t index;/* CRC input data buffer index */uint32_t temp =0U;/* CRC output (read from hcrc->Instance->DR register) *//* Change CRC peripheral state */
  hcrc->State = HAL_CRC_STATE_BUSY;/* Enter Data to the CRC calculator */for(index =0U; index < BufferLength; index++){
    hcrc->Instance->DR = pBuffer[index];}
  temp = hcrc->Instance->DR;/* Change CRC peripheral state */
  hcrc->State = HAL_CRC_STATE_READY;/* Return the CRC computed value */return temp;}/**
  * @brief  Compute the 32-bit CRC value of a 32-bit data buffer
  *         starting with hcrc->Instance->INIT as initialization value.
  * @param  hcrc CRC handle
  * @param  pBuffer pointer to the input data buffer.
  * @param  BufferLength input data buffer length (number of uint32_t words).
  * @retval uint32_t CRC (returned value LSBs for CRC shorter than 32 bits)
  */uint32_tHAL_CRC_Calculate(CRC_HandleTypeDef *hcrc,uint32_t pBuffer[],uint32_t BufferLength){uint32_t index;/* CRC input data buffer index */uint32_t temp =0U;/* CRC output (read from hcrc->Instance->DR register) *//* Change CRC peripheral state */
  hcrc->State = HAL_CRC_STATE_BUSY;/* Reset CRC Calculation Unit (hcrc->Instance->INIT is
  *  written in hcrc->Instance->DR) */__HAL_CRC_DR_RESET(hcrc);/* Enter 32-bit input data to the CRC calculator */for(index =0U; index < BufferLength; index++){
    hcrc->Instance->DR = pBuffer[index];}
  temp = hcrc->Instance->DR;/* Change CRC peripheral state */
  hcrc->State = HAL_CRC_STATE_READY;/* Return the CRC computed value */return temp;}

咋一看好像很没啥区别,其实还是有区别的,HAL_CRC_Calculate()函数在每次计算时,对DR寄存器进行了复位,而HAL_CRC_Accumulate()函数没有,因此在使用时就要根据需求来选择相应的函数了。

7.实验现象

本文标签: 循环冗余 对应的代 系统