【问题标题】:Serial Port Communication on the same Windows machine not working同一台 Windows 机器上的串行端口通信不起作用
【发布时间】:2016-09-03 10:30:27
【问题描述】:

对不起,问个简单的问题:

我有这个硬件设置:

同一台机器:“Com3” -> USB -> 转串口 -> 转 USB -> “Com4”

我按照MSDN SerialPort ClassMSDN SerialPort.ReadLine() 构建了这个例程:

SerialPort SendSerialPort = new SerialPort("Com3", 9600);
SerialPort ReceiveSerialPort = new SerialPort("Com4", 9600);

SendSerialPort.Open();
ReceiveSerialPort.Open();

SendSerialPort.WriteLine("Test");
var message = ReceiveSerialPort.ReadLine(); // control stops here

SendSerialPort.Close();
ReceiveSerialPort.Close();

Console.WriteLine(message);

但是,当我倾向于 ReadLine() 时,我的控制停止并只是等待。我没想到的是。

我希望收到字符串Test 并将其分配给我的var message。你能告诉我我在这里做错了什么吗?

编辑

我使用Serial Port Utility Applicationit worked just fine 测试了我的硬件。

【问题讨论】:

  • IO 操作被阻塞。您需要将至少一个 IO 操作移至另一个线程/任务。

标签: c# serial-port


【解决方案1】:

我修改了from the example you linked:

要真正让两个端口运行来来回读写,您实际上需要实现两个端口的读写线程。

使用计时器可能是个好主意。

public static void Main()
{
    SerialPort SendSerialPort = new SerialPort("Com3", 9600);
    SerialPort ReceiveSerialPort = new SerialPort("Com4", 9600);

    StringComparer stringComparer = StringComparer.OrdinalIgnoreCase;
    Thread readThread = new Thread(Read);

    // Set the read/write timeouts
    _serialPort.ReadTimeout = 500;
    _serialPort.WriteTimeout = 500;

    SendSerialPort.Open();
    ReceiveSerialPort.Open();
    bool _continue = true;
    readThread.Start();

    Console.Write("Name: ");
    name = Console.ReadLine();

    Console.WriteLine("Type QUIT to exit");

    while (_continue)
    {
        message = Console.ReadLine();

        if (stringComparer.Equals("quit", message))
            _continue = false;
        else
            SendSerialPort.WriteLine(String.Format("<{0}>: {1}", name, message));
    }
    readThread.Join();
    SendSerialPort.Close();
}

public static void Read()
{
    while (_continue)
    {
        try
        {
            string message = ReceiveSerialPort.ReadLine();
            Console.WriteLine(message);
        }
        catch (TimeoutException) { }
    }
}

通常在写入的数据中会有一个开始值和结束值,以告诉另一个端口消息已完成,并且端口也可以验证它们正在读取它们应该读取的数据,通常带有如何处理的命令那个数据。 (超出这个问题的范围)。

端口的初始化也很重要。

我更喜欢使用默认构造函数(仅限偏好)

SerialPort Constructor ()

然后像这样设置任何值:

_serialPort.BaudRate = SetPortBaudRate(_serialPort.BaudRate);
_serialPort.Parity = SetPortParity(_serialPort.Parity);
_serialPort.DataBits = SetPortDataBits(_serialPort.DataBits);
_serialPort.StopBits = SetPortStopBits(_serialPort.StopBits);
_serialPort.Handshake = SetPortHandshake(_serialPort.Handshake);

所有的构造函数都会给出这些值:

This constructor uses default property values when none are specified.例如DataBits属性默认为8,Parity属性默认为None枚举值,StopBits属性默认为1,默认端口名称为COM1。

即使是握手也有默认值。如果你look at the source code.

private const Handshake defaultHandshake = Handshake.None;

【讨论】:

  • 我无法重现该问题。因此,我假设它是随机的,因为参数DataBitsStopBitsParityare set by default。我想确保它不再发生,因此我将使用上面推荐的多线程方法通过串行端口进行连续读写。
【解决方案2】:

你的代码问题出在这一行

var message = ReceiveSerialPort.ReadLine();

您阻止代码等待一行,如果该行永远不会到达,它将永远保留在这里或将值设置为ReadTimeout

那为什么线路永远不会到达?

问题可能是WriteLine("Test"); 中的错误,您应该处理错误,或者您的in 在WriteLine("Test") 成功通过之前阻塞了您的代码ReadLine(),您可以插入Thread.Sleep(100)之间,但这并没有真正改进代码。

注意:您的代码有时也会按原样工作,具体取决于这些竞争条件。

这种从串口同步/阻塞读取的代码看起来很简单,只有一行;但它会在您的通信协议中产生很多负面影响。

一个更好的解决方案(考虑到您喜欢从微控制器读取/写入数据)是使用线程作为Yvette suggested 或使用异步读取Stream.BeginRead (Byte[], Int32, Int32, AsyncCallback, Object),我更喜欢。

当串口有东西传入时,异步读取将引发一个事件。这种编程策略的基本思想是不进行步进编程,而是期待结果,然后正确处理。

在异步读取AutoResetEvent 的通信协议中非常有用,因此您发送一些东西,然后您启动AutoResetEvent,如果异步预期结果到达您将设置此事件并且您的代码可以继续,如果它没有到达 AutoResetEvent 将超时,你可以处理这个。

【讨论】:

  • 感谢您的回答。如果您不介意,我有一个问题:如果“我的线路永远不会到达”,为什么它永远不会到达并且花了那么长时间?然后我的代码应该在“100”毫秒内保持在Read(),比方说,然后在消息到达后继续。我错过了什么?
  • 发送时可能出错,可能是您在 readLine 中阻止发送之前阻止了发送(它没有时间开始)(因此 readLine() 实际上正在阻止你的代码),而 writeLine 不是。
  • 哦,它一开始没有时间通过​​,它得到了“中断”。我懂了。谢谢你的课!
【解决方案3】:

当有可用数据时,它不能阻塞。您发送的内容要么卡在传输缓冲区中,要么由于接线错误而丢失,要么触发错误,要么被忽略。如果它与另一个程序一起工作,那么接线错误就不是问题了。

请记住,仅设置波特率是不够的,您还必须使用设置 DataBits、Parity 和 Stopbits 属性来匹配设备设置。不匹配会触发错误,只有在为 ErrorReceived 事件编写事件处理程序时才能看到这种错误。切勿跳过该事件,如果您不检查,可能会出现令人困惑的问题。

首先必须正确设置 Handshake 属性。正确的值取决于端口如何连接在一起,不连接它们太常见了。首先将其设置为 Handshake.None,因此 DSR 和 CTS 信号的错误状态不能阻止接收,DTR 和 RTS 信号的错误状态不能阻止传输。请注意,另一个程序启用硬件握手是很常见的,不匹配肯定会导致通信停止。

如果您使用同步读取而不是 DataReceived 事件,那么您通常应该处理设备没有响应的可能性。可能是因为它已关闭电源,根本没有连接或出现故障。为此,请使用 ReadTimeout 属性,这样您的程序就不会挂起。志存高远,10000毫秒是一个合理的选择。

注意这个问题的随机性,使用另一个程序可以很容易地正确配置端口,现在它会突然工作。并且要注意,启动一个线程什么也没做,现在是那个线程卡住了,Join() 调用会死锁。

【讨论】:

  • 一个问题:你建议需要多线程吗?它在同一个线程上工作。我想我只需要确保正确/正确的配置。
  • 我试图在最后一段中警告您,使用线程不是解决方案。所以不行。您必须专注于配置,这是您的问题。坚如磐石的规则是,你永远不能走捷径。
  • @Yvette 和 Hans 先生,我的任务是读取/写入来自微控制器的数据,速度高达 1000 次/秒。不过它会在同一个端口上。这是我熟悉代码并了解这项技术如何工作的热身开始。那么我最好的方法和道路是什么?
  • 好吧,你已经热身了。配置很重要,永远不要忽略错误。是使用同步读取还是使用 DataReceived 事件取决于程序的其余部分。
  • @HansPassant 我很好奇为什么“您还必须设置 DataBits、Parity 和 Stopbits”?,这应该已经在构造函数 referencesource.microsoft.com/#System/sys/system/io/ports/… 中完成了,它不是已经使用这些默认值了吗?跨度>
猜你喜欢
  • 1970-01-01
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多