1

我正在计算 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 应用程序和这个在线 CRC 计算器中,这个数据包的 CRC 为 0x4D8C

由于这些排列,我假设 C# 计算中的计算是合法的。无论如何,这是代码。从这个页面中提取,唯一的变化是我硬编码了多项式(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。我在输出上使用 XORs 0xFFFF 的 C 函数。将其更改为 0x0000(并处理所有 64 个字节)给我 0x16E7。

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

预先感谢您的帮助!让我知道我是否应该提供任何其他信息。

4

1 回答 1

3

当然,我在发布问题后不到 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;
}

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

于 2019-09-11T16:36:24.000 回答