- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在实现一个 C# 应用程序,该应用程序使用 USB 串行适配器 (FTDI FT232H) 以高波特率 (8 MegaBaud) 从微 Controller 读取二进制数据。问题是当流包含 0x1A 时,有时会在该字节后丢失一大块数据(数千字节)。
我在论坛上发现0x1A是一个特殊字符(EOFCHAR),Windows对此做了特殊处理。然而this solution反编译 SerialStream
并将 EOFCHAR
更改为另一个字节值对我没有帮助,我需要使用整个字节范围 (0..255)。
我创建了一个小型测试应用程序来重现我的问题,使用连接到同一台计算机的 2 个串行适配器和连接到 FT232H-RX 的 FT232R-TX 重复发送相同的字节
using System;
using System.IO.Ports;
using System.Collections.Generic;
using System.Text;
namespace SerialPort_0x1A_Loss_Test
{
class Program
{
static void Main(string[] args)
{
const byte BYTE_FILL = 0x1A; // 0x1A is EOFCHAR
const int BAUD_RATE = 3000000; // 3 MegaBaud
const int BUFF_SIZE = 1000000;
SerialPort sp1_FT232R =
new SerialPort("COM3", BAUD_RATE, Parity.None, 8, StopBits.One);
SerialPort sp2_FT232H =
new SerialPort("COM6", BAUD_RATE, Parity.None, 8, StopBits.One);
sp1_FT232R.Encoding = Encoding.GetEncoding(1252);
sp1_FT232R.WriteBufferSize = 20000000;
sp1_FT232R.Open();
sp2_FT232H.Encoding = Encoding.GetEncoding(1252);
sp2_FT232H.ReadBufferSize = 20000000;
sp2_FT232H.ReceivedBytesThreshold = 20000000;
sp2_FT232H.Open();
byte[] bufferTx = new byte[BUFF_SIZE];
for (int i = 0; i < BUFF_SIZE; i++)
{
bufferTx[i] = BYTE_FILL;
}
Console.WriteLine("Sending ...");
sp1_FT232R.Write(bufferTx, 0, BUFF_SIZE);
Console.WriteLine("Sending finished. " +
"Press a key to view status, ESC to exit.");
// Receiving maybe not yet finished,
// query the status with a keypress
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
{
Console.WriteLine("TOTAL RX = " + sp2_FT232H.BytesToRead);
}
// A second test, using .Read() call
// This will be executed after pressing ESC for the previous test
int totalRX_Read_Call = 0;
var listBufferRx = new List<byte>();
int btr; // BytesToRead
while ( (btr = sp2_FT232H.BytesToRead) > 0)
{
var bufferRx = new byte[btr];
totalRX_Read_Call += sp2_FT232H.Read(bufferRx, 0, btr);
listBufferRx.AddRange(bufferRx);
Console.WriteLine("totalRX_Read_Call = " + totalRX_Read_Call +
"; listBufferRx.Count = " + listBufferRx.Count);
}
Console.ReadKey();
sp1_FT232R.Close();
sp2_FT232H.Close();
}
}
}
测试结果(所有测试的 BUFF_SIZE = 1000000):
1. BYTE_FILL = 0x55; BAUD_RATE = 3000000; TOTAL RX = 1000000 ( no loss)
2. BYTE_FILL = 0x1A; BAUD_RATE = 3000000; TOTAL RX = 333529 (66% loss)
3. BYTE_FILL = 0x1A; BAUD_RATE = 2000000; TOTAL RX = 627222 (37% loss)
4. BYTE_FILL = 0x1A; BAUD_RATE = 1000000; TOTAL RX = 1000000 ( no loss)
此外,CPU 的负载(i7-4770k,4 GHz)对于测试 2、3、4 的负载很高(超过 30%),但对于测试 1 的负载很低(3%)。对于测试 1,我尝试了与一个字节的所有其他模式 (0x00..0x19, 0x1B..0xFF) 并没有丢失。
请问有解决办法吗?非常感谢!
最佳答案
首先,我无法解释为什么接收到那个字符会导致字节丢失。虽然您可能认为问题出在 EOF 字符的设置上,但这不太合理。
documentation for the DCB
(device control block) structure表示 EofChar
是“用于表示数据结束的字符的值”,但没有说明这意味着什么。我在其他任何地方都找不到对神秘的 EofChar
的其他引用。此外,同一页还提到了 fBinary
成员:“如果该成员为 TRUE,则启用二进制模式。Windows 不支持非二进制模式传输,因此该成员必须为 TRUE。”
有什么关系?嗯,kb101419说明它过去如何在 16 位 Windows 中工作:
fBinary - If fBinary is set to zero, reception of the EofChar character indicates the end of the input stream. ReadComm() will not return any characters past EofChar. If any characters are received after EofChar, it will be treated as overflowing the receive queue (CE_RXOVER). The reception of EofChar is indicated in the COMSTAT status flag CSTF_EOF. If fBinary is set to one, the EofChar character has no special meaning.
In other words, the EofChar
is only used when fBinary
is zero, yet Windows no longer supports that mode, thus it seems that EofChar
is ignored.
So what is causing 0x1A
to be treated as a special character? The DCB has another member called EvtChar
, defined as The value of the character used to signal an event
. When this character is received on the port, the port's event is signaled and the EV_RXFLAG
bit is set for that port. The SerialData
enum defines Eof = NativeMethods.EV_RXFLAG
, which is why there is some confusion as to what EOF means.
OK, but that doesn't explain why that character causes data loss. I couldn't find any documentation about it, but my guess is that when the EvtChar
is received on the port, the event is signaled and no more data is buffered until that event is cleared. At low data rates the event is cleared before another byte is received, so nobody ever noticed the problem before. At high data rates, potentially thousands of bytes may be received during this period. And if this is actually an aspect of the serial port driver, the behavior may be hard to reproduce on other systems.
Now the problem is how to disable this behavior. The SerialStream
class always sets the EvtChar
to 0x1A
, but that doesn't matter because changing it to a different byte just moves the problem rather than fixing it. I believe the actual problem is caused by calling SetCommMask with the EV_RXFLAG
bit (0x0002
) set. Unfortunately this flag is always set regardless of whether you are listening on the event, meaning that the driver will always need to signal it.
I suspect that you can solve your problem by calling SetCommMask
with that bit cleared. The default for SerialStream
is 0x1fb
(all bits but EV_TXEMPTY
). Since EV_RXFLAG
is 0x002
, you can pass in 0x1F9
to clear it.
The P/Invoke signature for SetCommMask
is:
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
[DllImport("Kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern bool SetCommMask(
SafeFileHandle hFile,
int dwEvtMask
);
要获取 hFile
,您必须使用反射来获取 sp1_FT232R.BaseStream
的 _handle
字段:
var _handle = (SafeFileHandle)sp1_FT232R.BaseStream.GetType()
.GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(sp1_FT232R.BaseStream);
SetCommMask(_handle, 0x1F9);
关于c# - 当流包含 0x1A 时,SerialPort 接收到的数据在高波特率下丢失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22745933/
我是一名优秀的程序员,十分优秀!