具有多个事件的连续串行端口读取

Posted

技术标签:

【中文标题】具有多个事件的连续串行端口读取【英文标题】:Continuous serial port read with multiple events 【发布时间】:2019-03-24 13:27:18 【问题描述】:

我是 C# 新手,正在寻找一些关于我一直试图在我的 Windows 窗体应用程序中解决的问题的建议。

我有一个应用程序需要连续读取通过连接的串行端口返回到程序的数据。我有通过用户打开和关闭端口的按钮。我在配置“DataReceived”事件处理程序以读取传入数据并将其显示在应用程序的文本框中时遇到问题。

我收到此错误:“跨线程操作无效:控件'textBox4'从创建它的线程以外的线程访问。”我看到这是一个线程错误,但我无法弄清楚我的问题。

namespace Program

    public partial class Form1 : Form
    
        public Form1()
        
            InitializeComponent();
            getAvailabePorts();
        

        private void getAvailabePorts()
        
            String[] ports = SerialPort.GetPortNames();
            comboBox1.Items.AddRange(ports);
        
        public void button1_Click(object sender, EventArgs e)
        
            try
            
                if (comboBox1.Text == "" || comboBox2.Text == "")
                
                    textBox4.Text = "Please select port settings";
                
                else
                

                    serialPort1.PortName = comboBox1.Text;
                    serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);
                    serialPort1.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);

                    serialPort1.Open();
                
            
            catch (UnauthorizedAccessException)
            
                textBox4.Text = "Unauthorized Access";
            

            public void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
            
                SerialPort sp = (SerialPort)sender;
                textBox4.Text = sp.ReadExisting() + "\n";
            

            private void button2_Click(object sender, EventArgs e)
            
                serialPort1.Close();

                textBox4.Clear();
            

        
    

【问题讨论】:

串行端口的事件处理程序无法更新表单 UI,因为它在单独的线程中运行。您需要使用委托。阅读此内容,了解如何操作...***.com/a/17810763/3516555 【参考方案1】:

首先,欢迎。

在“大”问题(编组数据)之前,让我警告您——串行端口很棘手。例如,您对“ReadExisting”的调用可能不会返回您所期望的 - 将返回串行端口缓冲区中的任何内容当时,但可能会出现更多内容,这将覆盖已经存在的内容你的文本框。所以你可能想追加数据你的文本框。

现在才是真正的问题。正如评论者所提到的,您不能直接从另一个线程发布数据到 UI 线程。不知不觉中,串口创建了一个新线程来接收数据。

您可以通过如下修改接收器代码来直接处理此问题:

    public void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    
        SerialPort sp = (SerialPort) sender;
        var dataRcvd = sp.ReadExisting();
        object[] dataArray = new object[1];
        dataArray[0] = dataRcvd;
        BeginInvoke( new postDataDelegate( postData), dataArray );
    

    private delegate void postDataDelegate( string d );
    private void postData( string d) 
    
        textBox4.Text = d;
    

这会将数据“编组”到 UI 线程,以便可以使用它。有很多方法可以做到这一点(并且在 WPF 与 Winforms 中的完成方式之间存在许多差异,因此请注意这一点)。我希望这能说明这一点。

另外——不需要公开 DataReceived 方法——它可以很好地私有化。

【讨论】:

或者,简而言之,BeginInvoke(new MethodInvoker(() => textBox4.AppendText(dataRcvd); ));。为什么要装箱/拆箱?没有必要。 dataArray 可以是 string[] dataArray = new string[1];。但这不是必需的。 好点。我必须承认,在我测试我的解决方案之前,我并没有真正使用过 WinForms,所以我不确定。另外,作为 C# 新手,我记得我发现 lambda 表达式语法很神秘,所以我希望能更清楚到底发生了什么。您的解决方案绝对更清洁。 我已经能够从 bobwki 和 Jimi 实现这两种解决方案!使用代表对我来说仍然有点困惑,但这些帮助我更好地理解它。谢谢! 很高兴听到这个消息!除非有充分的理由坚持使用 WinForms,否则我建议你切换到 WPF 或 UWP(仅适用于 Win10)开发环境——WinForms 现在有点老了,微软已经在继续前进了。

以上是关于具有多个事件的连续串行端口读取的主要内容,如果未能解决你的问题,请参考以下文章

连续性多个 WSASend() io 完成端口

使用 pyserial 将串行消息导出到文本文件

1200modbus连续读取多个数据

创建由多列和连续日期分区的序列

请教个实际问题,C# serialPort在读取数据有时候会连续触发DataReceived事件

matlab 连续读取多个文件