CRC-16计算C#

CRC-16 calculation C#

我有一个通过RS232 COM 与计算机通信的终端。给我的协议说我必须发送特定的字节组合和最后发送的数据的 CRC 16 IBM 计算

我还得到了一个 C 编写的应用程序,我可以用它来测试,该应用程序写入一个包含发送数据和接收数据的日志。在该日志中,我查看是否向终端发送了这个字符串 hexString = "02 00 04 a0 00 01 01 03"。我还必须发送数据 06 35 的 CRC16 IBM 结果。

我设法以某种方式将作为示例给出的 C 方法转换为 C#。但是我的结果与我知道我必须得到的结果相去甚远。

我测试了从日志中发送数据,一切正常。一定是我计算错了。我做错了什么吗?

这是我的代码:

CRCclass:

public enum Crc16Mode : ushort
    {
        ARINC_NORMAL = 0XA02B, ARINC_REVERSED = 0xD405, ARINC_REVERSED_RECIPROCAL = 0XD015,
        CCITT_NORMAL = 0X1021, CCITT_REVERSED = 0X8408, CCITT_REVERSED_RECIPROCAL = 0X8810,
        CDMA2000_NORMAL = 0XC867, CDMA2000_REVERSED = 0XE613, CDMA2000_REVERSED_RECIPROCAL = 0XE433,
        DECT_NORMAL = 0X0589, DECT_REVERSED = 0X91A0, DECT_REVERSED_RECIPROCAL = 0X82C4,
        T10_DIF_NORMAL = 0X8BB7, T10_DIF_REVERSED = 0XEDD1, T10_DIF_REVERSED_RECIPROCAL = 0XC5DB,
        DNP_NORMAL = 0X3D65, DNP_REVERSED = 0XA6BC, DNP_REVERSED_RECIPROCAL = 0X9EB2,
        IBM_NORMAL = 0X8005, IBM_REVERSED = 0XA001, IBM_REVERSED_RECIPROCAL = 0XC002,
        OPENSAFETY_A_NORMAL = 0X5935, OPENSAFETY_A_REVERSED = 0XAC9A, OPENSAFETY_A_REVERSED_RECIPROCAL = 0XAC9A,
        OPENSAFETY_B_NORMAL = 0X755B, OPENSAFETY_B_REVERSED = 0XDDAE, OPENSAFETY_B_REVERSED_RECIPROCAL = 0XBAAD,
        PROFIBUS_NORMAL = 0X1DCF, PROFIBUS_REVERSED = 0XF3B8, PROFIBUS_REVERSED_RECIPROCAL = 0X8EE7

    }

public class Crc16
{
    readonly ushort[] table = new ushort[256];

    public ushort ComputeChecksum(params byte[] bytes)
    {
        ushort crc = 0;
        for (int i = 0; i < bytes.Length; ++i)
        {
            byte index = (byte)(crc ^ bytes[i]);
            crc = (ushort)((crc >> 8) ^ table[index]);
        }
        return crc;
    }

    public byte[] ComputeChecksumBytes(params byte[] bytes)
    {
        ushort crc = ComputeChecksum(bytes);
        return BitConverter.GetBytes(crc);
    }

    public Crc16(Crc16Mode mode)
    {
        ushort polynomial = (ushort)mode;
        ushort value;
        ushort temp;
        for (ushort i = 0; i < table.Length; ++i)
        {
            value = 0;
            temp = i;
            for (byte j = 0; j < 8; ++j)
            {
                if (((value ^ temp) & 0x0001) != 0)
                {
                    value = (ushort)((value >> 1) ^ polynomial);
                }
                else
                {
                    value >>= 1;
                }
                temp >>= 1;
            }
            table[i] = value;
        }
    }
}

处理接收到的字节的方法:

public ushort CalculateCRC(byte[] data)
    {
        Crc16 crcCalc = new Crc16(Crc16Mode.IBM_NORMAL);
        ushort crc = crcCalc.ComputeChecksum(data);
        return crc;
    }

在这种方法中,您可以 select 枚举中的多项式。

主程序 Class:

static void Main(字符串[] args) { 尝试 { Metode m = new Metode();

            string hexString = "02 00 04 a0 00 01 01 03";
            byte[] bytes = m.HexStringToByteArray(hexString);


            ushort crc = m.CalculateCRC(bytes);

            string hexResult;
            int myInt = crc;
            hexResult = myInt.ToString("X");


            //Console.WriteLine(crc.ToString());
            Console.WriteLine(hexResult);
            Console.ReadLine();
        }
        catch (Exception ex)
        {
            Metode m = new Metode();
            m.writeError(ex.Message);
        }
    }

从十六进制字符串转换为字节数组:

public byte[] HexStringToByteArray(string hexString)
    {
        hexString = hexString.Replace(" ", "");

        return Enumerable.Range(0, hexString.Length)
                 .Where(x => x % 2 == 0)
                 .Select(x => Convert.ToByte(hexString.Substring(x, 2), 16))
                 .ToArray();
    }

从字节数组转换为十六进制字符串:

public string ByteArrayToHexString(byte[] byteArray)
    {
        return BitConverter.ToString(byteArray);
    }

我做错了什么?

更新:

感谢@MarkAdler,我成功地翻译了计算。直到很晚我才注意到 CRC 计算应该是针对在线发送到终端的数据,而不是整个消息!

所以hexString实际上应该是"a0 00 01 01",没有STX/length/ETX的数据。

这是 C# 中此特定 CRC16 微积分的代码:

public ushort CalculateCRC(byte[] data, int len)
        {
            int crc = 0, i = 0;
            while (len-- != 0)
            {
                crc ^= data[i++] << 8;
                for (int k = 0; k < 8; k++)
                    crc = ((crc & 0x8000) != 0) ? (crc << 1) ^ 0x8005 : (crc << 1);
            }
            return (ushort)(crc & 0xffff);
        }

您需要提供有关您尝试实施的规范的更多信息。但是我可以马上告诉您您使用了错误的多项式。 CRC 例程右移,这意味着多项式应为 bit-reversed。结果,IBM_NORMAL 不正确。

虽然 IBM_REVERSED 是适合右移的多项式,但它可能是也可能不是您需要满足您的规范的多项式。也可能有 exclusive-or 进入或离开所需的 CRC 例程。

更新:

链接的文档提供了计算 CRC 的实际代码。你为什么不看那个?在互联网上查找随机代码来计算 CRC 而不查看文档中的内容不太可能让你走得太远。但事实并非如此。

记录的代码将 CRC 左移,与您在问题中发布的代码相反。你需要向左移动。多项式是0x8005。没有最终的exclusive-or,初始CRC值为零

这是文档中代码的简化版本,用 C 编写(此代码避免了文档中代码中内置的 little-endian 假设):

#include <stddef.h>
typedef unsigned char byte;
typedef unsigned short ushort;

ushort crc16ecr(byte data[], int len) {
    ushort crc = 0;
    for (int i = 0; i < len; i++) {
        crc ^= (ushort)(data[i]) << 8;
        for (int k = 0; k < 8; k++)
            crc = crc & 0x8000 ? (crc << 1) ^ 0x8005 : crc << 1;
    }
    return crc;
}

根据文档,CRC 是根据标签、长度和数据计算的,对于您的消息来说是 a0 00 01 01。不是全部。 (通读文档始终是出色的第一步。)运行 通过文档中的 CRC 代码,您可以得到 0x0635。该文档说首先传输最高有效字节,因此 0x06 0x35.