奇怪的CRC计算

发布于 2024-12-11 04:45:47 字数 209 浏览 0 评论 0 原文

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

X15 + X2 + 1(使用以下公式有一个标准实现:X16 + X15 + X2 + 1)

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

I'm going to connect to a device (MODBUS protocol), and I have to calculate CRC (CRC16). There are standard implementations of this protocol, but I have to create my CRC using this formula:

X15 + X2 + 1 (there is a standard implementation with this formula: X16 + X15 + X2 + 1)

I've tested differnt values for CRC, but none of them is giving me the correct answer. I should write some bytes to a port, and at the end of this byte string I should write two CRC bytes in order to obtain my device information.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

戏剧牡丹亭 2024-12-18 04:45:47

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

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

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

根据维基百科:循环冗余检查
“由于高阶位始终为 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

以及许多其他内容,排名不分先后:

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

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

许多实现都是面向位的,这会产生更短的程序大小
并且有更少的 bug 潜伏的灰尘角落(但计算校验和需要大约 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 发送-bit 字节,包括 2 个 CRC 字节。

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

What is your question?
I'm assuming your question is "How do I calculate a MODBUS CRC at the end of a message so the MODBUS device at the other end of the cable recognizes it as a valid MODBUS message?"

I try to get test vectors in hand first,
before I implement yet another checksum or CRC function.

Do you have any examples of valid messages, including the proper/expected CRC at the end?

According to Wikipedia: cyclic redundancy check,
"Since the high-order bit is always 1, and since an n-bit CRC must be defined by an (n+1)-bit divisor which overflows an n-bit register, some writers assume that it is unnecessary to mention the divisor's high-order bit."

So writers that say that MODBUS uses a "X^15 + X^2 + 1" polynomial (with an understood x^16, since it's a 16-bit CRC) are referring to exactly the same polynomial as other writers that say MODBUS uses a "X^16+X^15+X^2+1" polynomial.
Both authors will write code that generates exactly the same CRC and are interoperable with each other.

Also, people calculating the standard MODBUS CRC in the "foward" direction often use the magic constant "0x8005".
Those calculating the standard MODBUS CRC in the "reverse" direction often use the magic constant "0xA001" instead (the bitwise-reverse of "0x8005").
Both people write code that generates exactly the same CRC bytes and are interoperable with each other.

There are many implementations online of MODBUS CRC calculation;
perhaps you might find one of them useful.

online live CRC calculator with many options
http://www.zorc.breitbandkatze.de/crc.html

On-line CRC calculation
http://www.lammertbies.nl/comm/info/crc-calculation.html

and many others, in no particular order:

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

Many implementations are byte-oriented, which run a little faster but require a big lookup table
(and it's far from obvious to me how to test whether the lookup table is valid).

Many implementations are bit-oriented, which produce a much shorter program size
and have fewer dusty corners where bugs can lurk (but take about 8 times as long to calculate the checksum), such as the following:

// 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).
}

"Binary MODBUS" ("Modbus RTU Frame Format") sends all the data of the message as raw 8-bit bytes, including the 2 CRC bytes.

"ASCII MODBUS" ("Modbus ASCII Frame Format") sends the message as more-or-less plain ASCII text, including an 8 bit checksum.
(The checksum is transmitted as 2 bytes -- as 2 ASCII hexadecimal characters).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文