之前一直使用的软件CRC,但程序里面调用太频繁了,所以准备优化下。
三种方案实测了性能比较
1、查表法
特点已经简单好用,性能算是软件方案里面不错的
[C] 纯文本查看 复制代码 uint16_t CRC16_Modbus(const uint8_t *_pBuf, uint16_t _usLen)
{
uint8_t ucCRCHi = 0xFF; /* 高CRC字节初始化 */
uint8_t ucCRCLo = 0xFF; /* 低CRC 字节初始化 */
uint16_t usIndex; /* CRC循环中的索引 */
while (_usLen--)
{
usIndex = ucCRCHi ^ *_pBuf++; /* 计算CRC */
ucCRCHi = ucCRCLo ^ s_CRCHi[usIndex];
ucCRCLo = s_CRCLo[usIndex];
}
return ((uint16_t)ucCRCHi << 8 | ucCRCLo);
}
2、硬件CRC + HAL库API实现
配置:
[C] 纯文本查看 复制代码 /* 用于CRC16_Modbus替代 */
crcHandle.Instance = CRC;
crcHandle.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE;
crcHandle.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE;
crcHandle.Init.GeneratingPolynomial = 0x8005;
crcHandle.Init.CRCLength = CRC_POLYLENGTH_16B;
crcHandle.Init.InitValue = 0xffff;
crcHandle.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_BYTE;
crcHandle.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
crcHandle.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
if (HAL_CRC_Init(&crcHandle) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}
输出使用
[C] 纯文本查看 复制代码 uint16_t CRC16_Modbus(const uint8_t *_pBuf, uint16_t _usLen)
{
uint8_t ucCRCHi = 0xFF; /* 高CRC字节初始化 */
uint8_t ucCRCLo = 0xFF; /* 低CRC 字节初始化 */
uint16_t usIndex; /* CRC循环中的索引 */
usIndex = HAL_CRC_Calculate(&crcHandle, (uint32_t *)_pBuf, _usLen);
ucCRCLo = usIndex >> 8;
ucCRCHi = usIndex & 0xFF;
return ((uint16_t)ucCRCHi << 8 | ucCRCLo);
}
3、硬件CRC + REV指令
使用REV指令,将4个字节组合发给DR寄存器。
[C] 纯文本查看 复制代码 uint16_t CRC16_Modbus_HW(const uint8_t *_pBuf, uint16_t _usLen)
{
uint32_t i;
uint32_t n;
CRC->CR |= CRC_CR_RESET; /* 加载 INIT=0xFFFF */
if (((uint32_t)_pBuf & 3u) == 0u) /* 4 字节对齐:LDR+REV 快速路径 */
{
const uint32_t *p32 = (const uint32_t *)_pBuf;
n = _usLen / 4u;
for (i = 0u; i < n; i++)
{
CRC->DR = __REV(*p32++); /* 1×LDR + 1×REV */
}
i = 4u * n;
}
else /* 非对齐:移位 OR 组包(HAL 等价,任何对齐安全) */
{
for (i = 0u; i < (_usLen / 4u); i++)
{
CRC->DR = ((uint32_t)_pBuf[4u*i] << 24u) |
((uint32_t)_pBuf[4u*i + 1] << 16u) |
((uint32_t)_pBuf[4u*i + 2] << 8u) |
((uint32_t)_pBuf[4u*i + 3]);
}
i = 4u * i;
}
/* 公共尾部:1~3 字节,BYTES 格式写 DR 低 8 位 */
while (_usLen > i)
{
*(__IO uint8_t *)(__IO void *)&CRC->DR = _pBuf[i];
i++;
}
return (uint16_t)CRC->DR;
}
输出结果再用REV16切换高低字节
[C] 纯文本查看 复制代码 uint16_t CRC16_Modbus(const uint8_t *_pBuf, uint16_t _usLen)
{
return __REV16(CRC16_Modbus_HW((const uint8_t *)_pBuf, _usLen));
}
4、实际测试性能比较
软件CRC, 直接HAL
_usLen =16 CRC_ENABLE: cycles=315, 0. 79us
_usLen=131 CRC_ENABLE: cycles=1926, 4. 82us
硬件CRC,直接HAL
_usLen =16 CRC_ENABLE:cycles=196, 0.49us
_usLen=131 CRC_ENABLE:cycles=570, 1.42us
硬件CRC, 配合REV
_usLen =16 CRC_ENABLE:cycles=173, 0.43us
_usLen=131 CRC_ENABLE:cycles= 509, 1.27us
|