CRC16 (ModBus) - 计算算法

Pra*_*hed 8 modbus crc16 boolean-polynomials

我正在使用 ModBus RTU,并且我正在尝试弄清楚如何计算 CRC16。我不需要代码示例。我只是对这个机制感到好奇。我了解到基本的 CRC 是数据字的多项式除法,根据多项式的长度用零填充。下面的测试示例应该检查我的基本理解是否正确:

  • 数据字:0100 1011
  • 多项式:1001 (x 3 +1)
  • 由于最高指数 x 3 ,所以用 3 位填充
  • 计算:0100 1011 000 / 1001 -> 余数:011

计算。

01001011000
 1001
 0000011000
      1001
      01010
       1001
       0011 
Run Code Online (Sandbox Code Playgroud)

Edit1:到目前为止已由 Mark Adler 在之前的评论/答案中验证。

在寻找答案时,我看到了很多不同的方法,包括反转、依赖小端或大端等,这些方法改变了给定的结果011

Modbus RTU CRC16

当然,我很想了解不同版本的 CRC 是如何工作的,但我的主要兴趣是简单地了解这里应用的机制。到目前为止我知道:

  • x 16 +x 15 +x 2 +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++ 或其他代码示例,我不明白。提前致谢。

编辑1:根据站点,“第一次尝试”指向另一个具有相同多项式但初始值不同(0x0000)的CRC16方法,这告诉我,计算应该是正确的。 CRC计算 如何合并初始值?

编辑2:马克·阿德勒的回答就可以了。然而,既然我可以计算 CRC16/Modbus,那么还有一些问题需要澄清。不需要但值得赞赏。

A) 计算顺序为: ... ?

  • 第一次应用 RefIn 进行完整输入(包括填充位)
  • 第二个xorInitValue 的前 16 位(在 CRC16 中)
  • 第三次应用 RefOut 进行完整输出/余数(CRC16 中余数最大 16 位)

B) 参考 RefIn 和 RefOut:尽管我使用 CRC8 或 CRC16 或 CRC32,它是否始终反映输入的 8 位和输出的所有位?

C) 我所指的网站中的第 3 列(检查)和第 8 列(XorOut)是什么意思?后者看起来相当简单,我猜它是通过计算xorRefOut 之后的值来应用的,就像 InitValue 一样?

Mar*_*ler 9

让我们一步一步来。您现在知道如何正确计算 CRC-16/BUYPASS,所以我们将从这里开始。

我们来看看CRC-16/CCITT-FALSE。该值的初始值不为零,但 RefIn 和 RefOut 仍为 false,如 CRC-16/BUYPASS。要计算数据的 CRC-16/CCITT-FALSE,您需要将数据的前 16 位与 Init 值进行异或0xffff。这给出了fe ef C0 03 00 01. 现在做你所知道的,但是使用多项式0x11021。您将得到表中的内容0xb53f

现在您知道如何应用 Init 了。下一步是处理 RefIn 和 RefOut 是否为true。我们将使用 CRC-16/ARC 作为示例。RefIn 意味着我们反映输入的每个字节中的位。RefOut 意味着我们反映余数的位。输入消息则为:80 08 03 c0 00 80。除以多项式0x18005我们得到0xb34b。现在我们反映所有这些位(不是每个字节,而是所有 16 位),然后我们得到0xd2cd。这就是您在表中看到的结果。

现在我们已经有了计算 CRC-16/MODBUS 所需的内容,它具有非零初始值 ( 0xffff) 且 RefIn 和 RefOut 均为 true。我们从消息开始,每个字节中的位都被反射前 16 位被反转。那是7f f7 03 c0 00 80。除以0x18005即可得到余数0xb393。反映这些位,我们就得到了0xc9cd预期的结果。

Init 的异或在反射后应用,您可以使用该表中的 CRC-16/RIELLO 进行验证。

补充问题的答案:

A) RefIn 与填充位无关。您反映输入字节。然而,在实际计算中,您反映的是多项式,它会处理两种反射。

B)是的。

C) 是的,XorOut 是您排除的结果,或者说最终结果。校验是 ASCII 格式的九个字节“123456789”的 CRC。