heyanmei 发表于 2025-6-30 11:44

APM32F072实现CRC16-MODBUS

本帖最后由 heyanmei 于 2025-7-1 19:41 编辑

# APM32F072实现CRC16-MODBUS

## CRC基本原理

### 什么是CRC

* [ ] 循环冗余校验(英语:Cyclic redundancy check,简称CRC),由W.Wesley Peterson于1961年首次提出的一种纠错码理论。
* [ ] CRC是一种数据纠错方法,主要应用于数据通信或者数据存储的场合,用来检测或校验数据传输或者数据存储后可能出现的错误,特别是擅长检测由传输通道中的噪声引起的常见错误

### 多项式的概念

* [ ] 在CRC校验中,多项式其实就是收发双方约定好的一个二进制数,当然要成为这个二进制数是要有一些要求的。
* [ ] 对任意的二进制数都可以构造一个与其对应的二进制系数多项式。比如:二进制10011b,这个二进制数对应的多项式公式就是:
P(x)= x^4+x+1

### 成为多项式要满足的条件

* [ ] 最高位和最低位都必须是1
* [ ] 当数据在传输过程中出错时,CRC的校验码不应该是0(也就是要有余数)
* [ ] 该多项式要有最大的错误检测能力

## 模2运算

### 模2运算的概念

* [ ] 模2运算,是一种二进制运算,是二进制编码理论中的运算基础。这种运算和我们以前学的四则运算的规则不同,模2运算不考虑进位、借位这些规则,它有着一套新的运算规则。
* [ ] 模2运算也有加减乘除法

### 模2运算加减法规则

* [ ] 加法规则:1+1=0    0+0=0    1+0=1    0+1=1
* [ ] 减法规则:0−0=0    1−1=0    0−1=1    1−0=1
* [ ] 加法不考虑进位,减法不考虑进位

模2加减法运算运算结果都是一样的,他和C语言的异或运算有着一样的规则。

!(data/attachment/forum/202506/30/111529p441r1vvarn3jiqa.png "image.png")

### 模2运算乘除法规则

* [ ] 乘法规则:0×0=0    0×1=0    1×0=0    1×1=1
* [ ] 除法规则:0÷1=0    1÷1=1
* [ ] 模2的乘除法运算,与普通的运算有着类似的演算规则。但是在乘法时乘积相加,除法时余数和除数相减,就需要按照模2加减法规则运算。
* [ ] 除法:当被除数位数大于除数时,商1,不够则商为0。然后被除数的位数补下来,如果最终余数小于除数时,那么除法终止运算。

!(data/attachment/forum/202506/30/111518pzrk6ys567yk686r.png "image.png")

## CRC校验

### CRC校验的检测过程

在计算过程中,我们首先要知道二进制多项式,这个多项式其实就是除数,而待校验的数据就是被除数,最终进行模2除法运算得到的余数,就是CRC校验码。

1. 先约定好收发双方选择的CRC多项式,这个多项式其实就是计算过程中的除数。
2. 在待校验的数据(可看作是发送方的数据)后面加上n个0,这个n是多少取决于你所选择的多项式。比如你选择的多项式是:P(x)=x^4+x+1。那么CRC检验码的位宽就是4,也就是说你要补4个0
3. 对待校验数据进行模2除法运算,得出的余数就是CRC校验值。
4. 然后把CRC检验码添加到待检验数据的末尾。
5. 接收方,把接收到的数据,也进行模2除法的计算过程,如果余数为0,那么接收正确,如果不为0,那么数据在传输过程中出错。

### CRC校验码计算演示过程

* [ ] 下面以多项式:P(x)=x^4+x+1为例,该多项式对应的二进制数就是:10011,进行计算演示。
* [ ] 原始数据假设是:1100110

!(data/attachment/forum/202506/30/111500xfbbsz3g9a39ahas.png "image.png")

### 常见的CRC多项式

CRC应用中的多项式不用我们自己去设计。前人经过研究,已经积累了各种应用场合的多项式了,不同的场合有不同的多项式,以便实现最大的纠错能力。下表列出了一小部分常用CRC的多项式表示:

!(data/attachment/forum/202507/01/194100ydafwoxwcqialjql.png "image.png")


## APM32F072的CRC函数库

| 函数名                     | 描述                                  |
| ---------------------------- | ------------------------------------- |
| CRC_Reset                  | 将CRC外设寄存器重置为其默认复位值   |
| CRC_ResetDATA                | 复位CRC数据寄存器                     |
| CRC_SetPolynomialSize      | 设置CRC多项式的大小                   |
| CRC_SetPolynomialValue       | 设置CRC多项式系数                     |
| CRC_SelectReverseInputData   | 选择要对输入数据执行的反向操作      |
| CRC_EnableReverseOutputData| 对输出数据进行反向操作                |
| CRC_DisableReverseOutputData | 禁止对输出数据进行反向操作            |
| CRC_WriteInitRegister      | 写CRC初始化值                         |
| CRC_CalculateCRC             | 计算给定数据字的32位CRC(32位)       |
| CRC_CalculateCRC8bits      | 计算给定数据字的32位CRC(8位)      |
| CRC_CalculateCRC16bits       | 计算给定数据字的32位CRC(16位)       |
| CRC_CalculateBlockCRC      | 计算给定数据字缓冲区的32位CRC(32位) |
| CRC_ReadCRC                  | 返回当前的CRC值                     |
| CRC_WriteIDRegister          | 在独立数据寄存器中存储8位数据         |
| CRC_ReadIDRegister         | 读独立数据寄存器中的8位数据         |

## APM32F072的CRC

APM32F072的CRC相比于APM32F103的CRC拥有更强的灵活性,可以配置输入数据是否翻转、输出数据是否翻转、CRC的初始值可配置、CRC多项式值可配置、多项式的大小可配置,因此APM32F072的CRC自由度相当高,不单纯只局限于CRC32-以太网;下面演示一下使用APM32F072实现CRC16-MODBUS

### CRC16-MODBUS的配置

```

void CRC16_MODBUS_Init(void)
{
        /* 开启CRC时钟 */
        RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_CRC);
        /* CRC复位 */
        CRC_Reset();

        /* 设置CRC多项式的大小为16 */
        CRC_SetPolynomialSize(CRC_POLYNOMIAL_SIZE_16);
        /* 设置CRC多项式系数为8005 */
        CRC_SetPolynomialValue(0x8005);
        /* 对输出数据进行反向操作 */
        CRC_EnableReverseOutputData();
        /* 选择要对输入数据执行的反向操作 */
        CRC_SelectReverseInputData(CRC_REVERSE_INPUT_DATA_16B);
        /* 配置CRC初始值 */
        CRC_WriteInitRegister(0xFFFF);
}
```

因为函数库里面实现数组的CRC运算是32位的,16位的需要我们自己编写一下函数

```
uint16_t CRC16_CalculateBlockCRC(uint16_t pBuffer[], uint32_t bufferLength)
{
    uint32_t index = 0;

    for (index = 0; index < bufferLength; index++)
    {
                CRC_CalculateCRC16bits(pBuffer);
    }

    return (CRC->DATA);
}
```

### CRC16-MODBUS演示和验证

demo验证和CRC在线网站计算的CRC16-MODBUS值一致,说明CRC16-MODBUS验证OK

!(data/attachment/forum/202506/30/113208r7zra7ruia38a5yf.png "image.png")

!(data/attachment/forum/202506/30/113240kd7dn0tw4tz0cj9s.png "image.png")

霜咬回响 发表于 2025-6-30 14:35

F072已经支持CRC16了呀!
硬件CRC校验还是非常好用。

记忆花园 发表于 2025-6-30 19:09

查了一下APM32E030,其尚未支持CRC硬件外设自定义校验多项式。

heyanmei 发表于 2025-6-30 19:17

记忆花园 发表于 2025-6-30 19:09
查了一下APM32E030,其尚未支持CRC硬件外设自定义校验多项式。

APM32E030目前是不支持硬件CRC自定义的

heyanmei 发表于 2025-6-30 19:20

#申请原创# @21小跑堂

转瞬回声 发表于 2025-6-30 20:27

我觉得CRC的计算方式非常适合硬件实现。
仅通过异或,移位便能轻松完成。

星云狂想曲 发表于 2025-6-30 20:52

如果能使用硬件方式实现,还是首先硬件方式。
但又会带来移植性的问题。

Dick Hou 发表于 2025-7-1 09:18

crc-16的多项式公式是错的!

heyanmei 发表于 2025-7-1 10:07

Dick Hou 发表于 2025-7-1 09:18
crc-16的多项式公式是错的!

CRC16-MODBUS的多项式系数是0x8005哦,建议百度一下CRC16-MODBUS的多项式系数

Dick Hou 发表于 2025-7-1 10:18

heyanmei 发表于 2025-7-1 10:07
CRC16-MODBUS的多项式系数是0x8005哦,建议百度一下CRC16-MODBUS的多项式系数

8005前面那一栏,公式不对,你可以根据你的公式,算一下是不是8005

正确的应该是:
X^16+X^15+X^2+1,这样才能得到8005

Dick Hou 发表于 2025-7-1 10:20

heyanmei 发表于 2025-7-1 10:07
CRC16-MODBUS的多项式系数是0x8005哦,建议百度一下CRC16-MODBUS的多项式系数



如上,这个多项式,才能得到8005

heyanmei 发表于 2025-7-1 14:47

Dick Hou 发表于 2025-7-1 10:20
如上,这个多项式,才能得到8005

这个是二进制,多项式系数换算成16进制是0x8005,上面那个写8005的函数,是把CRC16-MODBUS的多项式系数写进CRC的多项式寄存器

Dick Hou 发表于 2025-7-1 17:38

heyanmei 发表于 2025-7-1 14:47
这个是二进制,多项式系数换算成16进制是0x8005,上面那个写8005的函数,是把CRC16-MODBUS的多项式系数写 ...

算了,你还是没弄明白。

你根据自己的表格,将第二列的多项式,算一下十六进制,和第三列对一下,看是否正确。只有CRC16的第二列是错的。

我11楼已经贴图了,就是USB的CRC-16,那么大红框框出来的多项式,你视而不见?和你表格里的第二列是不一样的。11楼的二进制,换算过来十六进制就是8005,多项式和十六进制,是能对上的。

Dick Hou 发表于 2025-7-1 17:43


左边是你的表格,右边是百度的结果,还看不明白吗?

heyanmei 发表于 2025-7-1 19:39

Dick Hou 发表于 2025-7-1 17:43
左边是你的表格,右边是百度的结果,还看不明白吗?

看到了,帖子里的常见多项式公式那里有问题,我是在网上找到一个图片截图的,我把帖子里的常见CRC多项式图片更新成正确的,感谢指正[抱拳]

死不低头 发表于 2025-7-2 18:42

厉害,我用STM32H7的硬件CRC测试很多次,算出来的CRC16-MODBUS都不对,难道我人品问题?

jobszheng 发表于 2025-7-3 09:58

这个真方便。昨天我计算CRC32花了差不多一天的时间。

VelvetNight 发表于 2025-7-3 10:33

我还是在使用FreeModbus里面提供的CRC16校验算法
页: [1]
查看完整版本: APM32F072实现CRC16-MODBUS