串行端口未正确刷新

Posted

技术标签:

【中文标题】串行端口未正确刷新【英文标题】:Serial Port not being flushed properly 【发布时间】:2017-04-28 13:20:37 【问题描述】:

我有一个 RPi (是的,我知道,也许有人认为这属于 RPi 站点,但我认为它一般与 Linux 有关,所以 *** 是正确的地方) m 使用 Python3 和 pySerial 通过 MAX485 在一些 Arduino 之间进行通信。这或多或少有效,因为我发现我需要做一些奇怪的解决方法才能让一切“正常”工作。

如果我发送数据:

GPIO.output(23, 1) # Pulling transmit pin high to send
comport.write("Some data".encode()) # Writing data
comport.flush() # Flushing the buffer
GPIO.output(23, 0) # Pulling pin down to receive

Arduino 接收到数据并立即做出响应,但由于 pySerial 不知何故还没有准备好,它什么都没有回来,我们有一个丢失的数据包。

但是,如果我这样尝试:

GPIO.output(23, 1)
comport.write("Some data".encode())
time.sleep(.001) # Add some delay of only 1ms
comport.flush()
GPIO.output(23, 0)

然后数据被发送和接收。这让我想到一个问题:flush 命令是否工作正常?我试了一下:

GPIO.output(23, 1)
comport.write("Some data".encode())
time.sleep(.001) # Add some delay
# -- No flush --
GPIO.output(23, 0)

令人惊讶的是它也有效。似乎睡眠“取代”了flush 命令。

为什么 pySerial 的缓冲区没有刷新?我知道,这可能是一种方法,但sleep 只会添加(通常)不必要的代码位,导致整个代码等待(超过 500 行),这不是很棒。

我已经在网上搜索了,有人说是 USB TTL Adapter 不支持flushing(这里不是这种情况,这个是板载的),其他人说可能是 Linux 内核错误,所以对我来说没有任何意义。

如果有人能解释为什么 flush 命令不起作用以及如何让它起作用(如果有办法的话),我和这个问题的每一位未来访客都会很高兴。

【问题讨论】:

运行的波特率是多少?您不清楚这里使用的是什么 GPIO23。 @StephenRauch You are not clear what GPIO23 is being used for here. -> max485 芯片的传输引脚。 【参考方案1】:

您没有具体说明,但我认为假设您在半双工模式下使用 MAX485 是公平的。如果是这种情况,那么您尝试执行的操作将无法可靠地工作。

有什么问题?

半双工通信使用same pair of wires 在任一方向进行传输。但一次只能传输一个端点。因此,这需要某种方式来协调当前正在传输的人。

我相信你对这里的问题有一个很好的总结:

Arduino 接收到数据并立即做出响应,但由于 pySerial 不知何故还没有准备好,它什么都没有回来,我们有一个丢失的数据包。

不过不是PySerial没准备好,是你的MAX485,还有GPIO-23的状态。

一切都在时机上

这是一张来自 Moxa 在 Half Duplex 485 上的出色技术说明的图片,标题为 The Secrets of RS-485 Half-duplex Communication

这张图显示你需要在适当的时候改变GPIO-23(图中的MASTER-RTS)的状态,否则你的通讯会失败。该技术说明值得完整阅读,我相信它很好地描述了您面临的挑战。

我该如何解决?

答案是,视情况而定。您的问题可能是太早或太晚切换 GPIO-23。如果可能,最简单但性能最低的方法是让您的 Arduino 在响应之前等待一段时间。这将允许您sleep() 确保您的数据包已完成传输,然后在 Arduino 开始响应之前切换 GPIO-23 的状态。

如果此解决方案不可行或不可取,那么您将需要研究驱动 GPIO-23 的其他方法。在 SW 中还有许多其他方法可以做到这一点,有些方法会比其他方法更好。但请注意,仅使用 python 代码来切换 GPIO-23 线永远不会 100% 可靠。如果需要高可靠性,几乎肯定需要某种硬件辅助。

最后一点,除非需要 RS-485,否则您可以考虑使用 RS-422 甚至 RS-232 的全双工。

【讨论】:

不幸的是,RS-485 已经内置在我的应用程序中,无法更改为另一个。我会尝试用其他方式切换引脚...【参考方案2】:

对于pyserial,您可以设置 write_timeouttimeout(用于读取)参数,如果不设置为零,写入和读取操作将阻塞。

因此,如果您不设置 write_timeout 参数,则写入操作的默认行为是阻塞,直到您的数据发送到硬件。你不需要刷新数据,这个函数不会做任何事情。

现在这取决于您的配置外观以及您用于通信的内核驱动程序。

如果您使用的是 RPi 的嵌入式 UART 端口,则阻塞的写入操作应等到所有数据真正推出。否则,这可能是错误配置的内核驱动程序或错误。

如果您使用的是 FTDI 芯片之类的 USB 到 UART 转换器,那么在数据传输到芯片后,被阻止的写入操作将返回。这意味着当 write 函数返回时,数据可能没有被这个 ic 完全传输。

如何处理这个问题(您需要一台示波器): 1. 检查 tx 引脚置位和传输开始之间的延迟。 2. 检查置低 tx 引脚和传输结束之间的延迟。

对于小数据包,它可能有助于提高波特率。

此外,您应该考虑使用硬件驱动的 tx 启用电路。 可以将多个 FTDI 芯片配置为具有这样的行为(必须将适当的引脚连接到 tx enable)。

【讨论】:

以上是关于串行端口未正确刷新的主要内容,如果未能解决你的问题,请参考以下文章

如果未使用 CloseHandle 正确关闭,则重新打开串行端口失败

Qt 串行端口错误 - 未读取数据

释放未插入的虚拟串行端口

PySerial 报告看似未使用的串行端口的“资源忙”

如何正确使用 COMMTIMEOUTS 和从串行端口读取的 OVERLAPPED IO 模式

信号未从线程传递到 GUI