奇怪的CRC计算

时间:2011-10-23 06:01:42

标签: crc

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

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

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

1 个答案:

答案 0 :(得分:2)

你的问题是什么? 我假设您的问题是“如何在消息结束时计算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并且可以相互操作的代码。

另外,人们在“foward”方向计算标准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

和许多其他人,没有特别的顺序:

a b c d e f g h i {{ 3}} j k l m n o p

许多实现都是面向字节的,运行速度稍快但需要大查找表 (对于我来说,如何测试查找表是否有效,这一点并不明显。)

许多实现都是面向位的,这会产生更短的程序大小 并且臭虫可以潜伏的尘埃落角较少(但计算校验和需要大约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十六进制字符)。