为什么 uart 两个数据帧之间必须有空闲位?

123 天前
 allmightbe

https://www.zhihu.com/question/532997683/answer/2487632817

类似于知乎这个问题,里面提到了“如果发送端连续发送数据,没有空闲期,两位高电平的停止位后立刻传输下一次的起始位。那么接收端怎么区分启动位,停止位,与数据位?”

恕我愚昧,这个图就是按照之间没有空闲位来画的图(上图 4 和 5 之间没有空闲位),我看了半天,觉得没有空闲位,接收端也能正确识别每一帧啊?为啥大家说不可以呢。

1560 次点击
所在节点    程序员
20 条回复
mantouboji
123 天前
不同的 uart 实现不一定能正确识别,取决于内部状态机的设计。没有必要在这种未定义行为上钻牛角尖。
swulling
123 天前
很久之前做过硬件,记得串口协议里没必须必须要插入所谓的“空闲位”,协议里规定的是 1 个或多个停止位。

一般就是一个。
swulling
123 天前
如果要额外插入所谓的空闲位,只是把停止位改成了 2 个罢了,损失一点点带宽。
swulling
123 天前
看了下知乎的文章,可能是你理解有偏差。他没说要加什么空闲位,而是指出如果你仅用一个停止位可能会出现波特率的累积误差或者如果接收重启后就协议识别错误的风险。把停止加到 1.5 或者 2 个会更可靠一些。

实际上 UART 上层应用还会实现一层协议,所以一般是由上层的协议来解决这个问题,校验,ping-pong ,触发重连什么的,那么就没必要在下层做这个事情。
allmightbe
123 天前
@swulling
如果发送端连续发送数据,没有空闲期,两位高电平的停止位后立刻传输下一次的起始位。那么接收端怎么区分启动位,停止位,与数据位?

答:连续数据没法区分帧结构。一步错 步步错。(原话是这么说的,连续数据没法区分帧结构。我以为这就是再说必须要有空闲位😂,原来是我理解错了吗)
nuk
123 天前
那个问题是问的从比特流中间读怎么区分,因为 uart 的启动位和停止位和数据位无法区分,而像 ppp 之类的流协议,就会在数据中转义起始位,这样就算从半截帧开始读,也可以正常识别下一帧
bao3
123 天前
我能想到的 2 点考虑:
1. 要明确区分两帧,低电平和高电平会让电路信号做到严格区分。
2. 线路有干扰时,低电平的间隔能有充足的时域,保证帧是区分开的。

不光是 URT ,以太网电路信号也是这样的。
huang321hp
123 天前
楼主指出两个数据帧之间的空闲位也可以称为数据帧的间隔,这是一个非常细节的问题,我们可以从几个角度来分析。

1. 物理层,是否必须保留间隔。(如楼主解释,不需要)

补充一点:我们根据信号的启动位(空闲状态出现的第一个低脉冲)来决定开始接收一个新的数据帧,由于参数确定,所以在接收到一定数量(时间)的 bit 后,该数据帧就结束了。然后等待下一个启动位。

2. 从发送方角度,是否能完全移除间隔。

很难保证完全没有间隔,首先不同的芯片硬件使用的 UART 外设可能存在差异,导致会有不同间隔(特别是高速波特率,例如 12Mbps ,1bit 的时间很小)。
其次,在软件控制层面,需要给到较高的优先级将数据写入到 TX FIFO 或者利用 DMA 。

3. 从接收方角度,是否必须保留间隔。

取决于接收方芯片性能和接收方式(轮询、DMA 、IT ),大多情况下是在数据包(比如每 10bytes )之间做间隔。
原因和发送方类似,数据到达接收方 RX FIFO 后需要给到足够优先级去及时读取出来,不然就满了。

从经验上来说,有间隔相对更安全(容错性更强、减小了接收方的压力),但是传输会更慢。

UART 作为一个最常用的嵌入式接口,在实际应用中有很多细枝末节的问题,除了理论上要说得通,还需要接仪器实际分析和解决。许多时候,表面上能跑不代表没问题。
cccer
123 天前
1. 串口依赖系统时钟,但设备的时钟本身就有存在误差,或者波特率和设备时钟频率不能整除导致误差,所以需要一个停止位来同步时钟。
2. 停止位还能校验数据合法性,如果没有停止位,总线被卡到了低电平时,设备会无法检测出异常,只会认为一直在接收 0x00 。
geniussoft
123 天前
误差累计不考虑?

这怎么能不考虑……
iseki
123 天前
几个原因,一个是累计误差,另一个是启动时的同步问题。
累计误差这个,现在很多芯片支持过采样,可以一定程度缓解。
tek
123 天前
那我有另外一个问题,如果我有一秒钟的时间无数据可传,那么在此期间我该怎么做,持续发 0 还是持续发 1 ,双方之间终究是要规定一下怎么做的
TESTFLIGHT2021
123 天前
可加可以不加 有停止位就足够了
yolee599
123 天前
没必要插入空闲时间,还浪费带宽,至于知乎说的累积误差,下一个起始位到达就能清除了
Rorysky
123 天前
@yolee599 只考虑软件层面是不需要的,但是硬件可能出错,需要这个荣誉
duke000
122 天前
做为 UART 接收方,一般是在一个 bit 的中间位置采样数据。对应你引用的图片的时钟波形,在波形上升沿的地方采样,且启动位和停止位也应该有一样的波形才对(图中没有),等停止位的中间采样完成后,接收方的状态机就已经跳转到空闲了,等待下一次接收,所以在 0 等待的情况下,正常不会有长时间发送产生累积误差的问题。因为停止位的后一半时间就是可以用来消除累积误差的。(不能排除所有芯片都设计没有问题,这里能出问题的概率非常小。)

至于分隔数据包,一般有两种做法:

一是使用 1 、2 个字节的空闲时间来分隔,譬如 MODBUS 、CDBUS 等协议

另一种是用特殊的帧头来分隔:譬如 PPP 、以及很多私有协议,譬如 FF AA 开头之类的

其实还可以使用串口特殊字符 break 字符来分隔,只是没见有人这样用,break 字符是连续至少 1 个字节时间的低电平(相当于正常数据 0x00 的停止位也强制为 0 )

使用特殊帧头又会涉及到数据和帧头重复的问题,PPP 协议要求数据要转译,MCU 处理很麻烦、低效。如果要避免你说的没有空闲字节导致接收错位的问题,一般软件层面可以有一些流控机制,譬如发送一堆数据后要有回复,没收到回复等超时重新传输。而这个超时就是一种可以隔开数据包的空闲时间。

我个人是倾向直接使用空闲来分隔数据包,实时性高很多,通用性也就更好。即便是实时性不高的设备,譬如电脑接收 MODBUS 、CDBUS 等协议,数据包粘到一起,也没有关系,按照帧的定义一样可以正确解析所有数据包。(万一出问题,可能是数据包错位,可以逐字节遍历尝试重新解包,直到能解出正确的包。但我建议直接清空接收缓存,等待接收新的数据包。)

除了普通按字节为单位收发的 UART 硬件,也有能按数据包为单位来收发的串口芯片,譬如 CDCTL01A ,可以让 RS485 和 CAN 一样支持仲裁,支持多主对等通讯,可以把半双工的 RS485 当作全双工来用,速率可以达到 50Mbps, 芯片本身是开源的,你可以参考一下其 verilog 代码实现。
ghostxdy
122 天前
原来 V2EX 也有那么多熟悉硬件的.
cccer
122 天前
@yolee599 同步信号是高电平(停止位或者空闲)到低电平(起始位)跳变中断实现的。
当上一个字节的最后比特是低电平时,这时候硬件根本无法区分开始位的起止时间,也就无法实现同步时钟。
yolee599
121 天前
@cccer #18 错误的,最后还有停止位,停止位必是高电平。由于没有曼切斯特编码,启动位开始后必然是通过波特率来计算中间每个位的采样时间,停止位是可检测的
cccer
121 天前
@yolee599 那可能我理解错了,以位你说的空闲时间就是”停止位“

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1006650

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX