在我的UART通信中,我需要知道所发送消息的起始字节和终止字节。起始字节很容易,但终止字节不是很多。我在消息末尾实现了两个停止字节,即\ n和\ r(十进制的10和13)。UART仅适用于字节0-255的值,那么这有多安全?我可以想象,尽管可能性很小,但当它们不是停止字节时,我的消息可能会依次包含值“ 10和13”。
有没有更好的方法来实现这一目标?
"\x0D\x0A"
。
\r\n\r\n
的\n\r
序列...
在我的UART通信中,我需要知道所发送消息的起始字节和终止字节。起始字节很容易,但终止字节不是很多。我在消息末尾实现了两个停止字节,即\ n和\ r(十进制的10和13)。UART仅适用于字节0-255的值,那么这有多安全?我可以想象,尽管可能性很小,但当它们不是停止字节时,我的消息可能会依次包含值“ 10和13”。
有没有更好的方法来实现这一目标?
"\x0D\x0A"
。
\r\n\r\n
的\n\r
序列...
Answers:
有多种方法可以防止这种情况:
20 21 22 23 24 25 10 13
20 21 1b 10 1b 13 25 26
<nr_of_data_bytes> <数据>
如果您的消息最大为256字节,请发送:
06 20 21 22 23 24 25
因此,您知道在收到6个数据字节后即结束;您不必再发送10 13。您可以在邮件中发送10 13。如果您的消息可以更长,则可以使用2个字节作为数据大小。
更新1:定义数据包的另一种方法
另一种选择是发送具有特定长度并且可能有很多差异的命令,例如
10 20 30(命令10始终具有2个数据字节)
11 30 40 50(命令11始终具有3个数据字节)
12 06 10 11 12 13 14 15(命令12 + 1字节表示后面的数据字节数)
13 01 02 01 02 03 ...(命令13 + 2个字节(01 02表示256 + 2 = 258个数据字节)
14 80 90 10 13(命令14后跟一个以10 13结尾的ASCII字符串)
更新2:错误的连接/字节丢失
以上所有方法仅在UART线正确发送字节时有效。如果您想使用更可靠的发送方式,也有很多可能性。以下是一些:
请注意,上述所有机制可以是您想要的(也可以是简单的)。在重新发送消息的情况下,还需要用于标识消息的机制(例如,将序列号添加到包中)。
\ n \ r作为停止字节的故障安全性如何?
如果发送,则发送任意数据->可能不够安全。
常见的解决方案是使用转义:
让我们定义字符0x02(STX-帧开始)和0x03(ETX-帧结束)在传输的数据流中必须是唯一的。这样,可以安全地检测到消息的开始和结束。
如果应在消息帧中发送这些字符之一,则将其替换为前缀转义字符(ESC = 0x1b),并在原始字符上添加0x20。
原始字符替换为
0x02 -> 0x1b 0x22
0x03 -> 0x1b 0x23
0x1b -> 0x1b 0x3b
接收者撤消此过程:每当他接收到转义字符时,都会删除该字符,并用0x20减去下一个字符。
这仅增加了一些处理开销,但100%可靠(假设不发生传输错误,您可以/应该通过另外实现校验和机制来进行验证)。
'\x10'
DLE(数据链接转义)。某些Wikipedia页面建议DLE通常以相反的方式使用:说下一个字节是控制字符而不是数据字节。以我的经验,逃脱通常是相反的意思。
您知道,ASCII已经具有用于这些功能的字节。
它还在有效载荷内部具有用于各种用途的代码。
您的协议应指定ACK(0x06)和NAK(0x15)的最佳粒度,以便可以重新发送否定的确认数据。降低到这种最好的粒度,明智的做法是在任何(未转义的)开始指示符之后立即添加一个长度字段,并且(如其他答案所述),在任何(未转义的)停止指示符后面加上CRC是明智的。
UART就其本质而言并不是故障保护-我们在这里谈论的是1960年代技术。
问题的根源在于UART每10位仅同步一次,从而在这些同步周期之间传递了很多胡言乱语。不同于例如CAN对每个单个位多次采样的CAN。
数据内部发生的任何双位错误都将破坏UART帧并无法通过。开始/停止位中的位错误可能会或可能不会以溢出错误的形式被检测到。
因此,无论使用原始数据还是数据包,EMI引起的位翻转始终有可能导致意外的数据。
存在许多“传统UART quackery”方法可以稍微改善这种情况。您可以添加同步字节,同步位,奇偶校验,双停止位。您可以添加对所有字节的总和进行计数的校验和(然后将其求反,因为为什么不这样),也可以将二进制数作为校验和进行计数。所有这些都被广泛使用,极其不科学并且极有可能丢失错误。但是,这就是人们从1960年代到1990年代所做的事情,并且在今天,诸如此类的生活中发生了许多奇怪的事情。
处理通过UART进行安全传输的最专业方法是在数据包末尾具有16位CRC校验和。其他所有内容都不是很安全,并且很可能会丢失错误。
然后,在硬件级别上,您可以使用差分RS-422 / RS-485来大大提高传输的坚固性。这是长距离安全传输的必要条件。TTL级别UART仅应用于板载通信。RS-232不得用于任何其他目的,而应与旧版本向后兼容。
总体而言,错误检测机制越接近硬件,效果越好。就有效性而言,差分信号最多,其次是检查帧/溢出等错误。CRC16添加了一些,然后“传统UART quackery”添加了一点。
...我可以想象,尽管可能性很小,但当它们不是停止字节时,我的消息可能会依次包含值“ 10和13”。
在设计串行数据包的格式时,应考虑数据的一部分等于终止序列的情况。要考虑的另一件事是,在传输过程中任何字符都可能损坏或丢失。起始字符,终止字符,数据有效载荷字节,校验和或CRC字节,前向纠错字节不免遭破坏。帧机制必须能够检测数据包何时具有损坏的数据。
有几种方法可以解决所有这些问题。
我做出的工作假设是,数据包仅以串行字节为帧。握手线不用于框架。时间延迟不用于成帧。
在开头发送数据包的长度,而不是在结尾处发送终止字符(或除此以外)。
优点: 有效载荷以有效的二进制格式发送。
缺点: 在传输开始时需要知道数据包的长度。
发送有效载荷数据时转义特殊字符。前面的答案中已经对此进行了解释。
优点: 发送方在传输开始时不需要知道数据包的长度。
缺点: 效率略低,具体取决于需要释放多少有效负载字节。
数据包的有效载荷经过编码,因此不能包含开始或结束字符。通常,这是通过发送数字以ASCII或十六进制ASCII表示来完成的。
优点: 普通终端程序易于阅读。无需代码即可处理转义。传输开始时无需知道数据包的长度
缺点: 效率较低。对于有效载荷数据的一个字节,将发送几个字节。