需求
在项目中,需要用到MODBUS CRC16校验,也要用到CRC32的校验,出于效率的考虑,准备用硬件CRC。
CRC 16的参数模型有很多种,我这里用的是MODBUS,对于不同的参数模型,会有不同的参数设置和初值,这一点需要注意。
对于CRC32,参数模型只有2种,按需设置即可。
MODBUS CRC16
开启硬件CRC,再设置如下:
以下是针对MODBUS CRC16生成的代码
void MX_CRC_Init_modbus(void)
{
hcrc.Instance = CRC;
hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE;
hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE;
hcrc.Init.GeneratingPolynomial = 32773;
hcrc.Init.CRCLength = CRC_POLYLENGTH_16B;
hcrc.Init.InitValue = 0xffff;
hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_BYTE;
hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
if (HAL_CRC_Init(&hcrc) != HAL_OK)
{
Error_Handler();
}
}
CRC32
以下是针对CRC32生成的代码
void MX_CRC_Init_crc32(void)
{
hcrc.Instance = CRC;
hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE;
hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE;
hcrc.Init.GeneratingPolynomial = 79764919;
hcrc.Init.CRCLength = CRC_POLYLENGTH_32B;
hcrc.Init.InitValue = 0xffffffff;
hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_WORD;
hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_WORDS;
if (HAL_CRC_Init(&hcrc) != HAL_OK)
{
Error_Handler();
}
}
编程
先独立生成工程,各自安好,前面的2个设置都可以得出正确的结果。
接下来的事想的很简单,在需要切换不同的校验模式时,分别调用MX_CRC_Init_modbus()和MX_CRC_Init_crc32()这两个函数,重新初始化CRC外设,然后使用HAL_CRC_Calculate()来计算。
如果不行,再加上初始化之前再加上HAL_CRC_MspDeInit()函数,先做个禁止,再调用前面的初始化函数,应该就可以了吧。
现实是不行。重新初始化之后,CRC相关寄存器的值全部是0,无法使用。
然后尝试寄存器操作,强制重置CRC的相关寄存器,如下图
结果还是不行,工程原生校验CRC32的话,CRC32就可以正常计算,但MODBUS结果错误。反之也是类似结果。
注意我的注释,寄存器的设置是有先后顺序的,否则结果错乱。
//CRC 32
hcrc.Instance->POL = 0; //要先把多项式改为0,否则其他寄存器的值会乱
hcrc.Instance->CR = 0xe0; //输出反转,输入反转,32位
hcrc.Instance->INIT = 0xffffffff; //置初值
hcrc.Instance->DR = 0xffffffff; //置初值
hcrc.Instance->IDR = 0;
hcrc.Instance->POL = 0x04c11db7; //多项式的值
hcrc.Instance->CR |= 0x01; //复位,准备开始计算
//MODBUS CRC 16
hcrc.Instance->POL = 0; //要先把多项式改为0,否则其他寄存器的值会乱
hcrc.Instance->CR = 0xa8; //输出反转,输入反转,32位
hcrc.Instance->INIT = 0x0000ffff; //置初值
hcrc.Instance->DR = 0x0000ffff; //置初值
hcrc.Instance->IDR = 0;
hcrc.Instance->POL = 0x8005; //多项式的值
hcrc.Instance->CR |= 0x01; //复位,准备开始计算
CRC相关的就这么几个寄存器,我全设置一遍后,还是不能得出正确结果,这就尴尬了。
问题最终解决
在调试时,偶然发现HAL_CRC_Calculate()这个函数的调用时,第1个参数hcrc这个结构体,它还有自己的其他参数。
如果只更改CRC的5个寄存器的参数的话,hcrc结构体中的变量并未更改,这就导致在不同的设置做切换时,不能得出正确结果。
下图是CRC32时,hcrc结构体的数据
下图是MODBUS CRC16时,hcrc结构体的数据
我们修改参数,一般都只在Instance中修改,但这玩意还需要修改Init,和InputDataFormat才行。
代码如下:
由于CRC32用得不是很频繁,所以将模式切换代码放在LL_hw_crc32()函数中,在MODBUS模式时,就不用模式切换了,省点执行过程。
注意CRC32时,有个取反操作
u32 LL_hw_crc32(u32* pdata, u16 len) //长度是按照字为单位,即1表示4个字节
{
u32 crc32;
//CRC 32
hcrc.Instance->POL = 0; //要先把多项式改为0,否则其他寄存器的值会乱
hcrc.Instance->CR = 0xe0; //输出反转,输入反转,32位
hcrc.Instance->INIT = 0xffffffff; //置初值
hcrc.Instance->DR = 0xffffffff; //置初值
hcrc.Instance->IDR = 0;
hcrc.Instance->POL = 0x04c11db7; //多项式的值
hcrc.Instance->CR |= 0x01; //复位,准备开始计算
hcrc.Init.DefaultInitValueUse = 1;
hcrc.Init.DefaultPolynomialUse = 1;
hcrc.Init.GeneratingPolynomial = 0x04c11db7;
hcrc.Init.CRCLength = 0;
hcrc.Init.InitValue = 0xffffffff;
hcrc.Init.InputDataInversionMode = 0x60;
hcrc.Init.OutputDataInversionMode = 0x80;
hcrc.InputDataFormat = 3;
crc32 = ~HAL_CRC_Calculate(&hcrc, (u32*)pdata, len);
//MODBUS CRC 16
hcrc.Instance->POL = 0; //要先把多项式改为0,否则其他寄存器的值会乱
hcrc.Instance->CR = 0xa8; //输出反转,输入反转,32位
hcrc.Instance->INIT = 0x0000ffff; //置初值
hcrc.Instance->DR = 0x0000ffff; //置初值
hcrc.Instance->IDR = 0;
hcrc.Instance->POL = 0x8005; //多项式的值
hcrc.Instance->CR |= 0x01; //复位,准备开始计算
hcrc.Init.DefaultInitValueUse = 1;
hcrc.Init.DefaultPolynomialUse = 1;
hcrc.Init.GeneratingPolynomial = 0x8005;
hcrc.Init.CRCLength = 8;
hcrc.Init.InitValue = 0xffff;
hcrc.Init.InputDataInversionMode = 0x20;
hcrc.Init.OutputDataInversionMode = 0x80;
hcrc.InputDataFormat = 1;
return crc32;
}
u16 LL_hw_crc16(u8* pdata, u8 len)
{
u16 crc;
crc = HAL_CRC_Calculate(&hcrc, (u32*)pdata, len);
return crc;
}
通过这两个函数,即可实现硬件CRC计算,在2个不同的模式之间正确切换。
其他模式请自行摸索。