一、CRC简介

CRC(Cyclic Redundancy Check),即循环冗余校验,是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的原理来作错误侦测的。

在数据传输过程中,无论传输系统的设计再怎么完美,差错总会存在,这种差错可能会导致在链路上传输的一个或者多个帧被破坏(出现比特差错,0变为1,或者1变为0),从而接受方接收到错误的数据。为尽量提高接受方收到数据的正确率,在接收方接收数据之前需要对数据进行差错检测,当且仅当检测的结果为正确时接收方才真正收下数据。检测的方式有多种,常见的有奇偶校验、因特网校验和循环冗余校验等。循环冗余校验是一种用于校验通信链路上数字传输准确性的计算方法(通过某种数学运算来建立数据位和校验位的约定关系的 )。发送方计算机使用某公式计算出被传送数据所含信息的一个值,并将此值 附在被传送数据后,接收方计算机则对同一数据进行 相同的计算,应该得到相同的结果。如果这两个 CRC结果不一致,则说明发送中出现了差错,接收方计算机可要求发送方计算机重新发送该数据。

1.1 基本原理

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

几个基本概念

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

    每一个生成多项式都可以与一个代码相对应,如CRC8对应代码:100110001。

1.2 CRC检验码的计算

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

V(x)=A(x)g(x)=xRm(x)+r(x)V ( x ) = A ( x ) g ( x ) = x R m ( x ) + r ( x ) 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。

1.3 错误检测

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

【注】几点说明:

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

1.4 STM32中的CRC

所有的STM32芯片都内置了一个硬件的CRC计算模块,可以很方便地应用到需要进行通信的程序中,这个CRC计算模块使用常见的、在以太网中使用的计算多项式:

写成16进制就是:0x04C11DB7

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

下面是用C语言描述的这个计算模块的算法,大家可以把它放在通信的另一端,对通信的正确性进行验证:

DWORD dwPolynomial = 0x04c11db7;
DWORD cal_crc(DWORD *ptr, int len)
{DWORD    xbit;DWORD    data;DWORD    CRC = 0xFFFFFFFF;    // initwhile (len--) {xbit = 1 << 31;data = *ptr++;for (int bits = 0; bits < 32; bits++) {if (CRC & 0x80000000) {CRC <<= 1;CRC ^= dwPolynomial;}elseCRC <<= 1;if (data & xbit)CRC ^= dwPolynomial;xbit >>= 1;}}return CRC;
}

有几点需要说明:

  1. 上述算法中变量CRC,在每次循环结束包含了计算的余数,它始终是向左移位(既从最低位向最高位移动),溢出的数据位被丢弃。
  2. 输入的数据始终是以32位为单位,如果原始数据少于32位,需要在低位补0,当然也可以高位补0。
  3. 假定输入的DWORD数组中每个分量是按小端存储。
  4. 输入数据是按照最高位最先计算,最低位最后计算的顺序进行。

例如:
如果输入0x44434241,内存中按字节存放的顺序是:0x41, 0x42, 0x43, 0x44。计算的结果是:0xCF534AE1
如果输入0x41424344,内存中按字节存放的顺序是:0x44, 0x43, 0x42, 0x41。计算的结果是:0xABCF9A63

二、新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”

2. 选择 MCU 和封装

3. 配置时钟
RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)

选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置

4. 配置调试模式
非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire

三、CRC

3.1 参数配置

Computing 中选择 CRC 设置,并勾选 Activated 激活

3.2 生成代码

输入项目名和项目路径

选择应用的 IDE 开发环境 MDK-ARM V5

每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。

点击 GENERATE CODE 生成代码

四、库函数

HAL_CRC_Calculate() 函数在每次计算时,对DR寄存器进行了复位,而HAL_CRC_Accumulate() 函数没有,因此在使用时就要根据需求来选择相应的函数了。

/*** @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_t HAL_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_t HAL_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;
}

五、修改main函数

添加测试数据

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
#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
};/* Expected CRC Value */
uint32_t uwExpectedCRCValue = 0x379E9F06;
/* USER CODE END PV */

在 main 函数中添加用程序计算数据的 CRC 值并校验是否和正确的 CRC 值相等,打印输出校验信息。

/*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */__IO uint32_t CRCValue = 0;/* 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_USART1_UART_Init();MX_CRC_Init();/* USER CODE BEGIN 2 */printf("\n\r ****** CRC Test Example *****\n\r");/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){CRCValue = HAL_CRC_Calculate(&hcrc, (uint32_t *)dataBuffer, BUFFER_SIZE);printf("\r\n32-bit CRC:0x%X\n", CRCValue);if(CRCValue != uwExpectedCRCValue){printf("\n\r CRC wrong value\n\r");}else{printf("\n\r CRC right value\n\r");}HAL_Delay(2000);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

六、查看打印

串口打印功能查看 STM32CubeMX学习笔记(6)——USART串口使用

通过调用 HAL_CRC_Calculate() 函数得到的,每次清除DR寄存器。

通过调用 HAL_CRC_Accumulate() 函数得到的,每次会使用的DR寄存器的结果来进行本次计算。

七、注意事项

用户代码要加在 USER CODE BEGIN NUSER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。


• 由 Leung 写于 2021 年 3 月 23 日

• 参考:STM32CubeMX系列教程16:RNG和CRC
    《嵌入式-STM32开发指南》第二部分 基础篇 - 第13章 CRC(HAL库)

STM32CubeMX学习笔记(22)——CRC接口使用相关推荐

  1. STM32CubeMX学习笔记(24)——通用定时器接口使用(电容按键检测)

    一.电容按键简介 电容器(简称为电容)就是可以容纳电荷的器件,两个金属块中间隔一层绝缘体就可以构成一个最简单的电容.如图 32-1(俯视图),有两个金属片,之间有一个绝缘介质,这样就构成了一个电容.这 ...

  2. STM32CubeMX学习笔记(38)——FSMC接口使用(TFT-LCD屏显示)

    一.TFT-LCD简介 TFT-LCD(Thin Film Transistor-Liquid Crystal Display) 即薄膜晶体管液晶显示器.TFT-LCD 与无源 TN-LCD. STN ...

  3. STM32CubeMX学习笔记(9)——I2C接口使用(读写EEPROM AT24C02)

    一.I2C简介 I2C(Inter-Integrated Circuit ,内部集成电路) 总线是一种由飞利浦 Philip 公司开发的串行总线.是两条串行的总线,它由一根数据线(SDA)和一根 时钟 ...

  4. Java学习笔记22:并发(2)

    Java学习笔记22:并发(2) 图源:PHP中文网 终止任务 终止线程有一种非常简单的方式:设置一个多线程共享的标记位,子线程用轮询的方式检查这个标记位,如果该标记位显示取消状态,就让子线程退出执行 ...

  5. STM32CubeMX学习笔记(15)——电源管理(PWR)低功耗睡眠模式

    一.低功耗模式简介 系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗.由用户根据应用选择具体的低功耗模式,以在低功耗.短启动时间和可用唤醒源之间寻求最佳平衡. 睡眠模 ...

  6. STM32CubeMX学习笔记(25)——FatFs文件系统使用(操作SPI Flash)

    一.FatFs简介 FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统.它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质.因此它可以很容易地不加修改地移植到其他的处理器 ...

  7. STM32CubeMX学习笔记(16)——电源管理(PWR)低功耗停止模式

    一.低功耗模式简介 系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗.由用户根据应用选择具体的低功耗模式,以在低功耗.短启动时间和可用唤醒源之间寻求最佳平衡. 睡眠模 ...

  8. STM32CubeMX学习笔记(27)——FatFs文件系统使用(操作SD卡)

    一.FatFs简介 FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统.它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质.因此它可以很容易地不加修改地移植到其他的处理器 ...

  9. STM32CubeMX学习笔记——STM32H743_硬件I2C

    STM32CubeMX学习笔记--STM32H743_硬件I2C Github STM32CubeMX配置 Pinout配置 GPIO Clock Configuration配置 代码部分 main. ...

最新文章

  1. i18n java_Java国际化(i18n) Locale类详细示例
  2. beyond compare 4 的30天试用期已过-解决方法
  3. MySQL 2006-MySQLserver has gone away
  4. 响应式布局及bootstrap(实例)
  5. ssh相互访问不用密码
  6. java中的.take(),Rxjava2~take~timer~interval~buffer~filter等源码如何实现(你应该懂的)~学渣带你扣rxjava2...
  7. PPT精典基础教程 .
  8. python图表制作方法_python图表制作
  9. Switch使用方法
  10. 汉文SEO大牛来分析灰色案例的百度优化
  11. 制造业英文简写中文对照之二(轉自ITPUB)
  12. ./node_modules/.bin/webpack指令出错,该如何解决???
  13. 双向可控硅详细用法说明
  14. 如何在保持营养均衡的同时不长肉
  15. mysqladmin - 管理 MySQL 服务器、获取运行状态
  16. python elasticsearch bulk_关于ElasticSearch Bulk的用法
  17. 分销小程序定制开发|分销系统开发对商家有哪些好处?
  18. 考研复试(控制工程专硕)及大学本科(物联网工程)知识点回顾(五)——其他重点内容
  19. 共享单车数据获取方法整理
  20. BCM ESW芯片和Marvell CAT芯片的比较

热门文章

  1. 东邪西毒新编 (转)
  2. FME会员期刊(夏季版)
  3. 如何写一份优秀的商业计划书
  4. 浙大版《C语言程序设计(第4版)》题目集 练习5-3 字符金字塔 (15 分)
  5. 获取Android设备的唯一识别码|设备号|序号|UUID
  6. ssm学生综合素质评价系统
  7. 打开桌面计算机投屏到扩展屏,电脑投屏到电视显示不完全解决办法
  8. 苏黎世联邦理工学院计算机博士去向,2019年5月31日学术报告(李文 研究员,瑞士苏黎世联邦理工学院)...
  9. python爬虫requests的库使用详解
  10. PySCENIC(二):pyscenic单细胞转录组转录因子分析