如何在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日志。 我认为它们在这里也可能有用,所以它们在这里:
- 供应商软件 – 过滤掉所有
IOCTL_SERIAL_GET_COMMSTATUS
条目 - 我的脚趾尝试
每个RS-232设备都需要使用某种流量控制机制来通知相对的部分正在进行的通信。 有三种基本机制:
- 基于硬件的RTS / CTS通过两条专用线路,通常用于控制单个数据块的发送
- 基于硬件的DTR / DSR通过两条专用线路,通常用于控制整个通信会话
- 基于softwa的XON / XOFF通过一对专用字符(请注意,在这种情况下,需要对数据进行编码以防止与控制字符冲突)
(开始升级)
旧应用程序中的初始队列长度和超时:
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
和SerialPort.WriteTimeout
设置超时。
(最后更新)
在您的情况下,遗留应用程序执行:
12 IOCTL_SERIAL_CLR_RTS 13 IOCTL_SERIAL_SET_DTR
你做的时候:
12 IOCTL_SERIAL_CLR_RTS 13 IOCTL_SERIAL_CLR_DTR
您没有设置DTR(数据终端就绪)信号,因此设备不期望串行线路上的任何数据或命令。 因此将SerialPort.DtrEnable
设置为true
。
第二个问题是你不要打开握手。 遗留应用程序:
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
将其打开。
此外,您似乎经常打开,关闭和重新配置串行端口。 尝试设置端口,然后在整个会话中使用相同的SerialPort
实例。 不要尝试重新打开它,因为您将导致重新配置物理端口引脚的状态。
串行通信不是黑魔法,但它在设置上非常敏感,各种设备需要特殊的设置和处理。 适当的命令时序也可能是个问题。
如果您的设备确实有一些技术文档,请先阅读两次,并在第一时间遵守它。 至少应该正确记录握手模式,命令等。
如果您没有任何文档,请尝试逐个缓解差异。
更新:发送和接收数据。
您写道,您发送了命令'Ping'
(从hex解码为ASCII)。 但是,您没有提到发送和命令终止序列。 通常,串行设备期望序列结束序列(通常是CR LF)作为命令的终止。 在设备收到包含行结束的完整命令之前,它无法回复。
您正在通过调用ReadLine
来处理数据接收事件 – 但是,在您不能指望完整数据行的位置(即包括行结束以检测COMlete行)。 您应该检查提供的事件参数并逐字节读取输入。
创建一个包装类是个好主意,它将提供定制的发送命令,接收响应,发送数据,接收数据function。 包装器必须在内部与代码的其余部分异步工作。 这样,您将拥有可用的自定义API和良好的串行端口处理。
请注意, SerialPort.NewLine
属性用于指定行尾序列的外观。 (在你提到的另一个问题中,你试图将它设置为特殊字符集。这确实是错误的。)
曾经有一段时间我是一个串行通讯英雄(那些是我们没有vmware的日子,但有两台486供电的PC和一对直接连接的调制解调器来开发和调试通信应用程序:-)),但我希望这至少有帮助一点点。
最后但并非最不重要的是,一些常见的术语:
- DTE – 数据终端设备=您的计算器
- DCE – 数据通信设备=您的设备,例如调制解调器
您是否尝试过更改Handshake属性? 也许设备在接受数据之前需要在控制引脚上进行一些握手。
您应该打开RtsEnable或DtrEnable属性,当设备无法在线检测到这些信号时,设备将忽略您发送的任何内容,也不会发回任何内容。 将Handshake属性设置为RTS应该已经这样做了。
请注意,ReadLine()方法将阻塞,直到它获得NewLine字符。 你没有发送一个,所以你也不会回来。 使用Read()将是一个更好的测试。
首先使用已知的工作程序进行一些基本的故障排除,消除接线问题,错误的波特率或设备根本不回显。 使用超级终端或腻子。
汤姆,
我有类似的问题,
我打赌我的赌注是ReadLine()只有在收到\ r \ n或者.NET认为是新行的东西后才会返回。
在你的日志中,我没有看到Winbird报告任何新行字符,我相信APP正在逐字节读取,而不是等待换行。
因此,您可以向其发送许多字节的数据,并且它将继续阻塞,直到它收到正确的新行字符。
所以试试SerialPort.Read,一次读取1个字节给初学者,
public int Read ( byte[] buffer, int offset, int count
)
如果这没有解决它,您可能需要考虑以下内容
我的一些问题通过添加一些Thread.Sleep()来解决,因为我意识到PC的UART速度不够快,不能正确地通过RS232 / RS485端口电传输数据,也可以在打开端口后等待几毫秒。
我还建议你宁愿创建一个单独的线程来处理串行通信,而不是使用SerialDataReceivedEventHandler,创建一个发送缓冲区,它以正确的增量发送而不阻塞你的应用程序,也读取数据并将接收的字节附加到缓冲区,直到你收到了足够的数据。
您还需要第二个串行设备来捕获数据,以确保您的C#应用程序正在传输正确的数据。
要消除握手问题,只需连接PIN 2,3 + PIN 5(GND)即可进行测试。
希望这可以帮助