C# SerialPort - 混合具有不同波特率的端口时出现问题
我想通过串行接口连接两个设备,但它们的连接不兼容。为了解决这个问题,我将它们连接到我的 PC,并且正在开发一个 C# 程序,该程序将 COM 端口 X 上的流量路由到 COM 端口 Y,反之亦然。
该程序连接到两个 COM 端口。在数据接收事件处理程序中,我读入传入数据并将其写入另一个 COM 端口。为此,我有以下代码:
private void HandleDataReceived(SerialPort inPort, SerialPort outPort)
{
byte[] data = new byte[1];
while (inPort.BytesToRead > 0)
{
// Read the data
data[0] = (byte)inPort.ReadByte();
// Write the data
if (outPort.IsOpen)
{
outPort.Write(data, 0, 1);
}
}
}
只要传出 COM 端口以比传入 COM 端口更高的波特率运行,该代码就可以正常工作。如果传入的 COM 端口比传出的 COM 端口快,我就会开始丢失数据。我必须像这样更正代码:
private void HandleDataReceived(SerialPort inPort, SerialPort outPort)
{
byte[] data = new byte[1];
while (inPort.BytesToRead > 0)
{
// Read the data
data[0] = (byte)inPort.ReadByte();
// Write the data
if (outPort.IsOpen)
{
outPort.Write(data, 0, 1);
while (outPort.BytesToWrite > 0); //<-- Change to fix problem
}
}
}
我不明白为什么我需要该修复。我是 C# 新手(这是我的第一个程序),所以我想知道是否缺少一些东西。 SerialPort 默认为 2048 字节写入缓冲区,而我的命令不到 10 个字节。写缓冲区应该能够缓冲数据,直到可以将其写入较慢的 COM 端口。
总之,我在 COM X 上接收数据并将数据写入 COM Y。COM X 的连接波特率比 COM Y 更快。为什么写入缓冲区中的缓冲不处理这种差异?为什么我似乎需要等待写入缓冲区耗尽以避免丢失数据?
谢谢!
* 更新*
如前所述,此代码很容易遇到大量和/或快速传入数据传输的溢出情况。我应该写更多关于我的数据流的内容。我期待< 10 Hz 时的 10 字节命令(小于 10 字节响应)。此外,我发现第一个命令失败。
因此,虽然我知道这段代码无法扩展并且不是最佳的,但我想知道为什么 2-4K 读/写缓冲区甚至无法处理第一个命令。我想知道写入单个字节数据或事件处理程序中是否存在我不理解的错误。谢谢。
* 更新 *
这是失败的示例:
假设我的命令是四个字节:0x01 0x02 0x3 0x4。 COM X 上的设备发送命令。我可以看到 C# 程序接收四个字节并将它们发送到 COM Y 上的设备。COM Y 上的设备接收两个字节:0x01 0x03。我知道 COM Y 上的设备是可靠的,所以我想知道这两个字节是如何删除的。
顺便问一下,有人可以告诉我是否最好只用评论回复答案,或者我是否应该继续编辑原始问题?哪个更有帮助?
I have two devices that I would like to connect over a serial interface, but they have incompatible connections. To get around this problem, I connected them both to my PC and I'm working on a C# program that will route traffic on COM port X to COM port Y and vice versa.
The program connects to two COM ports. In the data received event handler, I read in incoming data and write it to the other COM port. To do this, I have the following code:
private void HandleDataReceived(SerialPort inPort, SerialPort outPort)
{
byte[] data = new byte[1];
while (inPort.BytesToRead > 0)
{
// Read the data
data[0] = (byte)inPort.ReadByte();
// Write the data
if (outPort.IsOpen)
{
outPort.Write(data, 0, 1);
}
}
}
That code worked fine as long as the outgoing COM port operated at a higher baud rate than the incoming COM port. If the incoming COM port was faster than the outgoing COM port, I started missing data. I had to correct the code like this:
private void HandleDataReceived(SerialPort inPort, SerialPort outPort)
{
byte[] data = new byte[1];
while (inPort.BytesToRead > 0)
{
// Read the data
data[0] = (byte)inPort.ReadByte();
// Write the data
if (outPort.IsOpen)
{
outPort.Write(data, 0, 1);
while (outPort.BytesToWrite > 0); //<-- Change to fix problem
}
}
}
I don't understand why I need that fix. I'm new to C# (this is my first program), so I'm wondering if there is something I am missing. The SerialPort defaults to a 2048 byte write buffer and my commands are less than ten bytes. The write buffer should have the ability to buffer the data until it can be written to a slower COM port.
In summary, I'm receiving data on COM X and writing the data to COM Y. COM X is connected at a faster baud rate than COM Y. Why doesn't the buffering in the write buffer handle this difference? Why does it seem that I need to wait for the write buffer to drain to avoid losing data?
Thanks!
* Update *
As noted, this code can very easily run into an overflow condition with large and/or fast incoming data transfers. I should have written more about my data stream. I'm expecting < 10 byte commands (with < 10 byte responses) at 10 Hz. In addition, I'm seeing failures on the first command.
So while I know this code does not scale and is less than optimal, I'm wondering why the 2-4K read/write buffers couldn't even handle the first command. I'm wondering if there is a bug with writing a single byte of data or something with the event handler that I don't understand. Thanks.
* Update *
Here's an example of the failure:
Let's say my command is four bytes: 0x01 0x02 0x3 0x4. The Device on COM X sends the command. I can see the C# program receiving four bytes and sending them on to the device on COM Y. The device on COM Y receives two bytes: 0x01 0x03. I know the device on COM Y is reliable, so I'm wondering how the two bytes were dropped.
By the way, can someone let me know if it's better to just reply to answers with comments or if I should keep editing the original question? Which is more helpful?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你试图做的事情相当于从消防水带里喝水。您依靠接收缓冲区来存储水,当有人不关闭水龙头时,它不会持续很长时间。通过您的解决方法,您可以确保接收缓冲区将静静地溢出,您可能没有实现 ErrorReceived 事件。
为了做到这一点,您必须告诉输入设备在缓冲区已满时停止发送。通过设置握手属性来做到这一点。首先将其设置为 Handshake.RequestToSend。接下来使用 XOnXOff。是否正确使用握手信号取决于设备。
使用 Read() 方法可以提高效率。
好吧,不是消防水带。我只能想到另一种可能性。早期 UART 芯片设计的一个常见问题是片上接收缓冲区只能存储一个字节。这需要中断服务例程在下一个字节到达之前读取该字节。如果 ISR 不够快,芯片就会进入 SerialError.Overrun 状态,并且字节将无法挽回地丢失。
此问题的解决方法是人为地在每个传输的字节之间添加延迟,从而为设备中的 ISR 有更多时间来读取字节。这就是您的解决方法代码的作用,作为副作用。
这不是一个很好的解释,现代芯片设计有一个至少 8 字节深的 FIFO 缓冲区。如果这确实有道理,那么当您降低波特率时,您应该会看到问题消失。另外,使用 Read() 而不是 ReadByte() 会使问题变得更糟,因为 Write() 调用现在可以一次传输多个字节,从而消除了字符间延迟。需要明确的是,我说的是输出设备。
What you are trying to do is equivalent to drinking from a fire hose. You are relying on the receive buffer to store the water, it isn't going to last long when somebody doesn't turn the tap off. With your workaround, you are making sure that the receive buffer will overflow silently, you probably didn't implement the ErrorReceived event.
To make this work, you'll have to tell the input device to stop sending when the buffer is full. Do that by setting the Handshake property. Set it to Handshake.RequestToSend first. Use XOnXOff next. It depends on the device whether it will use the handshake signals properly.
Use the Read() method to make this a bit more efficient.
Okay, not fire hose. I can think of only one other possibility. A common problem with early UART chip designs, they had a on-chip receive buffer that could store only one byte. Which required the interrupt service routine to read that byte before the next one arrived. If the ISR isn't quick enough, the chip turns on the SerialError.Overrun state and the byte is irretrievably lost.
A workaround for this issue was to artificially put a delay between each transmitted byte, giving the ISR in the device more time to read the byte. Which is what your workaround code does, as a side-effect.
It is not a great explanation, modern chip designs have a FIFO buffer that's at least 8 bytes deep. If there is any truth to this at all, you should see the problem disappear when you lower the baudrate. Also, using Read() instead of ReadByte() should make the problem worse since your Write() call can now transmit more than one byte at a time, eliminating the inter-character delay. To be clear, I'm talking about the output device.
您应该确保
outPort.WriteBufferSize
大于您期望发送的最大缓冲区。此外,在循环中调用ReadByte
和WriteByte
通常会很慢。如果您将处理程序更改为以下内容:这将减少开销,这应该会有所帮助。然后,写入缓冲区将(希望)按照您的预期处理时序。
You should make sure that
outPort.WriteBufferSize
is bigger than the biggest buffer you expect to send. Also, callingReadByte
andWriteByte
in a loop is generally going to be slow. If you change your handler to something like:this will reduce the overhead, which ought to help. The Write buffer will then (hopefully) handle the timing as you expect.