串行协议定界/同步技术


24

由于异步串行通信甚至在当今的电子设备中也很普遍,我相信我们许多人会不时遇到这样的问题。考虑与串行线(RS-232或类似产品)连接并且需要连续交换信息的电子设备D和计算机。即每个发送一个命令帧,并每个发送一个状态报告/遥测帧答复(报告可以作为对请求的响应发送,也可以独立发送-在这里并不重要)。通信帧可以包含任何任意二进制数据。假设通信帧是固定长度的分组。PCPCX msDY ms

问题:

由于协议是连续的,因此接收方可能会失去同步,或者只是在进行中的发送帧中间“加入”,因此它只是不知道帧起始位置(SOF)在哪里。根据数据相对于SOF的位置,数据具有不同的含义,接收到的数据可能会永久损坏。

所需的解决方案

可靠的定界/同步方案可在恢复时间短的情况下检测SOF(即重新同步所需的时间不超过1帧)。

我了解(并使用了一些)的现有技术:

1)标头/校验和 -SOF作为预定义的字节值。帧末的校验和。

  • 优点:简单。
  • 缺点:不可靠。恢复时间未知。

2)字节填充:

  • 优点:可靠,快速恢复,可与任何硬件一起使用
  • 缺点:不适用于固定大小的基于帧的通信

3)第9位标记 -在每个字节之前附加一个位,而SOF标记为1和数据字节标记为0

  • 优点:可靠,快速恢复
  • 缺点:需要硬件支持。大多数PC硬件和软件未直接支持。

4)第8位标记 -上面的一种模拟,同时使用第8位而不是第9位,每个数据字仅保留7位。

  • 优点:可靠,快速的恢复,可与任何硬件一起使用。
  • 缺点:需要从/到常规8位表示到/从7位表示的编码/解码方案。有点浪费。

5)基于超时 -假定SOF为某个已定义的空闲时间之后的第一个字节。

  • 优点:无数据开销,简单。
  • 缺点:不太可靠。在较差的计时系统(如Windows PC)上无法很好地工作。潜在的吞吐量开销。

问题: 还有哪些其他可能的技术/解决方案可以解决该问题?您能否指出上面列出的缺点,可以轻松解决这些缺点,从而消除它们?您(或您将)如何设计系统协议?

serial  communication  protocol  brushless-dc-motor  hall-effect  hdd  scr  flipflop  state-machines  pic  c  uart  gps  arduino  gsm  microcontroller  can  resonance  memory  microprocessor  verilog  modelsim  transistors  relay  voltage-regulator  switch-mode-power-supply  resistance  bluetooth  emc  fcc  microcontroller  atmel  flash  microcontroller  pic  c  stm32  interrupts  freertos  oscilloscope  arduino  esp8266  pcb-assembly  microcontroller  uart  level  arduino  transistors  amplifier  audio  transistors  diodes  spice  ltspice  schmitt-trigger  voltage  digital-logic  microprocessor  clock-speed  overclocking  filter  passive-networks  arduino  mosfet  control  12v  switching  temperature  light  luminous-flux  photometry  circuit-analysis  integrated-circuit  memory  pwm  simulation  behavioral-source  usb  serial  rs232  converter  diy  energia  diodes  7segmentdisplay  keypad  pcb-design  schematics  fuses  fuse-holders  radio  transmitter  power-supply  voltage  multimeter  tools  control  servo  avr  adc  uc3  identification  wire  port  not-gate  dc-motor  microcontroller  c  spi  voltage-regulator  microcontroller  sensor  c  i2c  conversion  microcontroller  low-battery  arduino  resistors  voltage-divider  lipo  pic  microchip  gpio  remappable-pins  peripheral-pin-select  soldering  flux  cleaning  sampling  filter  noise  computers  interference  power-supply  switch-mode-power-supply  efficiency  lm78xx 

图4是只有1/8大于3更浪费
尼克约翰逊

@NickJohnson同意,但这只是建议我在(3)中也添加“ Wasteful” :)
Eugene Sh。

我认为您没有完全解释关于通讯错误的假设。您是否假设通信是“完美”的,即没有错误,或“足够完美”的通信硬件已检测到并识别了所有错误(例如,通信使用奇偶校验,并且它们仅为单比特错误)?
gbulmer

接收者可以在字节的中间加入,例如可以将位8解释为位4。因此,第9位标记不可靠。
蒂莫西·鲍德温

@gbulmer最初的假设是通道是完美的,并且仅由于初始的不同步,才可能出现此问题。在这些假设下,我所指的“可靠性”仅与重新同步有关。在上面的列表中,除了第一个技术以外,所有这些技术均保证100%成功。但是可能错误检查方案和框架不应像这样分开。
尤金(Eugene Sh)。

Answers:


15

您(或您将)如何设计系统协议?

以我的经验,每个人花在调试通信系统上的时间都比他们预期的要多得多。因此,我强烈建议您在需要为通信协议做出选择时,尽可能选择一个可使系统更易于调试的选项。

我鼓励您尝试设计一些自定义协议-这很有趣而且很有教育意义。但是,我也鼓励您查看先前存在的协议。如果我需要将数据从一个地方传递到另一个地方,我会非常努力地使用一些其他人已经花费大量时间进行调试的预先存在的协议。

从头开始编写自己的通信协议很可能会遇到许多人在编写新协议时遇到的许多常见问题。

用于嵌入式到计算机通信的基于RS232的良好协议中列出了十多种嵌入式系统协议-哪一种最接近您的要求?

即使在某些情况下无法完全使用任何预先存在的协议,我也有可能通过从一些几乎可以满足要求的协议入手,然后对其进行调整来更快地工作。

坏消息

正如我之前所说

不幸的是,任何通信协议都不可能具有所有这些出色的功能:

  • 透明性:数据通信是透明的并且“ 8位干净”-(a)可以传输任何可能的数据文件,(b)文件中的字节序列始终作为数据处理,并且永远不会误解为其他内容,并且(c )目的地将毫无错误地接收整个数据文件,没有任何添加或删除。
  • 简单复制:如果我们简单地将数据从源盲目复制到数据包的数据字段而不进行更改,则形成数据包是最容易的。
  • 唯一的开始:数据包的开始符号很容易识别,因为它是一个已知的恒定字节,永远不会在标头,标头CRC,数据有效载荷或数据CRC中的任何其他位置出现。
  • 8位:仅使用8位字节。

如果某种通信协议具有所有这些功能,我将感到惊讶和高兴。

好消息

存在哪些其他可能的技术/解决方案来解决该问题?

如果文本终端的人员可以替换任何通信设备,通常会使调试变得非常容易。这就要求将协议设计为相对时间独立的(在人类键入的击键之间的相对较长的停顿期间不超时)。而且,这样的协议限于人们易于键入然后在屏幕上读取的字节的种类。

某些协议允许消息以“文本”或“二进制”模式发送(并要求所有可能的二进制消息都具有某种意思相同的“等效”文本消息)。这可以使调试更加容易。

有些人似乎认为将协议限制为仅使用可打印字符是“浪费的”,但是节省调试时间通常使它值得。

如您已经提到的,如果允许数据字段包含任意字节,包括头的开始和头的结束字节,那么当第一次打开接收器时,接收器可能会在在一个数据包中间的数据字段中看起来像首标(SOH)字节的字节。通常,接收者会在该伪数据包的末尾(通常是第二个实际数据包的一半)得到不匹配的校验和。在寻找下一个SOH之前,简单地丢弃整个伪消息(包括第二个数据包的前半部分)是非常诱人的-结果,接收者可能对许多消息不保持同步。

正如alex.forencich所指出的那样,一种更好的方法是让接收者从缓冲区的开始直到下一个SOH丢弃字节。这允许接收器(可能在通过该数据包中的几个SOH字节工作之后)立即在第二个包上进行同步。

您能否指出上面列出的缺点,可以轻松解决这些缺点,从而消除它们?

正如Nicholas Clark所指出的,一致开销字节填充(COBS)具有固定的开销,可以与固定大小的帧很好地配合使用。

经常被忽略的一种技术是专用的帧结束标记字节。当接收器在传输过程中打开时,专用的帧结束标记字节有助于接收器更快地同步。

当接收器在数据包中间打开时,并且数据包的数据字段恰好包含看起来像是数据包开始(伪数据包的开始)的字节时,发送器可以插入一系列该数据包之后的帧结束标记字节的数量,因此数据字段中的此类伪数据包起始字节不会干扰立即同步并正确解码下一个字节数据包-即使您非常不幸并且校验和也是如此该伪数据包的正确显示。

祝好运。


此答案值得重新考虑以前接受的答案(对不起,@ DaveTweed),链接的文章无疑是该主题的必读内容。感谢您抽出宝贵的时间来写。
尤金(Eugene Sh)。

1
很高兴您指出COBS,所以我不必写答案了:-)
Nils Pipenbrinck

11

这些年来,字节填充方案对我非常有用。它们很不错,因为它们易于在软件或硬件中实现,您可以使用标准的USB至UART电缆发送数据包,并且可以确保获得高质量的成帧而不必担心超时,热插拔或其他类似方法。

我主张将字节填充方法与长度字节(数据包长度模为256)和数据包级CRC相结合,然后将UART与奇偶校验位一起使用。长度字节确保了丢弃字节的检测,这与奇偶校验位一起很好地工作(因为大多数UART将丢弃任何奇偶校验失败的字节)。然后,数据包级CRC为您提供了额外的安全性。

至于字节填充的开销,您是否看过COBS协议?这是一种天才的方法,可以进行字节填充,每254个传输的固定开销为1个字节(加上成帧,CRC,LEN等)。

https://zh.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing


这是避免字节填充在最坏情况下爆炸为数据的2倍的绝佳方法。我使用了类似但更特定于应用程序的方案,但是很高兴看到以标准方式对此进行了描述。我将从现在开始使用
COBS

1
我也感谢您指出COBS-一种非常简洁的小算法。
尼克·约翰逊

6

您的选项1(SOH加校验和)是可靠的,并且可以在下一个未损坏的帧中恢复。

我假设您已经知道消息的长度,或者长度被编码在SOH之后的字节中。校验字节出现在消息的末尾。您还需要一个接收侧缓冲区,用于存储至少与最长消息一样长的数据。

每当您在缓冲区的开头看到一个SOH字节时,它就有可能是消息的开始。您浏览缓冲区以计算该消息的校验值,然后查看它是否与缓冲区中的校验字节匹配。如果是这样,那么您就完成了;否则,将丢弃缓冲区中的数据,直到到达下一个SOH字节为止。

请注意,如果一条消息实际上存在数据错误,则此算法将丢弃该消息-但您可能还是要这样做。如果您的检查算法包括前向纠错,则可以检查每个潜在的邮件对齐方式是否有可纠正的错误。

如果消息是固定长度的,则可以完全省去SOH字节-只需测试每个可能的起始位置的有效校验值即可。

您也可以省去校验算法,仅保留SOH字节,但这会使算法的确定性降低。这个想法是为了进行有效的消息对齐,SOH将始终出现在消息的开头。如果对齐不正确,则数据流中的下一个字节不太可能是另一个SOH(取决于SOH在消息数据中出现的频率)。您可以仅在此基础上选择有效的SOH字节。(基本上,这就是T1和E1等同步电信服务上的帧工作方式。)


我想可靠性有些概率?根据错误检查/纠正代码的强度,我们可能会遇到在随机/任意字节流中看似正确的帧。
尤金(Eugene Sh)。

当然可以。但是实际上,选择足够强大的检查算法相对容易。
戴夫·特威德

如果您的数据错误率非零,那么有一个非零的机会来接收一条无效的消息。
尼克·约翰逊

@NickJohnson假设频道完全干净,则从理论上讲,这种方法仍然不匹配。当然,它们的概率可以忽略不计。
尤金(Eugene Sh)。

1
我知道您已经知道这一点,并且已经顺便提到了它,但是您不缓存整个消息或者只是对解码方式不满意的版本的可靠性较低。如果您在不匹配的校验和之后的下一个SOH字节处重新同步,而不是在“假” SOH之后的下一个SOH字节处进行重新同步,则您很有可能会丢弃真实消息的开始并对于许多消息保持不同步,或者在最坏的情况,直到永远。
hobbs

5

未提及但广泛使用的一种选择(尤其是在互联网上)是ASCII /文本编码(实际上,大多数现代实现都采用UTF-8)。以我的经验,硬件人员不喜欢这样做,但是软件人员往往更喜欢这样做(几乎所有东西都是基于Unix的传统,它使所有事物基于文本)。

文本编码的优点是您可以使用不可打印的字符进行取景。例如,最简单的方法是使用诸如0x00指示帧开始和0xff结束之类的方法。

我已经看到了将数据编码为文本的两种主要方式:

  1. 当要求硬件/组装人员执行此操作时,则很有可能将其实现为十六进制编码。这只是将字节转换为ASCII的十六进制值。开销很大。基本上,您将为每个实际数据字节传输两个字节。

  2. 当要求软件人员执行此操作时,则可能会将其实现为base64编码。这是互联网的实际编码。用于从电子邮件MIME附件到URL数据编码的所有内容。开销恰好是33%。比简单的十六进制编码好得多。

或者,您可以完全放弃二进制数据并传输文本。在这种情况下,最常见的技术是用换行符分隔数据(正则"\n""\r\n")。NMEA(GPS),调制解调器AT命令和Adventech ADAM传感器是最常见的示例。

所有这些基于文本的协议/框架都具有以下优点和缺点:

优点:

  • 易于调试
  • 易于以脚本语言实现
  • 使用Hyperterminal / minicom可以简单地测试硬件
  • 易于在硬件上实现(除非它是像PIC这样的很小的微型单片机)
  • 可以是固定尺寸的框架,也可以是不同尺寸的框架。
  • 可预测的成帧和快速的同步恢复时间(在当前帧结束时恢复)

缺点:

  • 与纯二进制传输相比,开销非常大(然后,文本I / O也可以“压缩”数字,例如发送一个字节"0"(0x30)而不是发送四个字节0​​x00000000)
  • 在像PIC这样的非常小的micros上实现不是那么干净(除非您的库包含一个sprintf()函数)

就我个人而言,利弊大于弊。单单调试的简便性就算为5分(因此单单已经超过了两个缺点)。


然后,通常不是从软件人员那里就经过仔细考虑的解决方案:发送编码数据而不考虑成帧。

我不得不与过去发送原始XML的硬件进行交互。XML就是框架了。幸运的是,通过<xml></xml>标签找出框架边界相当容易。对我来说,最大的缺点是它使用了一个以上的字节进行成帧。另外,由于标签可能包含属性,因此帧本身可能不会固定:<tag foo="bar"></tag>因此,您必须缓冲最坏的情况才能找到帧的开始。

最近,我看到人们开始从串行端口发送JSON。JSON框架充其量只是一个猜测。您只有"{"(或"[")字符来检测帧,但它们也包含在数据中。因此,您最终需要递归下降解析器(或至少是大括号计数器)来确定框架。至少知道当前帧是否过早结束是微不足道的:"}{"或者"]["在JSON中是非法的,从而表明旧帧已经结束而新帧已经开始。


对于文本编码,还有base85,它只有25%的开销,而不是33%的开销。
戴夫·特威德

我认为这是第四种方法的一个子集。
尤金(Eugene Sh)。

@EugeneSh .:从技术上讲,它是bytestuffing的子集。再一次,由于您将其视为位标记的子集,因此您可以理解为什么这种歧义本身使它成为类别。另外,您不能将文本编码的大多数实现方式视为位标记的子集,因为从未使用过标记位(例如,我通常使用<>作为定界符,并且我相信电子邮件使用换行符。注意:是的,电子邮件格式正确可以通过RS232传输。我的一个朋友曾经使用RS232为他的房子运行邮件分发服务器)
slebetman

4

您所描述的“第X位标记”可以概括为其他代码,这些代码具有以恒定分数扩展数据的特性,而有些代码字可自由用作定界符。这些代码通常还提供其他好处;CD使用8到14调制,这保证了每1 CD 之间的最大运行长度为0位。其他示例包括前向纠错块代码,它也使用其他位来编码错误检测和纠正信息。

您尚未提到的另一个系统是使用带外信息(例如芯片选择线)来界定交易或数据包。


纠错码离问题还很远。无论如何,应该将它们添加到任何这些方案中。我指的是“带外信息”与“硬件流控制”相同吗?
尤金(Eugene Sh)。

@EugeneSh。-实际上,使用错误检查位进行成帧是完全有效的,尽管在接收端的计算量很大。您只需为每种可能的数据对齐方式进行错误计算,成功的是在未损坏的帧上进行有效对齐。当然,如果帧损坏,你不会找到它。
戴夫·特威德

@DaveTweed好吧,这几乎就是我所说的第一种技术。还是我误会你了?
尤金(Eugene Sh)。

不,你不是误会;那就是我在说的。但是,您的“骗局”是错误的-它是可靠的,并且对于实际的传输错误也可以使其健壮。
戴夫·特威德

@DaveTweed恢复时间如何?您有任何示例说明如何使其健壮吗?
尤金(Eugene Sh)。

3

另一种选择是所谓的行编码。线路编码使信号具有一定的电气特性,使其更易于传输(DC平衡和最大运行长度保证),并且它们支持用于帧和时钟同步的控制字符。线路代码用于所有现代高速串行协议-10M,100M,1G和10G以太网,串行ATA,FireWire,USB 3,PCIe等。常见线路代码为8b / 10b64b / 66b128b / 130b。还有一些更简单的线路代码,它们不提供帧信息,仅提供DC平衡和时钟同步。这些示例是Machester和NRZ。如果您想快速同步,则可能要使用8b / 10b。其他行代码并非设计用于紧急同步。使用上面提供的类似这样的线路代码将需要使用自定义硬件进行发送和接收。

至于选项5,标准RS232串行应该支持发送中断和接收中断,其中线路空闲几个字节时间。但是,并非所有系统都支持此功能。

通常,最简单,最可靠的成帧方法是选项1,结合长度字段和简单的CRC或校验和例程。解码例程很简单:丢弃字节,直到获得开始字节,读取长度字段,等待整个帧,检查校验和,保持良好。如果校验和不正确,请开始从缓冲区中丢弃字节,直到获得起始字节并重复执行。这种技术的主要问题是找到一个帧字节的开始,而实际上它不是帧字节的开始。为了缓解此问题,一种技术是使用另一个控制字符对与帧字节开头具有相同值的字节进行转义,然后更改转义的字节以使其具有不同的值。在这种情况下,您还必须对新的控制字节执行相同的操作。


这与尼克·约翰逊的答案相同。
戴夫·特威德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.