我正在开发一个串行端口应用程序,其中一个非常简单的部分给我带来了问题。我只是想从端口读取恒定的数据流,并将其写入二进制文件。问题似乎是速度:我的代码在我的 9600 波特率测试设备上运行良好,但是当转移到115200bps 的实时设备,我似乎正在丢失数据。发生的情况是,经过一段可变的时间后,我丢失了 1 个字节,导致其余数据丢失。我尝试过一些事情:
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
bwLogger.Write((byte)serialPort1.ReadByte());
}
或者
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] inc = new byte[serialPort1.BytesToRead];
serialPort1.Read(inc, 0, inc.Length);
bwLogger.Write(inc);
}
以及一些变化。我无法使用 ReadLine(),因为我正在处理恒定的数据流(对吗?)。我尝试调整缓冲区大小(serialPort1.ReadBufferSize 和硬件 FIFO 缓冲区)。理想情况下,出于可用性目的,我会在软件方面处理此问题,而不是让用户必须更改 Windows 驱动程序设置。
有什么想法吗?
I'm working on a SerialPort app and one very simple part of it is giving me issues. I simply want to read a constant stream of data from the port and write it out to a binary file as it comes in. The problem seems to be speed: my code has worked fine on my 9600 baud test device, but when carried over to the 115200bps live device, I seem to be losing data. What happens is after a variable period of time, I miss 1 byte which throws off the rest of the data. I've tried a few things:
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
bwLogger.Write((byte)serialPort1.ReadByte());
}
or
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] inc = new byte[serialPort1.BytesToRead];
serialPort1.Read(inc, 0, inc.Length);
bwLogger.Write(inc);
}
and a few variations. I can't use ReadLine() as I am working with a constant stream of data (right?). I've tried fiddling with the buffer size (both serialPort1.ReadBufferSize and the hardware FIFO buffer). Ideally, for usability purposes, I'd handle this on the software side and not make the user have to change Windows driver settings.
Any ideas?
发布评论
评论(6)
如果问题似乎是您无法足够快地处理数据,您可以尝试对数据进行双缓冲。
1) 允许一个线程将串口读入一个缓冲区。这可能涉及将数据从端口复制到缓冲区(我对 .NET 不太熟悉)。
2)当您准备好处理传入数据时,(在不同的线程上)让您的程序读入第二个缓冲区,当发生这种情况时,您应该将第一个缓冲区写入磁盘。
3)当第一个缓冲区写入磁盘时,将其交换回串口缓冲区,并将第二个缓冲区写入磁盘。重复该过程,不断交换缓冲区。
If the problem seems to be that you can't process the data fast enough, what you could try would be to double-buffer your data.
1) Allow one thread to read the serial port into one buffer. This may involve copying data off the port into the buffer (i'm not intimately familiar with .NET).
2) When you are ready to handle the incoming data, (on a different thread) make your program read into the 2nd buffer, and while this is happening you should write the first buffer to disk.
3) When the first buffer is written to disk, swap it back to the serial port buffer, and write the 2nd buffer to disk. Repeat process, continually swapping the buffers.
您可以尝试使用 SerialPort 对象的 Handshake 属性来启用握手。
您必须在发送者或接收者上都进行设置。但是:如果您溢出接收器的 UART 缓冲区(非常小,16 字节 IIRC),可能没有其他方法。如果您无法对发件人启用握手,您可能必须保持在 9600 或更低。
You might try enabling handshaking, using the Handshake property of the SerialPort object.
You'll have to set it on both the sender or receiver. however: if you're overflowing the receiver's UART's buffer (very small, 16 bytes IIRC), there's probably no other way. If you can't enable handshaking on the sender, you'll probably have to stay at 9600 or below.
我会尝试以下操作:
我不确定这是否有帮助,但应该在至少要承受框架经常触发事件的压力。
I'd try the following:
I'm not sure if this might help, but it should at least take the pressure of the framework to fire the event that often.
我会检查 Read(Byte>[], Int32, Int32) 方法返回的读取的字节数,并确保它符合您的预期。
确保您正在侦听端口对象上的 SerialErrorReceivedEventHandler ErrorReceived 事件。 RXOver 错误表明您的缓冲区已满。
检查输出缓冲区的线程安全性。如果对输出缓冲区的写入不是线程安全的,则第二次写入可能会损坏第一次写入。
I would check the number of bytes read which is returned by the Read(Byte>[], Int32, Int32) method and make sure it matches what you expect.
Make sure you are listening for SerialErrorReceivedEventHandler ErrorReceived events on the port object. An RXOver error would indicate your buffer is full.
Check the thread safety on your output buffer. If the write to the output buffer is not thread safe, a second write may corrupt the first write.
你的 bwLogger 是 BinaryWriter 类吗?您可以尝试将其与 BufferedStream 一起使用磁盘 I/O 非阻塞。
另外,如果您的数据包具有已知的结束字符,您可以设置 SerialPort.NewLine 属性使您能够使用 ReadLine/WriteLine,尽管我认为这不会产生太大的性能差异。
Is your bwLogger a BinaryWriter class? You might try using it with a BufferedStream to make the disk I/O nonblocking.
Also, if your packets have a known ending character, you can set the SerialPort.NewLine property to enable you to use ReadLine/WriteLine, although I don't think that would make much of a performance difference.
我最近使用的机器都发送停止代码(在我的例子中是 ASCII 代码 3 或 4)。如果您也有此功能,则可以在 SerialPort 对象中使用
ReadTo(string)
。The machines I've been working with recently all send a stop code (in my case ASCII code 3 or 4). If you also have this feature, you can make use of
ReadTo(string)
off your SerialPort object.