我不好意思要问这样一个问题,但我很难搞清楚如何通过.NET SerialPort
类在串口上可靠地读取数据。
我的第一个方法:
static void Main(string[] args)
{
_port = new SerialPort
{
PortName = portName,
BaudRate = 57600,
DataBits = 8,
Parity = Parity.None,
StopBits = StopBits.One,
RtsEnable = true,
DtrEnable = false,
WriteBufferSize = 2048,
ReadBufferSize = 2048,
ReceivedBytesThreshold = 1,
ReadTimeout = 5000,
};
_port.DataReceived += _port_DataReceived;
_port.Open();
// whatever
}
private void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var buf = new byte[_port.BytesToRead];
var bytesRead = _port.Read(buf, 0, buf.Length);
_port.DiscardInBuffer();
for (int i = 0; i < bytesRead; ++i)
{
// read each byte, look for start/end values,
// signal complete packet event if/when end is found
}
}
所以这有一个明显的问题;我正在调用DiscardInBuffer
,因此在事件被触发后进入的任何数据都将被丢弃,即我丢弃数据。
现在,the documentation for SerialPort.Read()甚至没有说明它是否会提升流的当前位置(真的吗?),但我发现其他来源声称它确实存在(这是有道理的)。但是,如果我不调用DiscardInBuffer
,我最终会收到RXOver
错误,即我处理每条消息的时间太长而且缓冲区溢出。
所以......我真的不喜欢这个界面。如果我必须在一个单独的线程上处理每个缓冲区,我会这样做,但这会带来一系列问题,而且我希望我错过了一些东西,因为我没有多少使用此界面的经验。
答案 0 :(得分:8)
Jason提出了一些关于减少来自工作线程的UI访问的好点,但更好的选择是首先不在工作线程上接收数据。
使用port.BaseStream.ReadAsync
在您想要的线程上获取事件驱动的数据。我在http://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport
答案 1 :(得分:4)
要正确处理串口数据,您需要做一些事情。
首先,不要处理接收事件中的数据。将数据复制到其他位置并在另一个线程上执行任何处理。 (对于大多数事件都是如此 - 在事件处理程序中进行任何耗时的处理是一个坏主意,因为它会延迟调用者并可能引入问题。您还需要小心,因为您的事件是在不同的线程上引发的你的主要申请)
其次,您无法保证在收到数据时只收到一个数据包或完整的数据包 - 它可能会以小碎片的形式发送给您。
因此,结果是你应该创建自己的缓冲区(大到足以容纳几个数据包),当你收到数据时,将它附加到你的缓冲区。然后在另一个线程中,您可以处理缓冲区,查看是否可以从中解码数据包然后使用该数据。在找到有效数据包的开头之前,您可能必须跳过部分数据包的末尾。如果您没有足够的数据来构建完整数据包,那么您可能需要稍等一会儿,直到有更多数据到达。
您不应该在端口上调用Discard - 只需读取数据并使用它。每次调用时,都会有另一个要处理的数据片段。它不记得以前调用的数据 - 每次调用事件时,都会给出自上次调用以来已经到达的一小段数据。只需使用您已获得的数据并返回。
作为最后一个建议:除非您特别需要使其正常运行,否则请勿更改端口的任何设置。因此,您必须设置波特率,数据/停止位和奇偶校验,但避免尝试更改Rts / Dtr,缓冲区大小和读取阈值等属性,除非您有充分的理由认为您比串行端口的作者更了解。如今大多数串行设备都以行业标准的方式工作,改变这些低级选项很可能会造成麻烦,除非你正在谈论一些不寻常的设备并且你非常了解硬件。
特别是将ReceivedBytesThreshold设置为1可能是导致您提到的失败的原因,因为您要求串行端口一次只调用一个字节,每秒57,600次调用事件处理程序 - 为您的事件提供处理器只需要0.017毫秒来处理每个字节,然后才开始重新调用。
答案 2 :(得分:0)
DiscardInBuffer通常仅在打开串口后立即使用。它不是标准串行端口通信所必需的,因此您不应该在dataReceived处理程序中使用它。