如何在 C# 中向串行设备发送字节?
我有一个使用串行(通过 USB 适配器)与我的 PC 连接的设备。我很难让它在 C# 中很好地发挥作用。我知道它可以正常工作,因为供应商提供的软件的行为符合预期。我还知道,由于测试模式反复发送“OK”,我能够使用我的代码接收数据。
这是我的代码:
private SerialPort port;
public SerialConnection()
{
this.port = new SerialPort("COM3", 38400, Parity.None, 8, StopBits.One);
this.port.WriteTimeout = 2000; port.ReadTimeout = 2000;
this.port.Open();
this.port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
public void SendCommand(byte[] command)
{
this.port.Write(command,0,command.Length);
string chars = "";
foreach (byte charbyte in command) chars += (char)charbyte;
Console.WriteLine(" -> " + chars);
}
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = this.port.ReadLine();
Console.WriteLine(" <- " + data);
}
目前我正在使用另一种测试模式,该模式应该回显它收到的任何内容。因此,我使用以下字节调用 SendCommand:
byte[] {
0x50,
0x69,
0x6E,
0x67
}
但似乎没有任何内容被发送回。
我不知道下一步该尝试什么。有人有什么建议吗?
对于一个附属问题,我发布了一些 PortMon 日志。我认为它们在这里也可能有用,所以它们在这里:
I have a device that uses serial (via a USB adaptor) to interface with my PC. I'm having real difficulty getting it to play nicely in C#. I know that it works properly because the vendor-supplied software behaves as expected. I also know that I am able to receive data using my code thanks to a test mode which repeatedly sends "OK".
Here's my code:
private SerialPort port;
public SerialConnection()
{
this.port = new SerialPort("COM3", 38400, Parity.None, 8, StopBits.One);
this.port.WriteTimeout = 2000; port.ReadTimeout = 2000;
this.port.Open();
this.port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
public void SendCommand(byte[] command)
{
this.port.Write(command,0,command.Length);
string chars = "";
foreach (byte charbyte in command) chars += (char)charbyte;
Console.WriteLine(" -> " + chars);
}
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = this.port.ReadLine();
Console.WriteLine(" <- " + data);
}
At the moment I'm using another test mode which is supposed to echo back whatever it receives. Accordingly, I'm calling SendCommand with the following bytes:
byte[] {
0x50,
0x69,
0x6E,
0x67
}
But nothing ever seems to get sent back.
I've no idea what to try next. Anyone got any suggestions?
For a subsidiary question I've posted some PortMon logs. I think they might be useful here too, so here they are:
- Vendors software - with all the
IOCTL_SERIAL_GET_COMMSTATUS
entries filtered out - My toe-dip attempt
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
每个 RS-232 设备都需要使用某种流量控制机制来通知对方正在进行的操作沟通。共有三种基本机制:
(开始更新)
旧版应用程序中的初始队列长度和超时:
当您未设置它们时。尝试使用
SerialPort 设置队列大小。 WriteBufferSize
和 <代码>SerialPort.ReadBufferSize。同样使用SerialPort.ReadTimeout< 设置超时/code>
和
SerialPort.WriteTimeout
。(结束更新)
在您的情况下,旧应用程序会:
当您这样做时:
您没有设置DTR(数据终端就绪)信号,因此设备不期望串行上有任何数据或命令线。因此设置
SerialPort.DtrEnable
为true
。第二个问题是你没有打开握手功能。旧版应用程序会:
当您这样做时:
通过设置
SerialPort.Handshake
到Handshake.RequestToSend
。此外,您似乎非常频繁地打开、关闭和重新配置串行端口。尝试设置端口,然后在整个会话中使用相同的
SerialPort
实例。不要尝试重新打开它,因为您将导致物理端口引脚状态的重新配置。串行通信并不是黑魔法,但它对设置非常敏感,并且各种设备需要特殊设置和处理。命令的正确时机也可能是一个问题。
如果您的设备确实有一些技术文档,请务必阅读两遍并首先遵守。至少应该正确记录握手模式、命令等。
如果您没有任何文档,请尝试一一缩小差异。
更新:发送和接收数据。
您写道您发送了命令
'Ping'
(从十六进制解码为 ASCII)。但是,您没有提及发送和命令终止序列。 通常串行设备期望行结束序列(通常是 CR LF)作为命令的终止。在设备收到包括线路结束在内的完整命令之前,它无法回复。您正在通过调用 ReadLine 来处理数据接收事件 - 但是,在您不能期望完整行数据的地方(即包括能够检测完整行的行尾)。您应该检查提供的事件参数并逐字节读取输入。
创建一个包装类是一个好主意,它将提供定制的发送命令、接收响应、发送数据、接收数据功能。包装器必须在内部与代码的其余部分异步工作。这样您将拥有可用的自定义 API 和良好的串行端口处理。
请注意,
SerialPort.NewLine
属性用于指定行尾序列的外观。 (在您的另一个问题中,您提到您试图将其设置为一组特殊字符。这确实是错误的。)我已经有一段时间成为串行通信英雄了(那些日子我们没有 vmware,只有两台 486 驱动的计算机) PC 和一对直接连接的调制解调器来开发和调试通信应用程序:-)),但我希望这至少有一点帮助。
最后但并非最不重要的一些常见术语:
Every RS-232 device needs to use some sort of flow control mechanism to notify the opposite part about ongoing communication. There are three basic mechanisms:
(start update)
Initial queue length and timeouts in the legacy app:
while you don't set them. Try setting the queue sizes using
SerialPort.WriteBufferSize
andSerialPort.ReadBufferSize
. Likewise set timeouts usingSerialPort.ReadTimeout
andSerialPort.WriteTimeout
.(end update)
In your case the legacy app does:
while you do:
You don't set the DTR (Data Terminal Ready) signal and hence the device is not expecting any data or commands on the serial line. Hence set
SerialPort.DtrEnable
totrue
.Second problem is you don't turn on handshaking. The legacy app does:
while you do:
Turn it on by setting
SerialPort.Handshake
toHandshake.RequestToSend
.Also, you seem to be opening, closing, and reconfiguring the serial port very frequently. Try to setup the port and then use the same instance of
SerialPort
for the whole session. Do not try to reopen it, because you will be causing reconfigurations to the state of the physical port's pins.Serial communication is not black magic, but it's quite sensitive on setup and various devices require special settings and treatment. Proper timing of commands may be an issue as well.
If you do have some technical documentation for your device, do read it twice and obey it at the first place. At least handshaking modes, commands, etc. should be documented properly.
If you don't have any documentation, try to mitigate differences one by one.
UPDATE: Sending and receiving data.
You wrote you sent the command
'Ping'
(as decoded from hex to ASCII). However, you don't mention sending and command termination sequence. Usually serial devices expect an end-of-line sequence (typically a CR LF) as the termination of the command. Before the device receives a complete command including line end, it can't reply.You are handling the data receive event by calling
ReadLine
— however, at the place you can't expect a full line of data (i.e. including a line end to be able to detect a comlete line). You should inspect the event arguments provided and read the input byte-by-byte.It's a good idea to create a wrapper class that will provide a tailored send-command, receive-response, send-data, receive-data features. The wrapper will have to internally work asynchronously in respect to the rest of your code. That way you will have a usable custom API and a well-behaved serial port handling.
Note that the
SerialPort.NewLine
property serves the purpose of specifying how a an end-of-line sequence looks like. (In your other question you mentioned you were trying to set it to the set of special characters. That was wrong indeed.)It's been some time I was a serial comm hero (those were the days we had no vmware but two 486-powered PCs and a pair of directly connected modems to develop and debug communication apps :-)), but I hope this helps at least a bit.
Last but not least, some common terminology:
您是否尝试过更改 握手 属性?也许设备在接受数据之前需要在控制引脚上进行一些握手。
Have you tried changing the Handshake property? Maybe the device requires some handshaking on the control pins before it will accept data.
您应该打开 RtsEnable 或 DtrEnable 属性,当设备没有从这些信号中检测到您在线时,设备将忽略您发送的任何内容,也不会发回任何内容。不过,将握手属性设置为 RTS 应该可以做到这一点。
请注意,ReadLine() 方法将阻塞,直到获得 NewLine 字符。您不会发送一封邮件,因此您也不会收到一封邮件。使用 Read() 会是一个更好的测试。
首先使用已知可以工作的程序进行一些基本的故障排除,消除接线问题、错误的波特率或设备根本不回显。使用超级终端或 Putty。
You should turn on the RtsEnable or DtrEnable properties, the device will ignore anything you send nor send anything back when it doesn't detect you online from these signals. Setting the Handshake property to RTS should have done that though.
Beware that the ReadLine() method will block until it gets the NewLine character. You are not sending one so you won't get one back either. Using Read() would be a better test.
Do some basic troubleshooting first with a known-to-work program, eliminating wiring problems, wrong baudrate or the device simply not echo-ing back. Use HyperTerminal or Putty.
汤姆,
我也遇到过类似的问题,
我将赌注押在 ReadLine() 仅在收到 \r\n 或 .NET 认为是的东西后才返回的事实换行。
在您的日志中,我没有看到 Winbird 报告任何新行字符,我相信 APP 正在逐字节读取,而不是等待换行。
因此,您可以向它发送许多字节的数据,它会继续阻塞,直到收到正确的换行符。
因此,尝试一下 SerialPort.Read,对于初学者来说,一次读取 1 个字节,
)
如果这不能解决问题,您可能需要考虑以下
问题我的一些问题通过添加一些 Thread.Sleep() 得到了解决,因为我意识到 PC 的 UART 速度不够快,无法通过 RS232/RS485 端口正确地以电方式传输数据,并且在打开端口后还要等待几毫秒。
我还建议您创建一个单独的线程来处理串行通信,而不是使用 SerialDataReceivedEventHandler,创建一个发送缓冲区,以正确的增量发送而不会阻塞您的应用程序,还读取数据并将收到的字节附加到缓冲区,直到您已收到足够的数据。
您还需要第二个串行设备来捕获数据,以确保您的 C# 应用程序传输正确的数据。
要消除握手问题,请仅连接 PIN 2,3 + PIN 5 (GND) 进行测试。
希望这有帮助
Tom ,
I have had similar problems,
I am going to place my bet on the fact that ReadLine() only returns once it has received a \r\n or something that .NET believes is a new line.
In your logs, i dont see Winbird reporting any new line characters, i believe that APP is reading byte by byte, and not waiting for newlines.
So you can send many bytes of data to it, and it will keep on blocking until it receives the correct new line characters.
So give SerialPort.Read a try, by reading 1 byte at a time for starters,
)
If this doesnt fix it, you might want to consider the following
Some of my problems were fixed with adding a few Thread.Sleep()'s because i realized the PC's UARTs are not fast enough to electrically transmit the data over the RS232/RS485 ports correctly, also wait a few milliseconds after opening the port.
I would also advise you to rather create a separate thread to handle the serial comms, instead of using SerialDataReceivedEventHandler, create a send buffer, which is sent in the correct increments without blocking your app, also read the data and append bytes received to buffer until you have received enough data.
You are also going to need a second serial device to capture your data to make sure your C# app is transmitting the correct data.
To elimate the issue of handshaking, test by only having PIN 2,3 + PIN 5 (GND) connected.
Hope this helps