如何强制串行端口写入方法在发送数据之前等待线路清除?

Posted

技术标签:

【中文标题】如何强制串行端口写入方法在发送数据之前等待线路清除?【英文标题】:How do I force a serial port write method to wait for the line to clear before sending its data? 【发布时间】:2010-09-18 08:35:28 【问题描述】:

以下是我正在尝试做的一些背景:

    打开从移动设备到蓝牙打印机的串行端口。 向蓝牙打印机发送 EPL/2 表单,以便它了解如何处理即将接收的数据。 收到表格后,将一些数据发送到打印机,这些数据将打印在标签纸上。 根据需要多次重复步骤 3,以打印每个要打印的标签。

第 2 步只发生在第一次,因为表单不需要在每个标签之前。我的问题是,当我发送表单时,如果我发送标签数据太快,它将无法打印。有时我会在标签上打印“蓝牙故障:无线电无法运行”,而不是我发送的数据。

我通过执行以下操作找到了解决此问题的方法:

for (int attempt = 0; attempt < 3; attempt++)

    try
    
        serialPort.Write(labelData);
        break;
    
    catch (TimeoutException ex)
    
        // Log info or display info based on ex.Message
        Thread.Sleep(3000);
    

所以基本上,我可以捕获一个 TimeoutException 并在等待一定时间后重试 write 方法(三秒似乎一直有效,但更少,似乎每次尝试都会抛出异常)。经过三次尝试,我只是假设串口有问题并让用户知道。

这种方式似乎工作正常,但我确信有更好的方法来处理这个问题。我认为我需要使用 SerialPort 类中的一些属性,但我真的找不到任何好的文档或如何使用它们的示例。我尝试过使用一些属性,但它们似乎都没有达到我想要达到的效果。

这是我玩过的属性列表:

CDHolding CtsHolding DsrHolding DtrEnable 握手 RtsEnable

我确信这些的某种组合可以更优雅地处理我正在尝试做的事情。

我正在使用 C#(2.0 框架)、Zebra QL 220+ 蓝牙打印机和 windows Mobile 6 手持设备,如果这对解决方案有任何影响的话。

任何建议将不胜感激。

[更新]

我还应注意,移动设备使用的是蓝牙 2.0,而打印机仅使用 1.1 版。我假设速度差异是导致打印机接收数据滞后的原因。

【问题讨论】:

Jason,你联系过 Zebra,他们说了什么?我很想知道这个故事的结局... 很久以前...我只记得他们推荐了软件流控制,我们已经确定这是最好的方法。我不记得他们提到限制传输速度或类似的任何事情。 【参考方案1】:

嗯,我已经根据已经给出的两个建议找到了一种方法。我需要使用以下内容设置我的串行端口对象:

serialPort.Handshake = Handshake.RequestToSendXOnXOff;
serialPort.WriteTimeout = 10000; // Could use a lower value here.

然后我只需要进行写调用:

serialPort.Write(labelData);

由于 Zebra 打印机支持软件流控制,它会在缓冲区快满时向移动设备发送 XOff 值。这会导致移动设备等待打印机发送 XOn 值,从而有效地通知移动设备它可以继续传输。

通过设置写入超时属性,我给出了在抛出写入超时异常之前允许传输的总时间。您仍然希望捕获写入超时,就像我在问题的示例代码中所做的那样。但是,没有必要循环 3(或任意数量)次,每次都尝试写入,因为软件流控制会启动和停止串行端口写入传输。

【讨论】:

【参考方案2】:

流控制在这里是正确的答案,它可能不存在/实现/适用于您的蓝牙连接。

查看 Zebra 规范,看看它们是否实现了,或者您是否可以打开软件流控制(xon、xoff),这样您就可以看到各种缓冲区何时已满。

此外,蓝牙无线电的最大传输速度不太可能超过 250k。您可能会考虑人为地将其限制为 9,600bps - 这将为无线电提供很大的喘息空间来进行重传、纠错、检测和自身的流量控制。

如果所有其他方法都失败了,那么您现在使用的 hack 还不错,但我会打电话给 Zebra 技术支持,并在放弃之前了解他们的建议。

-亚当

【讨论】:

【参考方案3】:

问题可能不在于串行端口代码,而在于底层蓝牙堆栈。您使用的端口是纯虚拟的,甚至不太可能实现任何握手(因为它在很大程度上没有意义)。 CTS/RTS DTR/DSR 根本不适用于您的工作。

潜在的问题是,当您创建虚拟端口时,它必须绑定到蓝牙堆栈并连接到配对的串行设备。端口本身不知道这可能需要多长时间,并且它可能已设置为异步执行此操作(尽管这将完全取决于设备 OEM 如何完成)以防止调用者长时间锁定(如果没有)配对设备或配对设备超出范围。

虽然您的代码可能感觉像是 hack,但它可能是执行您正在做的事情的最佳、最便携的方式。

您可以使用蓝牙堆栈 API 来尝试在连接之前查看设备是否存在并且处于活动状态,但堆栈 API 没有标准化,因此 Widcom 和 Microsoft API 在您执行此操作的方式上有所不同,并且 Widcom是专有且昂贵的。你最终会得到的是试图发现堆栈类型,动态加载适当的验证器类,让它调用堆栈并寻找设备的混乱。鉴于此,您的简单民意调查似乎更加简洁,而且您不必为 Widcom SDK 支付几美元。

【讨论】:

好答案。我会把它归档在“什么时候黑客不是真正的黑客”下。 这可以解释为什么使用任何清除发送、请求发送选项似乎都没有效果。 做一段时间的移动开发,你会倾向于收集几个“什么时候是黑客而不是黑客”的比特。事实上,您可能会为它们创建一个小型库。这是使桌面开发和设备开发相同的信念几乎无效的原因之一 ctacke - 我注意到尝试将代码从桌面应用程序移植到移动应用程序时出现很多问题。 Compact Framework 似乎从来没有对象所需的一个方法或属性,而它在常规 .NET 框架中可用。 Grrrr。

以上是关于如何强制串行端口写入方法在发送数据之前等待线路清除?的主要内容,如果未能解决你的问题,请参考以下文章

时序问题:QT 写入串行端口,然后读取

我如何知道要发送到串行端口的内容? [复制]

如何确定 linux 串行端口上剩余的写入/输出缓冲区空间量?

STM32SPI通信仿真没问题,下载后不能接收

如何通过usb端口通信从arduino将串行数据写入phpmyadmin

使用python在串行通信中读取输入缓冲区