如何在 C# 和 C 中计算 CRC16-CCITT/KERMIT

How to calculate CRC16-CCITT/KERMIT in both C# and C

我正在计算 CRC16-CCITT/KERMIT,以便我可以检查 C# Winforms 应用程序和微控制器 (PSoC5LP/Arm Cortex-M3) 之间传输的 64 字节数据包的数据完整性。我很接近,但只是没有完全让 CRC 计算在两者之间对齐并且很难找出原因。我计算 CRC 的示例数据包是:

02 03 01 02 03 04 05 07 08 09 0A 0B 00 00 06 0E 0C 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

在我的 Winforms 应用程序和这个 online CRC calculator

中,此数据包的 CRC 结果为 0x4D8C

由于这些排列,我假设 C# 计算中的计算是合法的。无论如何,这是代码。从 this page 中提取,唯一的变化是我对多项式 (0x8408) 进行了硬编码:

public static class Crc16
{
    const ushort polynomial = 0x8408;
    static readonly ushort[] table = new ushort[256];

    public static ushort ComputeChecksum(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;
    }

    static Crc16()
    {
        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;
        }
    }
}

现在问题正在为微控制器获取一个有效的 CRC16-CCITT/KERMIT 计算器函数,它将为该数据包生成相同的 CRC。

这是我目前拥有的(摘自 ):

uint16_t crc16k(uint16_t crc, uint8_t *mem, uint8_t len) {
    uint8_t *data = mem;

    if (data == NULL){
        return 0;
    }

    crc = ~crc;
    crc &= 0xffff;

    while (len--) {
        crc ^= *data++;
        for (uint8_t k = 0; k < 8; k++)
            crc = crc & 1 ? (crc >> 1) ^ 0x8408 : crc >> 1;
    }

    crc ^= 0xFFFF;
    return crc;
}

我是这样称呼它的:

uint16_t crc_calc = crc16k(0x0000, message_in, 64);

这里是我要搞笑的地方。该数据包是 64 字节,但实际上(对于此数据包),只有前 29 个字节是我正在使用的数据。其余的只是填充以满足 64 字节。当我在 WinForms 端计算 CRC 时,看起来它使用了所有 64 个字节,包括填充。当我在微控制器端做同样的事情时,我得到的结果是 0xE918。奇怪的是,如果我将长度参数限制为我感兴趣的 29 个字节,我会得到 0x4C8B,它非常接近我正在寻找的 0x4D8C。我还注意到,在我使用的在线计算器中,它声称输出的 XOR 应该是 0x0000。我在输出上使用异或 0xFFFF 的 C 函数。将其更改为 0x0000(并处理所有 64 个字节)会得到 0x16E7。

所以我不确定问题出在哪里。这是我第一次使用 CRC,所以我可能会遗漏一些明显的东西。有什么想法吗?

提前感谢您的帮助!让我知道是否需要我提供任何其他信息。

当然,我在发布问题后不到 10 分钟就收到了(不是总是这样吗?)

我认为 CRC-16/KERMIT 的 C 代码看起来实际上是 CRC-16/X-25。我想我很困惑,因为我从 的答案中提取代码的问题是询问 KERMIT,但答案说它是 X-25。

删除函数开头的 crc 按位取反:

crc = ~crc;

以及删除

crc ^= 0xFFFF;  

这给我留下了:

uint16_t crc16k(uint16_t crc, uint8_t *mem, uint8_t len) {
    uint8_t *data = mem;

    if (data == NULL){
        return 0;
    }

    while (len--) {
        crc ^= *data++;
        for (uint8_t k = 0; k < 8; k++)
            crc = crc & 1 ? (crc >> 1) ^ 0x8408 : crc >> 1;
    }

    return crc;
}

这似乎有效,并且 CRC 在我的 Winforms 应用程序和 PSoC5 之间匹配。我想我之前的 "near match" 只是巧合,数字相似?如果有人有解释,我很想听听。