CRC-16 0x8005 多项式,从 C 到 C#。紧急求救

CRC-16 0x8005 polynominal, from C to C#. SOS

我有这块 C 代码,我一生都无法理解。我需要为我发送给该方法的某个字节数组计算 CRC-16,它应该给我 msb(最高有效字节)和 lsb(最低有效字节)。我还获得了一个 C 编写的应用程序来测试某些功能,该应用程序还为我提供了通过 COM 端口发送和接收的内容的日志。

奇怪的是,我将在日志中找到的十六进制字符串输入 this online calculator,但它给了我不同的结果。

我试着将方法翻译成 C#,但我不明白某些方面:

  1. pucPTR 在那里做什么(它没有在其他任何地方使用)?
  2. 第一行下面的两行代码是什么意思?
  3. 为什么第二个短 "i" 是 <=7,不应该是 <=8?
  4. if 语句中的最后一行是否意味着 usCRC 实际上是 ushort 8005?

这是代码块:

unsigned short CalculateCRC(unsigned char* a_szBufuer, short a_sBufferLen)
{
    unsigned short usCRC = 0;
    for (short j = 0; j < a_sBufferLen; j++)
    {
        unsigned char* pucPtr = (unsigned char*)&usCRC;
        *(pucPtr + 1) = *(pucPtr + 1) ^ *a_szBufuer++;
        for (short i = 0; i <= 7; i++)
        {
            if (usCRC & ((short)0x8000))
            {
                usCRC = usCRC << 1;
                usCRC = usCRC ^ ((ushort)0x8005);
            }
            else
                usCRC = usCRC << 1;
        }
    }
    return (usCRC);
}

这是我转换为字节数组并发送给方法的十六进制字符串: 02 00 04 a0 00 01 01 03

这是CRC演算应该给出的结果: 06 35

我得到的文件说这是整个数据的 CRC16 IBM (msb, lsb)。

有人可以帮忙吗?我已经坚持了一段时间了。

有没有能够将 C 方法转换为 C# 的代码大师?显然我没有能力提供这种资源。

  1. What is pucPTR doing there (it's not beeing used anywhere else)?

pucPtr用于将丑化一个unsigned short到2[=16=的数组].根据平台的字节序,pucPtr 将指向 unsigned short 的第一个字节,而 pucPtr+1 将指向 unsigned short 的第二个字节(反之亦然)。你必须知道这个算法是为小端还是大端设计的。

等效代码(并且可移植,如果代码是为大端开发的):

unsigned char rawCrc[2];
rawCrc[0] = (unsigned char)(usCRC & 0x00FF);
rawCrc[1] = (unsigned char)((usCRC >> 8) & 0x00FF);

rawCrc[1] = rawCrc[1] ^ *a_szBufuer++;

usCRC = (unsigned short)rawCrc[0] 
       | (unsigned short)((unsigned int)rawCrc[1] << 8);

对于小端,你必须反转raw[0]raw[1]

  1. What do the 2 lines of code mean, under the first for?

第一行做 1.ugly 中描述的转换。

第二行检索 a_szBufuer 指向的值并增加它。并使用 crc 的第二个(或第一个,根据字节序)字节执行 "xor"(注意 *(pucPtr +1) 相当于 pucPtr[1])并将结果存储在第二个(或第一个,根据字节序)字节中crc.

*(pucPtr + 1) = *(pucPtr + 1) ^ *a_szBufuer++;

等同于

pucPtr[1] = pucPtr[1] ^ *a_szBufuer++;
  1. Why in the second for the short "i" is <=7, shouldn't it be <=8?

您必须进行 8 次迭代,从 0 到 7。您可以将条件更改为 i = 0; i<8i=1; i<=8

  1. Last line in if statement means that usCRC is in fact ushort 8005?

不,不是。这意味着 usCRC 现在等于 usCRC XOR 0x8005^XOR bitwise operation (也称为 or-exclusive)。示例:

 0b1100110
^0b1001011
----------
 0b0101101

首先请注意,在 C 中,^ 运算符表示按位异或。

  1. What is pucPTR doing there (it's not beeing used anywhere else)?
  2. What do the 2 lines of code mean, under the first for?

从表面上看会导致错误。只是用来抓取FCS的两个字节之一,但是代码写的是endianess-dependent的方式。

字节序在处理校验和算法时非常重要,因为它们最初是为硬件移位寄存器设计的,需要 MSB 在前,也就是大端。另外,CRC常表示数据通信,数据通信可能意味着发送方、协议和接收方之间的字节顺序不同。

我猜想这段代码只是为小端机器编写的,目的是与 ms 字节进行异或。代码指向第一个字节,然后使用 +1 指针算法到达第二个字节。更正后的代码应类似于:

uint8_t puc = (unsigned int)usCRC >> 8;
puc ^= *a_szBufuer;
usCRC = (usCRC & 0xFF) | ((unsigned int)puc << 8);
a_szBufuer++;

对 unsigned int 的转换是为了可移植地防止隐式整数提升的事故。


  1. Why in the second for the short "i" is <=7, shouldn't it be <=8?

我认为它是正确的,但更易读的是它可以写​​成 i < 8

  1. Last line in if statement means that usCRC is in fact ushort 8005?

不,这意味着将您的 FCS 与多项式 0x8005 进行异或。参见 this

The document I have been given says that this is a CRC16 IBM

是的,有时也这么叫。尽管据我记得,"CRC16 IBM" 还涉及最终结果的一些位反转(?)。我会仔细检查一下。


总体而言,请谨慎使用此代码。编写它的人对字节序、整数符号和隐式类型提升没有太多线索。它是 amateur-level 代码。您应该能够在网上找到相同 CRC 算法的更安全、便携的专业版本。


非常好的阅读主题是 A Painless Guide To CRC