https://www.zhihu.com/question/532997683/answer/2487632817
类似于知乎这个问题,里面提到了“如果发送端连续发送数据,没有空闲期,两位高电平的停止位后立刻传输下一次的起始位。那么接收端怎么区分启动位,停止位,与数据位?”
恕我愚昧,这个图就是按照之间没有空闲位来画的图(上图 4 和 5 之间没有空闲位),我看了半天,觉得没有空闲位,接收端也能正确识别每一帧啊?为啥大家说不可以呢。
1
mantouboji 350 天前
不同的 uart 实现不一定能正确识别,取决于内部状态机的设计。没有必要在这种未定义行为上钻牛角尖。
|
2
swulling 350 天前 via iPhone
很久之前做过硬件,记得串口协议里没必须必须要插入所谓的“空闲位”,协议里规定的是 1 个或多个停止位。
一般就是一个。 |
3
swulling 350 天前 via iPhone
如果要额外插入所谓的空闲位,只是把停止位改成了 2 个罢了,损失一点点带宽。
|
4
swulling 350 天前 via iPhone
看了下知乎的文章,可能是你理解有偏差。他没说要加什么空闲位,而是指出如果你仅用一个停止位可能会出现波特率的累积误差或者如果接收重启后就协议识别错误的风险。把停止加到 1.5 或者 2 个会更可靠一些。
实际上 UART 上层应用还会实现一层协议,所以一般是由上层的协议来解决这个问题,校验,ping-pong ,触发重连什么的,那么就没必要在下层做这个事情。 |
5
allmightbe OP @swulling
如果发送端连续发送数据,没有空闲期,两位高电平的停止位后立刻传输下一次的起始位。那么接收端怎么区分启动位,停止位,与数据位? 答:连续数据没法区分帧结构。一步错 步步错。(原话是这么说的,连续数据没法区分帧结构。我以为这就是再说必须要有空闲位😂,原来是我理解错了吗) |
6
nuk 350 天前
那个问题是问的从比特流中间读怎么区分,因为 uart 的启动位和停止位和数据位无法区分,而像 ppp 之类的流协议,就会在数据中转义起始位,这样就算从半截帧开始读,也可以正常识别下一帧
|
7
bao3 350 天前
我能想到的 2 点考虑:
1. 要明确区分两帧,低电平和高电平会让电路信号做到严格区分。 2. 线路有干扰时,低电平的间隔能有充足的时域,保证帧是区分开的。 不光是 URT ,以太网电路信号也是这样的。 |
8
huang321hp 350 天前
楼主指出两个数据帧之间的空闲位也可以称为数据帧的间隔,这是一个非常细节的问题,我们可以从几个角度来分析。
1. 物理层,是否必须保留间隔。(如楼主解释,不需要) 补充一点:我们根据信号的启动位(空闲状态出现的第一个低脉冲)来决定开始接收一个新的数据帧,由于参数确定,所以在接收到一定数量(时间)的 bit 后,该数据帧就结束了。然后等待下一个启动位。 2. 从发送方角度,是否能完全移除间隔。 很难保证完全没有间隔,首先不同的芯片硬件使用的 UART 外设可能存在差异,导致会有不同间隔(特别是高速波特率,例如 12Mbps ,1bit 的时间很小)。 其次,在软件控制层面,需要给到较高的优先级将数据写入到 TX FIFO 或者利用 DMA 。 3. 从接收方角度,是否必须保留间隔。 取决于接收方芯片性能和接收方式(轮询、DMA 、IT ),大多情况下是在数据包(比如每 10bytes )之间做间隔。 原因和发送方类似,数据到达接收方 RX FIFO 后需要给到足够优先级去及时读取出来,不然就满了。 从经验上来说,有间隔相对更安全(容错性更强、减小了接收方的压力),但是传输会更慢。 UART 作为一个最常用的嵌入式接口,在实际应用中有很多细枝末节的问题,除了理论上要说得通,还需要接仪器实际分析和解决。许多时候,表面上能跑不代表没问题。 |
9
cccer 350 天前 2
1. 串口依赖系统时钟,但设备的时钟本身就有存在误差,或者波特率和设备时钟频率不能整除导致误差,所以需要一个停止位来同步时钟。
2. 停止位还能校验数据合法性,如果没有停止位,总线被卡到了低电平时,设备会无法检测出异常,只会认为一直在接收 0x00 。 |
10
geniussoft 350 天前 via iPhone
误差累计不考虑?
这怎么能不考虑…… |
11
iseki 350 天前
几个原因,一个是累计误差,另一个是启动时的同步问题。
累计误差这个,现在很多芯片支持过采样,可以一定程度缓解。 |
12
tek 350 天前
那我有另外一个问题,如果我有一秒钟的时间无数据可传,那么在此期间我该怎么做,持续发 0 还是持续发 1 ,双方之间终究是要规定一下怎么做的
|
13
TESTFLIGHT2021 350 天前
可加可以不加 有停止位就足够了
|
14
yolee599 350 天前 via Android
没必要插入空闲时间,还浪费带宽,至于知乎说的累积误差,下一个起始位到达就能清除了
|
16
duke000 349 天前 1
做为 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 代码实现。 |
17
ghostxdy 349 天前
原来 V2EX 也有那么多熟悉硬件的.
|
18
cccer 349 天前
@yolee599 同步信号是高电平(停止位或者空闲)到低电平(起始位)跳变中断实现的。
当上一个字节的最后比特是低电平时,这时候硬件根本无法区分开始位的起止时间,也就无法实现同步时钟。 |