硬汉嵌入式论坛

 找回密码
 立即注册
搜索
查看: 902|回复: 8
收起左侧

[STM32H7] 硬件CRC配合REV指令实现Modbus CRC16极致优化

[复制链接]

1万

主题

7万

回帖

12万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
124289
QQ
发表于 2026-8-25 10:39:03 | 显示全部楼层 |阅读模式


之前一直使用的软件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



回复

使用道具 举报

13

主题

529

回帖

568

积分

金牌会员

积分
568
发表于 2026-8-25 11:58:04 | 显示全部楼层
stm32的crc好像是crc32,在Modbus上用不了吧?只能用软件crc吧?
回复

使用道具 举报

1万

主题

7万

回帖

12万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
124289
QQ
 楼主| 发表于 2026-8-25 12:11:28 | 显示全部楼层
fxyc87 发表于 2026-8-25 11:58
stm32的crc好像是crc32,在Modbus上用不了吧?只能用软件crc吧?

可以用,合理配置CRC参数就可以了。

就是楼主位那个配置
回复

使用道具 举报

4

主题

132

回帖

144

积分

初级会员

积分
144
发表于 2026-8-25 12:24:07 | 显示全部楼层
有没有可能使用DMA处理进一步降低CPU的使用率
回复

使用道具 举报

1万

主题

7万

回帖

12万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
124289
QQ
 楼主| 发表于 2026-8-25 12:58:34 | 显示全部楼层
hance2000 发表于 2026-8-25 12:24
有没有可能使用DMA处理进一步降低CPU的使用率

没弄DMA,主要是数据长度一直是动态变化话的,DMA优势不好发挥出来。
回复

使用道具 举报

2

主题

115

回帖

121

积分

初级会员

积分
121
发表于 2026-8-26 09:20:28 | 显示全部楼层
不需要这么复杂吧,crc 模块支持设置字节序的,不需要使用 REV 转换字节序
回复

使用道具 举报

1万

主题

7万

回帖

12万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
124289
QQ
 楼主| 发表于 2026-8-26 10:32:14 | 显示全部楼层
dukelec 发表于 2026-8-26 09:20
不需要这么复杂吧,crc 模块支持设置字节序的,不需要使用 REV 转换字节序

那个硬件DR寄存器是32bit的,计算加速可以1次送4个字节数据。
回复

使用道具 举报

1万

主题

7万

回帖

12万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
124289
QQ
 楼主| 发表于 2026-8-28 11:01:13 | 显示全部楼层
颓了,工程还有个地方调用到了硬件CRC,这两个地址只能二选一了。
回复

使用道具 举报

2

主题

59

回帖

65

积分

初级会员

积分
65
发表于 2026-8-31 09:29:26 | 显示全部楼层
工匠精神
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2026-10-15 21:08 , Processed in 0.227623 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表