打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
关于STM32F4xx的硬件CRC32校验

一、概述

前段时间由于项目所需,要对MCU上某些数据进行CRC32校验,MCU选用的是STM32F4系列,以前看到过STM32有硬件CRC32校验功能,决定采用硬件CRC32校验,于是成功入坑。STM32硬件CRC32校验的结果跟预期的值并不一致,参考了CSDN大神的方法,根据项目加以改进,校验成功。STM32硬件CRC使用相关资料在网上是相对较少的,所以这里做个总结。

环境:MDK5或IAR8
参考:https://blog.csdn.net/lan120576664/article/details/47156067
申明:以下部分内容来自上述网站

二、STM32硬件CRC32与PC端计算结果的差异

STM32硬件CRC32计算结果不一致,也是使用时最大的坑,其原因为:

  1. STM32的硬件CRC32的每个字节的位序相反,STM32是按32位,高位在先,而主流实例每字节里面是从低位起的。

  2. 最终计算结果主流实例与0xFFFFFFFF进行了异或,而STM32并没有。

三、具体实现

首先对硬件CRC32初始化,主要是开启时钟,这里用的是标准库

void CRC32_Init(void){	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);	CRC->CR = CRC_CR_RESET;		//复位CRC寄存器}

根据前面叙述可知,要使其计算结果与主流计算结果一致,需要做以下几步:

  1. 改变字节序,可以理解为将32位的小端数据转为大端数据(或将32位大端数据转为小端数据);

  2. 将要计算的数据按位颠倒,如1110变为0111,不过这里是32位数据;

  3. 硬件CRC32计算结果与0xFFFFFFFF异或。

所以先实现以下函数

/** *	@brief	将一个32位数据按位颠倒函数,如0011->1100 *	@param	data:要颠倒的数据 *	@retval	颠倒后的数据 */u32 Revbit(u32 data) //{	u32 uRevData = 0,i = 0;	uRevData |= ((data >> i) & 0x01);	for(i = 1; i < 32; i  )	{		uRevData <<= 1;		uRevData |= ((data >> i) & 0x01);	}	return uRevData;}/** *	@brief	32位数据改变字节序 *	@param	data:源数据 *	@retval	改变后的数据 */u32 Revswap (u32 data) // 自己写的32字节序转换函数{	u8 *p = (u8 *)&data;	u8 *q = p   3;		while(p < q)		{		*p = *p^*q;		*q = *p^*q;		*p = *p^*q;		p  ;		q--;	}	return data;}

有了上面两个函数就可以得到与PC端保持一致的计算结果了,代码如下(这里只计算了一个数据)

u32 CRC32_Cal(u32 data){	CRC->DR = Revbit(Revswap(data));	return (Revbit(CRC->DR)^0xFFFFFFFF);}

执行代码测试,确实能得到与PC结果一致的CRC32校验值

我开始想,计算一个数据就要几十次的循环,感觉跟软件计算CRC差不了多少,那用硬件CRC的意义何在,还有必要吗,更何况校验一个较大的buffer呢?好在本人有些ARM汇编基础,当回头再看参考博客的描述,Revbit函数与Revswap貌似用了汇编指令,激动中的我马上查了下ARM相关的汇编指令,果然如此查到了两条汇编指令,rbit和rev,接下来进行一下改进:

对于MDK环境:

__asm u32 Revbit (u32 data)	{	RBIT	R0, R0	BX		LR}__asm u32 Revswap(u32 data){	REV	R0, R0	BX		LR}

对于IAR环境

u32 Revbit (u32 data)	{	asm("RBIT	R0, R0 \n"		"BX		LR");}u32 Revswap (u32 data){	asm("REV	R0, R0 \n"		"BX		LR");}

对于Linux gcc环境

u32 Revbit (u32 data)	{	__ASM volatile("RBIT	R0, R0 \n"				   "BX		LR");}u32 Revswap (u32 data){	__ASM volatile("REV	R0, R0 \n"				   "BX		LR");}

最终两个复杂的函数被两条汇编指令干掉了,这里解释下这两个汇编指令:

  1. RBIT:把一个32位的数据按位倒置,效果如下:

    原数据 01000001 01000010 01000011 01000100,0x41424344
    执行后 00100010 11000010 00100100 10000010,0x42c22482

  2. REV:把一个32位数据的字节序改变(若芯片是小端则转为大端),效果如下

    原数据 0x12345678
    执行后 0x78563412

在MDK中编码时,输入RBIT时提示了一个__rbit()函数,我才意识到这个汇编指令在CM4相关头文件实现了,对应的在IAR中有__RBIT()函数,同样REV指令也一样,MDK中是__rev(),IAR中是__REV()函数,哈哈,谁让咱是小白呢。

接下来校验buffer就只需要传入buffer地址和长度就行

u32 CRC32_Cal_Buffer(u32 *buffer, u32 length){	u32 i = 0;	CRC->CR = CRC_CR_RESET;//复位CRC寄存器	for(i = 0; i < length - 1; i  )	{		CRC32_Cal(buffer[i]);	}	return CRC32_Cal(buffer[i]);	//返回最后一次计算的值}

到此,在我的项目中硬件CRC32校验成功进行。若有什么不正确的地方,欢迎各位大神指正。

四、注意

  1. 由于在我的项目中只用到32位CRC校验,且数据都是32位的,所以没有做不满32位数据的CRC32校验,以减少不必要的消耗,若要做8位数据(或说不满32位的数据)的CRC32校验请参考:https://blog.csdn.net/lan120576664/article/details/47156067

  2. 另外,根据自己项目需求,并没有支持大小端的转换,若是大端数据校验,另当别论。

来源:https://www.icode9.com/content-4-671851.html
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
STM8学习笔记
4种Bootloader程序安全机制设计
UC头条:STM32内置的硬件功能安全属性,你用过哪些
SD卡初始化
【青风带你学stm32f051系列教程】第9课 串口UART | 爱板网
STM8L051F3 硬件I2C从机实例
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服