【问题标题】:Strange CRC calculation奇怪的CRC计算
【发布时间】:2011-10-23 06:01:41
【问题描述】:

我要连接到一个设备(MODBUS 协议),我必须计算 CRC(CRC16)。该协议有标准实现,但我必须使用以下公式创建我的 CRC:

X15 + X2 + 1(这个公式有一个标准实现:X16 + X15 + X2 + 1)

我测试了 CRC 的不同值,但没有一个给出正确答案。我应该向端口写入一些字节,并且在这个字节串的末尾我应该写入两个 CRC 字节以获取我的设备信息。

【问题讨论】:

  • 那么问题是什么?
  • 我的问题是关于使用 X15+X2+1 公式找到 CRC 的正确方法,我应该如何将这些字节写入我的端口?我使用 C# (VS2010),我应该使用 port.write 还是 port.writeline?我应该如何将这些字节传递给端口?作为 ascii 字符串还是十六进制?谢谢
  • 您应该将字节作为原始二进制字符串(您可以从字节数组形成)传递,既不是 ASCII 字符串也不是十六进制。您可以使用 Port.Write。您可能需要使用Port.ByteArrayToString

标签: crc


【解决方案1】:

你的问题是什么? 我假设您的问题是“如何计算消息末尾的 MODBUS CRC,以便电缆另一端的 MODBUS 设备将其识别为有效的 MODBUS 消息?”

我首先尝试获取测试向量, 在我实现另一个校验和或 CRC 函数之前。

您是否有任何有效消息的示例,包括末尾的正确/预期 CRC?

根据维基百科:cyclic redundancy check, “由于高位始终为 1,并且由于 n 位 CRC 必须由溢出 n 位寄存器的 (n+1) 位除数来定义,因此一些作者认为没有必要提及除数的高位。”

因此,说 MODBUS 使用“X^15 + X^2 + 1”多项式(具有可理解的 x^16,因为它是 16 位 CRC)的作者指的是与其他作者完全相同的多项式假设 MODBUS 使用“X^16+X^15+X^2+1”多项式。 两位作者将编写生成完全相同的 CRC 并且可以相互操作的代码。

此外,人们在“正向”方向计算标准 MODBUS CRC 时经常使用魔术常数“0x8005”。 那些在“反向”方向计算标准 MODBUS CRC 的人通常使用魔术常数“0xA001”代替(“0x8005”的按位反转)。 两人编写的代码生成完全相同的 CRC 字节并且可以相互操作。

MODBUS CRC计算的在线实现有很多; 也许你会发现其中一个很有用。

具有多种选项的在线实时 CRC 计算器 http://www.zorc.breitbandkatze.de/crc.html

在线CRC计算 http://www.lammertbies.nl/comm/info/crc-calculation.html

还有很多其他的,排名不分先后:

abcdefgghijj987654334@9876786 nopq

许多实现都是面向字节的,运行速度更快,但需要一个大的查找表 (对我来说,如何测试查找表是否有效还远非显而易见)。

许多实现都是面向位的,这会产生更短的程序大小 并且有更少的尘土飞扬的角落可以潜伏错误(但需要大约 8 倍的时间来计算校验和),例如:

// warning: untested code
// optimized for size rather than speed
// (i.e., uses a bit-oriented calculation rather than table-driven calculation)
uint16_t modbusCRC(uint8_t* data, int length) {
    uint16_t crc = 0xFFFF;
    for(int pos = 0; pos<length; pos++){
        crc ^= (uint16_t)data[pos];
        for( int i=0; i<8; i++ ){
            if(crc & 1){      // LSB is set
                crc >>= 1;                 // Shift right
                crc ^= 0xA001;             // XOR 0xA001
            }else{                         // LSB is not set
                crc >>= 1;
            };
        };
    };
    return crc;
}
int main(void){
    uint8_t message[80] = { // 6-byte test vector
      0x11, 0x03, 0x00, 0x6B, 0x00, 0x03
    };
    int message_length = 6;
    uint16_t the_CRC = modbusCRC(message, message_length); // find CRC of the message
    message[message_length++] = the_CRC & 0xFF; // send low byte first
    message[message_length++] = the_CRC >> 8; // then send high byte
    assert( 0x76 == message[6] ); // test against known-good CRC
    assert( 0x87 == message[7] );
    send_message(message, message_length); // send all 8 bytes of the message,
    // including the two MODBUS CRC bytes.
    // (Must send *all* 8 bytes of the message,
    // even though there are multiple 0x00 bytes).
}

“二进制 MODBUS”(“Modbus RTU 帧格式”)将消息的所有数据作为原始 8 位字节发送,包括 2 个 CRC 字节。

“ASCII MODBUS”(“Modbus ASCII 帧格式”)将消息作为或多或少的纯 ASCII 文本发送,包括 8 位校验和。 (校验和作为 2 个字节传输 - 作为 2 个 ASCII 十六进制字符)。

【讨论】:

    猜你喜欢
    • 2011-08-03
    • 2021-08-09
    • 1970-01-01
    • 1970-01-01
    • 2017-03-01
    • 2011-11-08
    • 1970-01-01
    • 1970-01-01
    • 2019-09-27
    相关资源
    最近更新 更多