简体   繁体   English

如何在C#中知道串口的输入缓冲区有信息?

[英]How to know the input buffer of the serial port has information, in C#?

I am building a program in C# to be used in one of my course at a college to demonstrate how Asynchronous connections work using RS-232 and two computers connected together. 我正在用C#开发一个程序,供我在大学的一门课程中使用,以演示如何使用RS-232和两台连接在一起的计算机进行异步连接。 My course is not about programming, but data networks, so the connectivity is what I am looking for. 我的课程不是编程,而是数据网络,因此连通性是我想要的。

picture 1 - sample layout of GUI using Visual Studio 2015 图片1-使用Visual Studio 2015的GUI的示例布局

One of the features I want to implement in my program is to show how a Master-slave, simplex connection works (ie the program can choose between been a master to send input from the keyboard; or slave to only receive information and print it on a textbox). 我要在程序中实现的功能之一是演示主从连接的单工连接的工作方式(即程序可以选择是从键盘发送输入的主机还是从键盘接收信息并在其上打印的从机)。一个文本框)。

What I have already is the capability of initializing the serial port with specific characteristics (baud rate, data bits, stop bits, etc). 我已经具备了初始化具有特定特征(波特率,数据位,停止位等)的串行端口的功能。 This features are selected using combo boxes from the GUI, and assigned to the port when the user clicks a button to "open the port". 使用GUI中的组合框可以选择此功能,并在用户单击按钮“打开端口”时将其分配给端口。

What I don't know is how to create the "slave" part of the program. 我不知道如何创建程序的“从属”部分。 My idea of what I could do is, after you choose the program to be "slave", you open the port waiting for some sort of flag or event to trigger when the input buffer has data stored. 我的想法是,在将程序选择为“从”程序后,打开端口,等待输入缓冲区存储数据时触发某种标志或事件。

I've been reading several forums and I can't find anything similar to what I need. 我读过几个论坛,但找不到与我需要的东西相似的东西。 I have, however, tested multiple alternatives that I believed would bring me closer to what I need with little to no result. 但是,我已经测试了多种选择,我相信它们会使我更接近所需,几乎没有结果。 I come to ask for an idea of what I could be doing wrong, or suggestions on how to tackle this problem. 我来询问关于我可能做错了什么的想法,或者关于如何解决此问题的建议。 The problematic lines are bolded (or 2 stars ( * ) ): 有问题的行以粗体显示 (或2星(*)):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;

namespace SerialCommTester
{
public partial class frmSerialComm : Form
{
    static SerialPort _PuertoSerial;

    public frmSerialComm()
    {
        InitializeComponent();
        getAvailablePorts();
     }

    //---------------------------------my functions--------------------------------------
    void getAvailablePorts()
    {
        string[] ports = SerialPort.GetPortNames();
        cmbPortList.Items.AddRange(ports);
    }

    void activatePort()
    {
     //Note that all the combo boxes are named somewhat accordingly to what the information they are meant to display.
        if (cmbPortList.Text != "" && cmbBaudRate.Text != "" && cmbParity.Text != "" && cmbStopBits.Text != "")
        {
            _PuertoSerial.PortName = cmbPortList.Text;
            _PuertoSerial.BaudRate = Convert.ToInt32(cmbBaudRate.Text);
            _PuertoSerial.RtsEnable = true;
            _PuertoSerial.DtrEnable = true;

            _PuertoSerial.DataBits = Convert.ToInt32(cmbDataBits.Text);

            if (cmbParity.Text == "Even") { _PuertoSerial.Parity = Parity.Even; }
            else if (cmbParity.Text == "Odd") { _PuertoSerial.Parity = Parity.Odd; }
            else if (cmbParity.Text == "Space") { _PuertoSerial.Parity = Parity.Space; }
            else if (cmbParity.Text == "Mark") { _PuertoSerial.Parity = Parity.Mark; }
            else { _PuertoSerial.Parity = Parity.None; }

            if (cmbStopBits.Text =="2") { _PuertoSerial.StopBits = StopBits.Two; }
            else if (cmbStopBits.Text == "1.5") { _PuertoSerial.StopBits = StopBits.OnePointFive; }
            else { _PuertoSerial.StopBits = StopBits.One; }

            if (cmbHandShake.Text == "Software Flow Control") { _PuertoSerial.Handshake = Handshake.XOnXOff; }
            else if (cmbHandShake.Text == "Hardware Flow Control") { _PuertoSerial.Handshake = Handshake.RequestToSend; }
            else { _PuertoSerial.Handshake = Handshake.None; }

            _PuertoSerial.ReadTimeout = 500; 
            _PuertoSerial.WriteTimeout = 500;

            _PuertoSerial.Open();
//in my understanding, this line of code is needed to handle data being received. Does it trigger a flag or something?
            **_PuertoSerial.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);**
        }
        else
        {
            txtRecieve.Text = "Input selection missing 1 or more characteristics";
        }
    }

    **
 private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    { 
        SerialPort testing = (SerialPort)sender;
        txtRecieve.AppendText(testing.ReadExisting());  //txtRecieve cannot be reached within this function. It indicates the following error: "An object reference is required for the non-static field, method, or property 'frmSerialComm.txtRecieve'
    }
    **

    void enableDisableGUI(bool[] input)
    {
        grpConnection.Enabled = input[0];
        grpCharacteristics.Enabled = input[1];
        btnOpenPort.Enabled = input[2];
        btnClosePort.Enabled = input[3];
        txtSend.Enabled = ((cmbControlMasterSlave.Text == "Slave") ? false : true);
    }

    //----------------------------C# objects / functions--------------------------------------
    private void btnOpenPort_Click(object sender, EventArgs e)
    {
        try
        {
            _PuertoSerial = new SerialPort();
            activatePort();
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.Message, "Message ", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        bool[] format = { false, false, false, true};
        enableDisableGUI(format);
    }

    private void btnClosePort_Click(object sender, EventArgs e)
    {
        _PuertoSerial.Close();
        bool[] format = { true, true, true, false};
        enableDisableGUI(format);
    }

    private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
    {
       _PuertoSerial.Write(e.KeyChar.ToString()); //this is how I send data through the serial port.
    }

    private void btnClearTxts_Click(object sender, EventArgs e)
    {
        txtRecieve.Clear();
        txtSend.Clear();
    }

} //class closes
} //program closes

I am not an experienced programmer, I just want to create something useful for my students. 我不是一个经验丰富的程序员,我只想为学生创造一些有用的东西。 Any constructive criticism will be highly appreciated. 任何建设性的批评将不胜感激。

I don't have any definitive answers for you. 我没有确切的答案给您。 You code looks like it should provide what you need once you get past the two possible glitches. 一旦克服了两种可能的故障,您的代码看起来就应该提供所需的内容。

  1. I think you should attach your SerialDataReceivedEventHandler BEFORE you call _PuertoSerial.Open(). 我认为您应该在调用_PuertoSerial.Open()之前附加SerialDataReceivedEventHandler。

    It may have no effect since event handlers can normally be enabled/disabled dynamically, but I base the advice on the following comment taken from the .Net source code for SerialPort on MSDN. 因为事件处理程序通常可以动态地启用/禁用,所以它可能没有任何作用,但是我的建议基于以下注释,这些注释来自MSDN上SerialPort的.Net源代码。

    // all the magic happens in the call to the instance's .Open() method. //所有魔术都发生在对实例的.Open()方法的调用中。

    // Internally, the SerialStream constructor opens the file handle, sets the device control block and associated Win32 structures, and begins the event-watching cycle. //在内部,SerialStream构造函数打开文件句柄,设置设备控制块和关联的Win32结构,并开始事件监视周期。

  2. The "object reference" error might be resolved by removing the static modifier from your DataReceivedHandler. 通过从DataReceivedHandler中删除静态修饰符,可以解决“对象引用”错误。 If not, or if that static modifier is necessary for some reason, then perhaps the txtRecieve control has a private modifier which needs to be changed to internal or public. 如果不是这样,或者由于某种原因需要该静态修饰符,则txtRecieve控件可能具有一个私有修饰符,需要将其更改为内部或公共。 You should be able to use Visual Studio in debug mode to step into the InitializeComponent() method and see where txtRecieve is being instantiated. 您应该能够在调试模式下使用Visual Studio来进入InitializeComponent()方法,并查看txtRecieve的实例化位置。

Well, I believe that I needed to read more. 好吧,我相信我需要阅读更多内容。 This is how I solved the problem (if this is not the real solution, at least is working for now): 这就是我解决问题的方式(如果这不是真正的解决方案,至少现在可以使用):

  1. I moved the "SerialDataReceivedEventHandler" line before the _PuertoSerial.open(); 我将“ SerialDataReceivedEventHandler”行移到_PuertoSerial.open();之前。

  2. I followed the suggestions from this article: 我遵循了本文的建议:

https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(EHInvalidOperation.WinForms.IllegalCrossThreadCall);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(EHInvalidOperation.WinForms.IllegalCrossThreadCall);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLangang -csharp)&rd = true

So my funtions (one existings + a new one) look like this: 因此,我的功能(一个存在项+一个新存在项)如下所示:

    void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
        printReceivedText(_PuertoSerial.ReadExisting());
    }

    private void printReceivedText(string text)
    {
        if (this.txtSend.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(printReceivedText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.txtRecieve.AppendText(text);
            _PuertoSerial.DiscardInBuffer();
        }
    }

For now seems to be working fine. 目前看来工作正常。 The final testing will come when I connect another terminal and see the program interacting with each other. 当我连接另一个终端并看到程序相互交互时,将进行最终测试。

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

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