简体   繁体   English

同一台Windows机器上的串行端口通信无法正常工作

[英]Serial Port Communication on the same Windows machine not working

Excuse me, quick question: 对不起,快问:

I have this hardware setup: 我有这个硬件设置:

Same machine: "Com3" -> USB -> To Serial -> To USB -> "Com4" 同一台机器:“Com3” - > USB - >串口 - >转USB - >“Com4”

And I followed MSDN SerialPort Class and MSDN SerialPort.ReadLine() to build this routine: 我按照MSDN SerialPort类MSDN 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);

However, when I tend to ReadLine() , my control stops and just waits. 但是,当我倾向于ReadLine() ,我的控件会停止并等待。 I did not expect that. 我原本没想到。

I am expecting to receive the string Test and assign it to my var message . 我期待收到字符串Test并将其分配给我的var message Could you please tell me what am I doing wrong here? 你能告诉我这里我做错了什么吗?

EDIT : 编辑

I tested my hardware using the Serial Port Utility Application and it worked just fine . 我使用串行端口实用程序应用程序测试了我的硬件, 它运行得很好

I've altered from the example you linked : 我改变了你链接的例子

To actually have both ports running to read and write back and forth you will actually need to implement threading for reading and writing for both. 要实际让两个端口都运行来回读写,你实际上需要为两者实现读写的线程。

It can be a good idea to use a timer. 使用计时器是个好主意。

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) { }
    }
}

Usually there will be a beginning and end value within the written data to tell the other port that the message is finished and also for the ports to validate that they are reading data they should be, usually with commands of what to do with that data. 通常在写入的数据中会有一个开始和结束值,告诉另一个端口消息已完成,并且端口也要验证它们是否正在读取它们应该读取的数据,通常使用有关该数据的命令。 (out of scope for this question). (超出此问题的范围)。

Also lacking and important is the intialisation of your ports. 缺乏和重要的是您的端口的初始化。

I prefer to use the default constructor (preference only) 我更喜欢使用默认构造函数(仅限首选项)

SerialPort Constructor ()

And then set any values like so: 然后设置任何值,如下所示:

_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);

All the constructors will give these values: 所有构造函数都将给出以下值:

This constructor uses default property values when none are specified. 当没有指定时,此构造函数使用默认属性值。 For example, the DataBits property defaults to 8, the Parity property defaults to the None enumeration value, the StopBits property defaults to 1, and a default port name of COM1. 例如,DataBits属性默认为8,Parity属性默认为None枚举值,StopBits属性默认为1,默认端口名称为COM1。

Even the handshake has a default value. 即使握手也有默认值。 If you look at the source code . 如果你看一下源代码

private const Handshake defaultHandshake = Handshake.None;

The problem with your code is in this line 您的代码存在问题

var message = ReceiveSerialPort.ReadLine();

You block your code to wait for a line, if the line never arrives it will remain here forever or the value set to ReadTimeout 您阻止代码等待一行,如果该行永远不会到达,它将永久保留在此处或将值设置为ReadTimeout

So why does the line never arrive? 那么为什么线路永远不会到达?

The problem can be an error in WriteLine("Test"); 问题可能是WriteLine("Test");的错误WriteLine("Test"); , you should handle errors, or it can be that your in are blocking your code ReadLine() before the WriteLine("Test") manage to come through, you could insert a Thread.Sleep(100) between, but this is not really improving the code. 你应该处理错误,或者你的in在阻止你的代码ReadLine()之前WriteLine("Test")设法通过,你可以插入一个Thread.Sleep(100) ,但这不是真的改进代码。

Note : Your code will also work as is sometimes, depending on these race conditions. 注意 :您的代码有时会起作用,具体取决于这些竞争条件。

This synchronized / blocking reading from serial ports seems simple in code just one line; 串行端口的这种同步/阻塞读取在代码中只有一行似乎很简单; but it creates a lot of negative side effects in your communication protocol's. 但它会在您的通信协议中产生许多负面影响。

A much better solution (considering that you like to Read / Write data from a microcontroller) is to either use a thread as Yvette suggested or use asynchronously reading Stream.BeginRead (Byte[], Int32, Int32, AsyncCallback, Object) which I would prefer. 一个更好的解决方案(考虑到您喜欢从微控制器读取/写入数据)是要么使用Yvette建议的线程,要么异步读取Stream.BeginRead(Byte [],Int32,Int32,AsyncCallback,Object) ,我会喜欢。

The asynchronously reading will throw an event when something is incoming on the serial port. 当串行端口传入某些内容时,异步读取将抛出一个事件。 The basic idea of this programming strategy is to not do step programming but expecting what ever result and then handle it correctly. 这种编程策略的基本思想是不进行步骤编程,而是期望得到的结果,然后正确处理它。

In communications protocol with asynchronously reading the AutoResetEvent is very useful, hence you send something, then you start the AutoResetEvent , if asynchronously the expected result is arriving you will set this event and your code can continue, if it does not arrive the AutoResetEvent will timeout and you can handle this. 在通信协议中异步读取AutoResetEvent是非常有用的,因此你发送一些东西,然后启动AutoResetEvent ,如果异步地预期结果到达你将设置此事件并且你的代码可以继续,如果它没有到达, AutoResetEvent将超时你可以处理这个。

It cannot block when there is data available. 当有可用数据时,它无法阻止。 What you sent either got stuck in the transmit buffer, got lost due to a wiring mistake, triggered an error or was ignored. 您发送的内容或者卡在发送缓冲区中,由于布线错误而丢失,触发错误或被忽略。 If it works with another program then a wiring mistake can't be the problem. 如果它适用于另一个程序,则接线错误不是问题。

Do keep in mind that just setting the Baudrate is not enough, you must also use set the DataBits, Parity and Stopbits properties to match the device settings. 请记住,仅设置Baudrate是不够的,您还必须使用设置DataBits,Parity和Stopbits属性来匹配设备设置。 A mismatch can trigger an error, the kind you can only see when you write an event handler for the ErrorReceived event. 不匹配可能会触发错误,您只能在为ErrorReceived事件编写事件处理程序时才能看到错误。 Never skip that event, confounding problems can occur if you never check. 永远不要跳过那个事件,如果你从不检查,可能会出现混乱的问题。

And above all the Handshake property must be set correctly. 最重要的是,必须正确设置Handshake属性。 The proper value depends on how the ports are wired together, it is too common to not connect them. 正确的值取决于端口如何连接在一起,不常连接它们太常见了。 Start by setting it to Handshake.None so a wrong state for the DSR and CTS signals can't block reception and a wrong state for the DTR and RTS signals can't block transmission. 首先将其设置为Handshake.None,因此DSR和CTS信号的错误状态不能阻止接收,并且DTR和RTS信号的错误状态不能阻止传输。 Beware that it is common for another program to enable hardware handshaking, a mismatch is guaranteed to cause communications to stall. 请注意,另一个程序通常会启用硬件握手,但保证不匹配会导致通信停止。

If you use synchronous reads instead of the DataReceived event then you should in general deal with the possibility that a device is not responding. 如果您使用同步读取而不是DataReceived事件,那么您通常应该处理设备没有响应的可能性。 Either because it is powered off, not connected at all or malfunctioning. 要么是因为它断电,根本没有连接或发生故障。 Use the ReadTimeout property for that so your program cannot hang. 使用ReadTimeout属性,这样您的程序就无法挂起。 Aim high, 10000 milliseconds is a reasonable choice. 目标高,10000毫秒是一个合理的选择。

Beware the randomness of this problem, putzing around with another program can easily get the port configured correctly and now it will suddenly work. 注意这个问题的随机性,使用另一个程序可以轻松地正确配置端口,现在它将突然工作。 And beware that starting a thread accomplishes nothing, it will now be that thread that gets stuck and the Join() call will deadlock. 并且要注意,启动一个线程什么都不会完成,现在它将被卡住的线程和Join()调用将死锁。

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

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