【问题标题】:How to calculate CRC16-CCITT/KERMIT in both C# and C如何在 C# 和 C 中计算 CRC16-CCITT/KERMIT
【发布时间】:2019-09-11 16:23:15
【问题描述】:

我正在计算 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 0 00 00 0 0 0 0 0 00 00 0 0 0 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。

这是我目前拥有的(来自this answer):

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,所以我可能会遗漏一些明显的东西。有什么想法吗?

提前感谢您的帮助!如果我需要提供任何其他信息,请告诉我。

【问题讨论】:

  • 您在网页上使用哪些选项来获得正确的结果?阳光网页解释了选项:sunshine2k.de/articles/coding/crc/understanding_crc.html 不知道为什么你有桌子。您可以使用表格方法或不使用表格方法。
  • 您在网页上使用哪些选项来获得正确的结果?阳光网页解释了选项:sunshine2k.de/articles/coding/crc/understanding_crc.html 不知道为什么你有桌子。您可以使用表格方法或不使用表格方法。当您为 16crc 生成校验和时,您会执行长度 x 字节的校验和(不包括 CRC)。然后,当您验证您为 x + 2(包括 crc)字节执行此操作时,将得到全零或全一(取决于选项)。

标签: c# c crc crc16 psoc


【解决方案1】:

当然,我在发布问题后不到 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 匹配。我想我之前的“接近匹配”只是巧合,数字相似?如果有人有,我很想听听解释。

【讨论】:

  • 您知道crc ^= 0x0000; 是空操作吗?以及 uint16 上的 crc &amp;= 0xFFFF;
  • @Ctx 哦,嘿,你说得对,哈哈。当在线计算器说 XorOut = 0x0000 这意味着不要 XOR 它时,我没有想到。我已经完全删除了该行,因为它没有意义(并编辑了匹配的答案)。是的,不需要 crc 上的面具。我也想知道为什么它在那里(也许它最初为“无符号”编写的东西是 32 位类型?)。无论如何,你是对的,我在使用 uint16 时不需要它。
  • @K_Trenholm - 您是否有理由不在 C 代码中使用 256 条目查找表,从而加快代码速度?您只需稍作更改即可将 C# 代码复制/翻译成 C 代码(C 代码不需要是类的一部分)。
  • @rcgldr 除了我碰巧找到的用于计算 CRC16 的 C 代码没有使用表格之外,没有其他具体原因。在完成这个项目之前,我可能会回去制作两端的桌子。
猜你喜欢
  • 2020-10-27
  • 2021-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-23
  • 1970-01-01
  • 2021-07-10
相关资源
最近更新 更多