CRC16 (ModBus) - 计算算法
CRC16 (ModBus) - computing algorithm
我正在使用 ModBus RTU,我正在尝试弄清楚如何计算 CRC16。
我不需要代码示例。我只是对这个机制感到好奇。
我了解到基本的 CRC 是数据字的多项式除法,根据多项式的长度用零填充。
下面的测试例子是为了检查我的基本理解是否正确:
- 数据字:0100 1011
- 多项式:1001 (x3+1)
- 由于最高指数 x3
被填充 3 位
- 计算:0100 1011 000 / 1001 -> 余数:011
计算。
01001011000
1001
0000011000
1001
01010
1001
0011
Edit1:到目前为止,Mark Adler 在之前的 comments/answers.
中进行了验证
在寻找答案时,我看到了很多不同的方法,包括反转、依赖小端或大端等,这些方法改变了给定 011
.
的结果
Modbus RTU CRC16
当然,我很想了解不同版本的 CRC 是如何工作的,但我的主要兴趣是简单地了解这里应用的机制。目前我知道:
- x16+x15+x2+1 是多项式:0x18005或 0b11000000000000101
- 初始值为 0xFFFF
- 十六进制的示例消息:01 10 C0 03 00 01
- 以上消息的十六进制 CRC16:C9CD
我确实像上面的例子那样手动计算了这个,但我不想在这个问题中用二进制写下来。我认为我转换成二进制是正确的。我不知道的是如何合并初始值——它是用来填充数据字而不是零吗?还是我需要推翻答案?还有别的吗?
第一次尝试:用 0 填充 16 位。
计算出的二进制余数为 1111 1111 1001 1011
,十六进制为 FF9B
,CrC16/Modbus 不正确,但 CRC16/Bypass
正确
第二次尝试:由于初始值,用 1 填充 16 位。
计算出的二进制余数为 0000 0000 0110 0100
,十六进制为 0064
,不正确。
如果有人能解释或阐明我的假设,那就太好了。老实说,我确实花了很多时间寻找答案,但每个解释都是基于 C/C++ 或其他我不理解的代码示例。提前致谢。
EDIT1:根据 this 站点,“第一次尝试”指向另一个具有相同多项式但初始值不同 (0x0000) 的 CRC16 方法,这告诉我,计算应该是正确的。
如何合并初始值?
EDIT2:Mark Adlers Answer 可以解决问题。但是,现在我可以计算 CRC16/Modbus,还有一些问题需要澄清。不需要但值得赞赏。
A) 计算顺序为:... ?
- 第一次将 RefIn 应用于完整输入(包括填充位)
- 第二个
xor
InitValue 前 16 位(在 CRC16 中)
- 第 3 次应用 RefOut 完成 output/remainder(CRC16 中剩余最多 16 位)
B) 参考 RefIn 和 RefOut:是否始终反映 8 位输入和所有输出位,尽管如此我使用 CRC8 或 CRC16 或 CRC32?
C) 我所指的网站中的第 3 列(检查)和第 8 列(XorOut)是什么意思?后者看起来相当简单,我猜它是通过计算 RefOut 之后的值 xor
来应用的,就像 InitValue?
让我们一步一个脚印。您现在已经知道如何正确计算 CRC-16/BUYPASS,所以我们将从那里开始。
一起来看看CRC-16/CCITT-FALSE。那个初始值不为零,但 RefIn 和 RefOut 仍然为 false,如 CRC-16/BUYPASS。要计算数据的 CRC-16/CCITT-FALSE,您可以将数据的前 16 位与 0xffff
的 Init 值进行异或运算。这给出了 fe ef C0 03 00 01
。现在做你所知道的,但使用多项式 0x11021
。您将获得 table、0xb53f
.
中的内容
现在你知道如何应用Init了。下一步是处理 RefIn 和 RefOut 为 true。我们将使用 CRC-16/ARC 作为示例。 RefIn 意味着我们反映输入的每个字节中的位。 RefOut 表示我们反映余数的位。然后输入消息是:80 08 03 c0 00 80
。除以多项式 0x18005
我们得到 0xb34b
。现在我们反映所有这些位(不是在每个字节中,而是在所有 16 位中),我们得到 0xd2cd
。这就是您在 table.
中看到的结果
我们现在有了计算 CRC-16/MODBUS 所需的内容,它具有非零初始值 (0xffff
) 以及 RefIn 和 RefOut 为真。我们从消息开始,每个字节中的位反映 和 前 16 位反转。即7f f7 03 c0 00 80
。除以 0x18005
得到余数 0xb393
。反映这些位,我们得到 0xc9cd
,预期结果。
Init的异或是在反射后进行的,可以在table.
中使用CRC-16/RIELLO验证
补充问题的答案:
A) RefIn 与填充位无关。您反映输入字节。然而,在实际计算中,您反映的是多项式,它处理了两种反射。
B) 是的。
C) 是的,XorOut 是你独占的或最终的结果。校验是ASCII中的九个字节“123456789”的CRC校验。
我正在使用 ModBus RTU,我正在尝试弄清楚如何计算 CRC16。 我不需要代码示例。我只是对这个机制感到好奇。 我了解到基本的 CRC 是数据字的多项式除法,根据多项式的长度用零填充。 下面的测试例子是为了检查我的基本理解是否正确:
- 数据字:0100 1011
- 多项式:1001 (x3+1)
- 由于最高指数 x3 被填充 3 位
- 计算:0100 1011 000 / 1001 -> 余数:011
计算。
01001011000
1001
0000011000
1001
01010
1001
0011
Edit1:到目前为止,Mark Adler 在之前的 comments/answers.
中进行了验证在寻找答案时,我看到了很多不同的方法,包括反转、依赖小端或大端等,这些方法改变了给定 011
.
Modbus RTU CRC16
当然,我很想了解不同版本的 CRC 是如何工作的,但我的主要兴趣是简单地了解这里应用的机制。目前我知道:
- x16+x15+x2+1 是多项式:0x18005或 0b11000000000000101
- 初始值为 0xFFFF
- 十六进制的示例消息:01 10 C0 03 00 01
- 以上消息的十六进制 CRC16:C9CD
我确实像上面的例子那样手动计算了这个,但我不想在这个问题中用二进制写下来。我认为我转换成二进制是正确的。我不知道的是如何合并初始值——它是用来填充数据字而不是零吗?还是我需要推翻答案?还有别的吗?
第一次尝试:用 0 填充 16 位。 计算出的二进制余数为
1111 1111 1001 1011
,十六进制为FF9B
,CrC16/Modbus 不正确,但 CRC16/Bypass 正确
第二次尝试:由于初始值,用 1 填充 16 位。 计算出的二进制余数为
0000 0000 0110 0100
,十六进制为0064
,不正确。
如果有人能解释或阐明我的假设,那就太好了。老实说,我确实花了很多时间寻找答案,但每个解释都是基于 C/C++ 或其他我不理解的代码示例。提前致谢。
EDIT1:根据 this 站点,“第一次尝试”指向另一个具有相同多项式但初始值不同 (0x0000) 的 CRC16 方法,这告诉我,计算应该是正确的。
EDIT2:Mark Adlers Answer 可以解决问题。但是,现在我可以计算 CRC16/Modbus,还有一些问题需要澄清。不需要但值得赞赏。
A) 计算顺序为:... ?
- 第一次将 RefIn 应用于完整输入(包括填充位)
- 第二个
xor
InitValue 前 16 位(在 CRC16 中) - 第 3 次应用 RefOut 完成 output/remainder(CRC16 中剩余最多 16 位)
B) 参考 RefIn 和 RefOut:是否始终反映 8 位输入和所有输出位,尽管如此我使用 CRC8 或 CRC16 或 CRC32?
C) 我所指的网站中的第 3 列(检查)和第 8 列(XorOut)是什么意思?后者看起来相当简单,我猜它是通过计算 RefOut 之后的值 xor
来应用的,就像 InitValue?
让我们一步一个脚印。您现在已经知道如何正确计算 CRC-16/BUYPASS,所以我们将从那里开始。
一起来看看CRC-16/CCITT-FALSE。那个初始值不为零,但 RefIn 和 RefOut 仍然为 false,如 CRC-16/BUYPASS。要计算数据的 CRC-16/CCITT-FALSE,您可以将数据的前 16 位与 0xffff
的 Init 值进行异或运算。这给出了 fe ef C0 03 00 01
。现在做你所知道的,但使用多项式 0x11021
。您将获得 table、0xb53f
.
现在你知道如何应用Init了。下一步是处理 RefIn 和 RefOut 为 true。我们将使用 CRC-16/ARC 作为示例。 RefIn 意味着我们反映输入的每个字节中的位。 RefOut 表示我们反映余数的位。然后输入消息是:80 08 03 c0 00 80
。除以多项式 0x18005
我们得到 0xb34b
。现在我们反映所有这些位(不是在每个字节中,而是在所有 16 位中),我们得到 0xd2cd
。这就是您在 table.
我们现在有了计算 CRC-16/MODBUS 所需的内容,它具有非零初始值 (0xffff
) 以及 RefIn 和 RefOut 为真。我们从消息开始,每个字节中的位反映 和 前 16 位反转。即7f f7 03 c0 00 80
。除以 0x18005
得到余数 0xb393
。反映这些位,我们得到 0xc9cd
,预期结果。
Init的异或是在反射后进行的,可以在table.
中使用CRC-16/RIELLO验证补充问题的答案:
A) RefIn 与填充位无关。您反映输入字节。然而,在实际计算中,您反映的是多项式,它处理了两种反射。
B) 是的。
C) 是的,XorOut 是你独占的或最终的结果。校验是ASCII中的九个字节“123456789”的CRC校验。