问题描述
我有一个使用串行(通过 USB 适配器)与我的 PC 连接的设备.我很难让它在 C# 中很好地发挥作用.我知道它可以正常工作,因为供应商提供的软件按预期运行.我也知道我能够使用我的代码接收数据,这要归功于反复发送OK"的测试模式.
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".
这是我的代码:
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:
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?
对于一个附属问题,我发布了一些 PortMon 日志.我认为它们在这里也可能有用,所以它们在这里:
For a subsidiary question I've posted some PortMon logs. I think they might be useful here too, so here they are:
- 供应商软件 - 过滤掉所有
IOCTL_SERIAL_GET_COMMSTATUS
条目 - 我的脚趾试探
- Vendors software - with all the
IOCTL_SERIAL_GET_COMMSTATUS
entries filtered out - My toe-dip attempt
推荐答案
每个 RS-232 设备都需要使用某种 流控制机制来通知对方正在进行的通信.有三种基本机制:
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:
- 基于硬件的RTS/CTS,通过两条专用线,通常用于控制单个数据块的发送
- 基于硬件的DTR/DSR通过两条专用线,通常用于控制整个通信会话
- 基于软件的 XON/XOFF 通过一对专用字符(请注意,在这种情况下,需要对数据进行编码以防止与控制字符发生冲突)
- hardware-based RTS/CTS via two dedicated wires, usually used for controlling sending of individual chunks of data
- hardware-based DTR/DSR via two dedicated wires, usually used for controlling the whole communication session
- softwa-based XON/XOFF via a pair of dedicated characters (note that in this case data needs to be encoded to prevent collisions with control characters)
(开始更新)
旧版应用程序中的初始队列长度和超时:
Initial queue length and timeouts in the legacy app:
1 IOCTL_SERIAL_SET_QUEUE_SIZE InSize: 1024 OutSize: 1024
2 IOCTL_SERIAL_SET_TIMEOUT RI:2000 RM:0 RC:2000 WM:0 WC:2000
虽然你没有设置它们.尝试使用 SerialPort 设置队列大小.WriteBufferSize
和 <代码>SerialPort.ReadBufferSize.同样使用 SerialPort.ReadTimeout<设置超时/code> 和
SerialPort.WriteTimeout
.
while you don't set them. Try setting the queue sizes using SerialPort.WriteBufferSize
and SerialPort.ReadBufferSize
. Likewise set timeouts using SerialPort.ReadTimeout
and SerialPort.WriteTimeout
.
(结束更新)
在您的情况下,旧版应用程序可以:
In your case the legacy app does:
12 IOCTL_SERIAL_CLR_RTS
13 IOCTL_SERIAL_SET_DTR
当你这样做时:
12 IOCTL_SERIAL_CLR_RTS
13 IOCTL_SERIAL_CLR_DTR
您没有设置 DTR(数据终端就绪)信号,因此设备不期望串行线路上有任何数据或命令.因此设置 SerialPort.DtrEnable
到 true
.
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
to true
.
第二个问题是你没有打开握手.旧版应用会:
Second problem is you don't turn on handshaking. The legacy app does:
16 IOCTL_SERIAL_SET_HANDFLOW Shake:1 Replace:0 XonLimit:0 XoffLimit:0
18 IOCTL_SERIAL_SET_RTS
…
21 IOCTL_SERIAL_CLR_RTS
当你这样做时:
16 IOCTL_SERIAL_SET_HANDFLOW Shake:0 Replace:0 XonLimit:4096 XoffLimit:4096
通过设置打开它 SerialPort.Handshake
到 Handshake.RequestToSend
.
Turn it on by setting SerialPort.Handshake
to Handshake.RequestToSend
.
此外,您似乎非常频繁地打开、关闭和重新配置串行端口.尝试设置端口,然后在整个会话中使用相同的 SerialPort
实例.不要尝试重新打开它,因为您会重新配置物理端口引脚的状态.
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.
更新:发送和接收数据.
你写你发送了命令'Ping'
(从十六进制解码为ASCII).但是,您没有提到发送和命令终止序列.通常串行设备需要一个行尾序列(通常是 CR LF)作为命令的终止.设备在收到包含 line end 的 complete 命令之前,无法回复.
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.
您正在通过调用 ReadLine
来处理数据接收事件——但是,在您不能指望一整行数据的地方(即包括能够检测到完整行的行尾)).您应该检查提供的事件参数并逐字节读取输入.
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.
创建一个提供定制的发送命令、接收响应、发送数据和接收数据功能的包装类是个好主意.包装器必须相对于您的其余代码在内部异步工作.这样,您将拥有可用的自定义 API 和良好的串行端口处理.
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.
请注意,SerialPort.NewLine
属性用于指定行尾序列的外观.(在您提到的另一个问题中,您试图将其设置为特殊字符集.这确实是错误的.)
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.)
有一段时间我是串行通信英雄(那时我们没有 vmware,只有两台 486 驱动的 PC 和一对直接连接的调制解调器来开发和调试通信应用程序 :-)),但我希望这个至少有一点帮助.
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:
- DTE — 数据终端设备 = 您的计算机
- DCE — 数据通信设备 = 您的设备,例如调制解调器
这篇关于如何在 C# 中将字节发送到串行设备?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!