在C#中计算CRC16

时间:2015-08-11 22:59:17

标签: c# c crc16

我试图将旧代码从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;
    }

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

这是错误吗?我真的很感激任何帮助。

谢谢!最好的问候

Gutemberg

更新

根据@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必须是&#34; \ xA8&#34;和lsb必须是&#34; \ xA9&#34;为了使命令工作(设备上的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变量是&#39; \ x16&#39;和ETB是&#39; \ x17&#39;

谢谢大家的帮助!

Gutemberg

1 个答案:

答案 0 :(得分:4)

这里的问题是包含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;
}