用于非常短的串行分组的分组成帧

时间:2016-08-18 11:25:40

标签: c embedded crc serial-communication error-detection

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

 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字节)
  • 别的什么?

3 个答案:

答案 0 :(得分: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)取决于您的媒体。

答案 1 :(得分:1)

  

有一个问题:CRC4的建议消息长度为11位,此处计算为12位。

不,这里计算的是16位。

  

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

有关CRC的建议可能是指您是否有100%的机会发现单位错误。所有的CRC都会遇到多位错误,并不一定能找到它们。

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

  

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

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

  

我的问题是 - 有没有办法在不添加任何字节的情况下纠正帧?

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

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

  

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

不太可能。 CRC几乎是唯一的专业校验和算法。多项式是根据噪声的例外性质来挑选的 - 它们选择一个多项式,它可以提醒尽可能少的噪声。然而,这主要是出于学术兴趣,因为没有CRC专家可以知道你的具体应用中的噪音是什么样的。

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

  

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

如果不知道数据的性质,任何人都无法回答这个问题。

答案 2 :(得分:0)

如果CRC主要用于偏执(来自评论),您可以放弃一些错误检查的稳健性和处理时间。

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

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

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