简体   繁体   English

如何在C#中向串行设备发送字节?

[英]How do I send bytes to a serial device in C#?

I have a device that uses serial (via a USB adaptor) to interface with my PC. 我有一个使用串行(通过USB适配器)与我的PC连接的设备。 I'm having real difficulty getting it to play nicely in C#. 我真的很难让它在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". 我也知道我能够使用我的代码接收数据,这要归功于一个重复发送“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: 因此,我用以下字节调用SendCommand:

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. 对于一个附属问题,我发布了一些PortMon日志。 I think they might be useful here too, so here they are: 我认为它们在这里也可能有用,所以它们在这里:

  1. Vendors software - with all the IOCTL_SERIAL_GET_COMMSTATUS entries filtered out 供应商软件 - 过滤掉所有IOCTL_SERIAL_GET_COMMSTATUS条目
  2. My toe-dip attempt 我的脚趾尝试

Every RS-232 device needs to use some sort of flow control mechanism to notify the opposite part about ongoing communication. 每个RS-232设备都需要使用某种流量控制机制来通知相对的部分正在进行的通信。 There are three basic mechanisms: 有三种基本机制:

  • hardware-based RTS/CTS via two dedicated wires, usually used for controlling sending of individual chunks of data 基于硬件的RTS / CTS通过两条专用线路,通常用于控制单个数据块的发送
  • hardware-based DTR/DSR via two dedicated wires, usually used for controlling the whole communication session 基于硬件的DTR / DSR通过两条专用线路,通常用于控制整个通信会话
  • 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) 基于softwa的XON / XOFF通过一对专用字符(请注意,在这种情况下,需要对数据进行编码以防止与控制字符冲突)

(start update) (开始升级)

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

while you don't set them. 而你没有设置它们。 Try setting the queue sizes using SerialPort.WriteBufferSize and SerialPort.ReadBufferSize . 尝试使用SerialPort.WriteBufferSizeSerialPort.ReadBufferSize设置队列大小。 Likewise set timeouts using SerialPort.ReadTimeout and SerialPort.WriteTimeout . 同样使用SerialPort.ReadTimeoutSerialPort.WriteTimeout设置超时。

(end update) (最后更新)

In your case the legacy app does: 在您的情况下,遗留应用程序会:

12  IOCTL_SERIAL_CLR_RTS
13  IOCTL_SERIAL_SET_DTR

while you do: 你做的时候:

12  IOCTL_SERIAL_CLR_RTS
13  IOCTL_SERIAL_CLR_DTR

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. 您没有设置DTR(数据终端就绪)信号,因此设备不希望串行线路上有任何数据或命令。 Hence set SerialPort.DtrEnable to true . 因此将SerialPort.DtrEnable设置为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

while you do: 你做的时候:

16  IOCTL_SERIAL_SET_HANDFLOW  Shake:0 Replace:0 XonLimit:4096 XoffLimit:4096

Turn it on by setting SerialPort.Handshake to Handshake.RequestToSend . SerialPort.Handshake设置为Handshake.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. 尝试设置端口,然后在整个会话中使用相同的SerialPort实例。 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). 您写道,您发送了命令'Ping' (从十六进制解码为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. 通常,串行设备期望行结束序列(通常是CR LF)作为命令的终止。 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 (ie including a line end to be able to detect a comlete line). 您正在通过调用ReadLine来处理数据接收事件 - 但是,在您不能指望完整数据行的位置(即包括行结束以检测COMlete行)。 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. 这样,您将拥有可用的自定义API和良好的串行端口处理。

Note that the SerialPort.NewLine property serves the purpose of specifying how a an end-of-line sequence looks like. 请注意, SerialPort.NewLine属性用于指定行尾序列的外观。 (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. 曾经有一段时间我是一个串行通讯英雄(那些是我们没有vmware的日子,但有两台486供电的PC和一对直接连接的调制解调器来开发和调试通信应用程序:-)),但我希望这至少有帮助一点点。

Last but not least, some common terminology: 最后但并非最不重要的一些常用术语:

  • DTE — Data Terminal Equipment = your comupter DTE - 数据终端设备=您的comupter
  • DCE — Data Communications Equipment = your device, eg a modem DCE - 数据通信设备=您的设备,例如调制解调器

Have you tried changing the Handshake property? 您是否尝试过更改Handshake属性? Maybe the device requires some handshaking on the control pins before it will accept data. 也许设备在接受数据之前需要在控制引脚上进行一些握手。

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. 您应该打开RtsEnable或DtrEnable属性,设备将忽略您发送的任何内容,也不会在这些信号未在线检测到您时发回任何内容。 Setting the Handshake property to RTS should have done that though. 将Handshake属性设置为RTS应该已经这样做了。

Beware that the ReadLine() method will block until it gets the NewLine character. 请注意,ReadLine()方法将阻塞,直到获取NewLine字符。 You are not sending one so you won't get one back either. 你没有发送一个,所以你也不会回来。 Using Read() would be a better test. 使用Read()将是一个更好的测试。

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. 使用超级终端或腻子。

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. 我打赌我的赌注是ReadLine()只有在收到\\ r \\ n或者.NET认为是新行的东西后才会返回

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. 在你的日志中,我没有看到Winbird报告任何新行字符,我相信APP正在逐字节读取,而不是等待换行。

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, 所以试试SerialPort.Read,一次读取1个字节给初学者,

public int Read (
byte[] buffer,
int offset,
int count

)

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. 我的一些问题通过添加一些Thread.Sleep()来解决,因为我意识到PC的UART速度不够快,不能正确地通过RS232 / RS485端口电传输数据,也可以在打开端口后等待几毫秒。

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. 我还建议你创建一个单独的线程来处理串行通信,而不是使用SerialDataReceivedEventHandler,创建一个发送缓冲区,它以正确的增量发送而不阻塞你的应用程序,也读取数据并将接收到的字节追加到缓冲区,直到你收到了足够的数据。

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. 您还需要第二个串行设备来捕获数据,以确保您的C#应用​​程序正在传输正确的数据。

To elimate the issue of handshaking, test by only having PIN 2,3 + PIN 5 (GND) connected. 要消除握手问题,只需连接PIN 2,3 + PIN 5(GND)即可进行测试。

Hope this helps 希望这可以帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM