0

我们为嵌入式设备设计了简单的固定长度协议。每个数据包只有两个字节:

 bits |  15..12  |    11..4     |  3..0   |
      |  OpCode  |     DATA     |  CRC4   |

我们使用“基于 crc 的成帧”,即接收方收集两个字节,计算 CRC4,如果匹配,则认为帧有效。如您所见,没有帧开始或帧结束。

有一个问题:CRC4 的推荐消息长度为 11 位,此处计算为 12 位。据我了解,这意味着 CRC 错误检测属性会降低(但我不确定会降低多少)。

(顺便说一句,如果有人需要 CRC4(或任何其他)的代码并且觉得自己没有足够的技能来编写它,boost 有非常好的 boost::crc 函数可以计算任何 crc )

问题是:这种基于 crc 的框架不起作用,并且我们得到框架错误,即来自一条消息的第二个字节和来自以下消息的第一个字节有时会形成正确的消息。

我的问题是 - 有没有办法在不添加更多字节的情况下纠正框架?我们花了相当长的时间压缩这两个字节中的所有内容,就这样扔掉它会有点难过。不过,我们在操作码字段中确实有一个备用位。

  • 基于时间的成帧不会很可靠,因为我们的无线电频道喜欢一次“吐出”几个数据包
  • 也许还有其他一些比 CRC4 更有效的错误检测方法?

如果我们必须附加更多字节,那么最好的方法是什么?

  • 我们可以使用帧开始字节和字节填充(例如 COBS)(+2 个字节,但我不确定如何处理损坏的消息)
  • 我们可以使用帧开始半字节并将 CRC 扩展为 CRC8(+1 字节)
  • 还有什么?
4

3 回答 3

2

执行您所要求的一种常见方法是在启动时“寻找帧”,并在接受任何数据包之前需要 N 个连续的好数据包。这可以使用具有 3 种状态的状态机来实现:HUNT、LOF(帧丢失)、SYNC

它可能是这样的:

#define GOOD_PACKETS_REQUIRED_BEFORE_SYNC 8
int state = HUNT;
int good_count = 0;

Packet GetPacket(void)
{
    unsigned char fb = 0;
    unsigned char sb = 0;

    while (1)
    {
        if (state == HUNT)
        {
            fb = sb;
            sb = GetNextByteFromUART();

            if (IsValidCRC(fb, sb))
            {
                state = LOF;
                good_count = 1;
            }
        }
        else if (state == LOF)
        {
            fb = GetNextByteFromUART();
            sb = GetNextByteFromUART();

            if (IsValidCRC(fb, sb))
            {
                good_count++;
                if (good_count >= GOOD_PACKETS_REQUIRED_BEFORE_SYNC)
                {
                    state = SYNC;
                }
            }
            else
            {
                state = HUNT;
                good_count = 0;
            }
        }
        else if (state == SYNC)
        {
            fb = GetNextByteFromUART();
            sb = GetNextByteFromUART();

            if (IsValidCRC(fb, sb))
            {
                return packet(fb, sb);;
            }

            // SYNC lost! Start a new hunt for correct framing
            state = HUNT;
            good_count = 0;
        }
    } 
}

您可以找到几种使用这种(或类似)技术的标准通信协议,例如 ATM 和 E1(https://en.wikipedia.org/wiki/E-carrier)。该原理有不同的变体。例如,您可能希望在接收到第一个坏包(减少 good_count)时从 SYNC 转到 LOF,然后在第二个连续的坏包上从 LOF 转到 HUNT。这将减少重新构图所需的时间。上面只是显示了一个非常简单的变体。

注意:在现实世界的代码中,您可能无法接受上述阻塞函数。上述代码仅用于描述原理。

您是否需要 CRC 或可以使用固定的帧字(例如 0xB)取决于您的媒体。

于 2016-08-18T13:21:10.620 回答
0

如果 CRC 主要用于偏执狂(来自评论),您可以放弃一些错误检查的鲁棒性和处理器时间来进行框架。

由于操作码中有一个空闲位,因此始终将第一个字节的最高有效位设置为零。然后在传输之前,但在计算 CRC 之后,将第二个字节的最高有效位设置为 1。

然后,一个帧是两个连续的字节,其中第一个最高有效位为零,第二个最高有效位为 1。如果两个字节未能通过 CRC 校验,则将第二个字节的最高有效位设置为零并重新计算以查看数据包在传输前是否翻转了该位。

缺点是大约一半的时间将计算两次 CRC。此外,设置成帧位可能会导致无效数据与 CRC 匹配。

于 2016-08-19T23:29:55.463 回答
0

有一个问题:CRC4 的推荐消息长度为 11 位,这里计算为 12 位。

不,这里是针对 16 位计算的。

据我了解,这意味着 CRC 错误检测属性会降低(但我不确定会降低多少)。

关于 CRC 的建议可能是指您是否有 100% 的机会找到单位错误。所有 CRC 都与多位错误作斗争,不一定会找到它们。

在处理有关 UART 的 CRC 可靠性的计算时,您还必须考虑开始位和停止位。位错误也可能在那里发生,在这种情况下,硬件可能会或可能不会帮助找到错误。

一个消息的第二个字节和下一个消息的第一个字节有时会形成正确的消息

当然。你没有同步机制,你期望什么?这与CRC无关。

我的问题是 - 有没有办法在不添加更多字节的情况下纠正框架?

您必须牺牲每个字节一位作为同步标志或增加数据包长度。或者,您可以在数据位之间使用不同的延迟。也许直接发送两个字节,然后使用延迟。

选择哪种方法取决于数据的性质和您的规范。SO上没有人可以告诉您您的规格是什么样的。

也许还有其他一些比 CRC4 更有效的错误检测方法?

不见得。CRC 几乎是唯一的专业校验和算法。多项式是根据噪声的例外性质选择的——它们选择一个尽可能少地提醒噪声的多项式。然而,这主要是学术兴趣,因为没有 CRC 专家可以知道噪声在您的特定应用程序中的样子。

替代方案是求和、异或、奇偶校验、1 的计数等......所有这些都非常糟糕,概率方面。

如果我们必须附加更多字节,那么最好的方法是什么?

在不了解数据性质的情况下,没有人可以回答这个问题。

于 2016-08-18T13:00:00.647 回答