【问题标题】:Calculating CRC16 in C#在 C# 中计算 CRC16
【发布时间】:2015-08-11 22:59:16
【问题描述】:

我正在尝试将旧代码从 C 移植到 C#,它基本上接收一个字符串并返回它的 CRC16...

C方法如下:

#define CRC_MASK 0x1021 /* x^16 + x^12 + x^5 + x^0 */

UINT16 CRC_Calc (unsigned char *pbData, int iLength)
{
    UINT16 wData, wCRC = 0;
    int i;

    for ( ;iLength > 0; iLength--, pbData++) {
        wData = (UINT16) (((UINT16) *pbData) << 8);
        for (i = 0; i < 8; i++, wData <<= 1) {
            if ((wCRC ^ wData) & 0x8000)
                wCRC = (UINT16) ((wCRC << 1) ^ CRC_MASK);
            else
                wCRC <<= 1;
        }
    }
    return wCRC;
}

我移植的 C# 代码是这样的:

private static ushort Calc(byte[] data)
    {
        ushort wData, wCRC = 0;
        for (int i = 0; i < data.Length; i++)
        {
            wData = Convert.ToUInt16(data[i] << 8);
            for (int j = 0; j < 8; j++, wData <<= 1)
            {
                var a = (wCRC ^ wData) & 0x8000;
                if ( a != 0)
                {
                    var c = (wCRC << 1) ^ 0x1021;
                    wCRC = Convert.ToUInt16(c);
                }
                else
                {
                    wCRC <<= 1;
                }
            }
        }
        return wCRC;
    }

测试字符串是“OPN”...它必须返回一个 uint,即 (ofc) 2 个字节 A8 A9 并且 #CRC_MASK 是该计算的多项式。我确实在这里和网上找到了几个 CRC16 的例子,但没有一个能达到这个结果,因为这个 CRC 计算必须与我们连接的设备匹配。

哪里出错了?非常感谢任何帮助。

谢谢!最好的问候

古腾堡

更新

根据@rcgldr 的回答,我整理了以下示例:

_serial = new SerialPort("COM6", 19200, Parity.None, 8, StopBits.One);
        _serial.Open();
        _serial.Encoding = Encoding.GetEncoding(1252);
        _serial.DataReceived += Serial_DataReceived;
        var msg = "OPN";
        var data = Encoding.GetEncoding(1252).GetBytes(msg);
        var crc = BitConverter.GetBytes(Calc(data));
        var msb = crc[0].ToString("X");
        var lsb = crc[1].ToString("X");
        //The following line must be something like: \x16OPN\x17\xA8\xA9
        var cmd = string.Format(@"{0}{1}{2}\x{3}\x{4}", SYN, msg, ETB, msb, lsb);
        //var cmd = "\x16OPN\x17\xA8\xA9";
        _serial.Write(cmd);

cmd 变量的值是我要发送到设备的值。如果您查看注释的 cmd 值,这是一个工作字符串。 CRC16 的 2 个字节位于最后两个参数(msb 和 lsb)中。因此,在此处的示例中,msb 必须是“\xA8”并且 lsb 必须是“\xA9”才能使命令正常工作(设备上的 CRC16 匹配)。

有什么线索吗?

再次感谢。

更新 2 对于那些属于相同情况的人,您需要使用 \x 格式化字符串,这是我为使其正常工作所做的:

protected string ToMessage(string data)
    {
        var msg = data + ETB;
        var crc = CRC16.Compute(msg);
        var fullMsg = string.Format(@"{0}{1}{2:X}{3:X}", SYN, msg, crc[0], crc[1]);
        return fullMsg;
    }

这将返回给我的完整消息,我需要在其上包含 \x。 SYN 变量是 '\x16' 而 ETB 是 '\x17'

谢谢大家的帮助!

古腾堡

【问题讨论】:

  • 不确定,但您可能需要检查&lt;&lt; 运算符是否没有在单词末尾添加1's
  • 对不起,我的错,这似乎只适用于&gt;&gt;(用于签名类型)运算符。 msdn.microsoft.com/en-us/library/xt18et0d.aspx
  • 你确定 0xA8A9 吗?该代码(在 lammertbies.nl/comm/info/crc-calculation.html 测试时)给出 0x9629,这似乎对应于 CRC-CCITT (XModem)。
  • *尚未测试 C 版本。
  • 如果 C 和 C# 版本都给出 0x9629,我会说代码可以正常工作。编码呢?

标签: c# c crc16


【解决方案1】:

这里的问题是包含 ETB (\x17) 的消息有 4 个字节长(前导同步字节不用于 CRC): "OPN\x17" == {'O', 'P' , 'N', 0x17},这导致将 {0xA8, 0xA9} 的 CRC 附加到消息中。所以CRC函数是正确的,但原始测试数据不包括第4个字节,即0x17。

这是一个工作示例(至少在 VS2015 express 中)。

private static ushort Calc(byte[] data)
{
    ushort wCRC = 0;
    for (int i = 0; i < data.Length; i++)
    {
        wCRC ^= (ushort)(data[i] << 8);
        for (int j = 0; j < 8; j++)
        {
            if ((wCRC & 0x8000) != 0)
                wCRC = (ushort)((wCRC << 1) ^ 0x1021);
            else
                wCRC <<= 1;
        }
    }
    return wCRC;
}

【讨论】:

  • 在这两个示例中,不需要第二次强制转换为无符号 16 位整数 - 不,它是必需的。在 C# 中,移位运算符是 defined to return variations of int and long。这些需要被转换回ushort^ 运算符也是如此。在这个问题中有一个deleted answer,你can't see,它说的是同样的事情,它显然被删除了,因为这个变化实际上没有任何区别。
  • 因此不需要转换为 ushort,因为 C# 无论如何都会在转换之前提升参数。不过,有什么不同的是用unchecked((ushort)(value)) 替换Convert.ToUInt16(value) 以匹配C 行为。使用当前代码,第二个ToUInt16会出现溢出异常。
猜你喜欢
  • 2023-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-27
  • 1970-01-01
  • 2012-12-10
  • 2021-11-20
  • 1970-01-01
相关资源
最近更新 更多