那菈麻木 2025-03-23 16:59 采纳率: 50%
浏览 65

C#,上位机接收串口数据,winform

C#,winform
关于串口接收数据更新和显示,遇到了两个问题,
1、我有多个页面,然后页面8有连接串口的设置,现在是遇到了checkBox1_CheckedChanged订阅事件SerialPort1_DataReceived失败,没法接收数据
2、之前直接把SerialPort1_DataReceived里面的代码放在checkBox1_CheckedChanged就可以接收到数据,但是会存在另一个问题,只有最开始接收的数据可以显示,更改串口发送的值,接收SetMsg框和数据不更新,只有关闭连接按键,再重新点击连接按键才会更新接收的数据
ps:modbus的ReadHoldingRegisters(),ReadInputRegisters()等等代码没贴
页面8

img

代码段

       using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Windows.Forms;
using Modbus.Device;  // 引入 NModbus 库
using Modbus.Message;
using Modbus.Data;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Markup;
using System.Threading;
using System.Diagnostics;


namespace WindowsFormsApp1
{

    public partial class UserControl8 : UserControl
    {
        public UserControl8()
        {
            InitializeComponent();
        }
        /// 私有ModbusRTU主站字段
        private static ModbusMaster master1;
       private static IModbusMaster master;



        /// 串口设置
        private void UserControl8_Load(object sender, EventArgs e)
        {
           //串口的COM口设置
            for (int i = 1; i < 20; i++)
            {
                comboBox1.Items.Add("COM" + i.ToString());
            }
           //默认串口值是检测到的串口
            string[] portname_com = SerialPort.GetPortNames();
            string com = String.Join(",", portname_com);
            if (portname_com.Length > 0)
            {
            // 将第一个可用的串口设置为 comboBox1 的默认选中项
                comboBox1.SelectedItem = portname_com[0];
            }
            //串口的波特率设置
            int[] numbers = { 1200, 2400, 4800, 9600, 14400 ,19200,38400,43000,57600,76800,115200,128000,230400,256000,460800,921600,1382400 };
            comboBox2.DataSource = numbers;
            comboBox2.DisplayMember = "ToString";
            //地址最小0,最大256
            nudSlaveID.Minimum = 0;
            nudSlaveID.Maximum = 256;
            //           nudStartAdr.Minimum = 0;
            //         nudLength.Minimum = 1;
            /* 
                        for (int i = 0; i < 256; i++)
                        {
                            string s = string.Format("0x{0:X2}", i);
                            nudSlaveID.Items.Add(s);
                        }
            */
            string[] numbers4 = { "RTU", "ASCII" };
            comboBox3.DataSource = numbers4;
            comboBox3.DisplayMember = "ToString";
        }
//串口接收数据
        private void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
               {
            switch (PublicValue.code)     {
                    case 0x03:
                        this.Invoke((MethodInvoker)delegate { SetMsg(ReadHoldingRegisters().ToList());});
                        PublicValue.UpdateResult0x03(ReadHoldingRegisters());
                        break; // "读取保持型寄存器"
                    case 0x04:
                        this.Invoke((MethodInvoker)delegate { SetMsg(ReadInputRegisters().ToList()); });
                        PublicValue.UpdateResult0x04(ReadInputRegisters());
                        break; // "读取输入寄存器"
                    case 0x10:
                        WriteArrayRegister();
                        break; // "写入多个寄存器"
                    case 0x11:
                        QuerySetupData();
                        break; // "查询设置数据"
                    case 0x12:
                        QueryHistory();
                        break; // "查询历史记录"
                    default:
                        break;
                }
           }
           catch (Exception ex)
           {
               this.Invoke((MethodInvoker)delegate
                {
                   MessageBox.Show($"接收数据时发生错误: {ex.Message}");
                });
            }
        }

//连接串口和订阅串口接收数据
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                label5.Text = PublicValue.code.ToString();
                try
                {
                    // 波特率
                    serialPort1.BaudRate = (int)comboBox2.SelectedItem;
                    // 数据位
                    serialPort1.DataBits = 8;
                    serialPort1.PortName = comboBox1.Text;
                    // 一个停止位
                    serialPort1.StopBits = System.IO.Ports.StopBits.One;
                    // 无奇偶校验位
                    serialPort1.Parity = System.IO.Ports.Parity.None;
                    serialPort1.ReadTimeout = 100;
                    master1 = ModbusSerialMaster.CreateRtu(serialPort1);
                    // 打开串口成功后,订阅 DataReceived 事件
                    serialPort1.DataReceived += SerialPort1_DataReceived;
                    serialPort1.Open();

                    if (!serialPort1.IsOpen)
                    {
                        MessageBox.Show("端口打开失败");
                        return;
                    }
                    else
                    {
                        MessageBox.Show("端口" + comboBox1.Text + "打开成功\r\n");
                        checkBox1.Text = "关闭";                     
                    }               
                }
                catch (Exception ex)
                {
                    serialPort1.Dispose();
                    MessageBox.Show($"打开串口时发生错误: {ex.Message}");
                }
            }
            else
            {
                try
                {
                    // 关闭串口前,取消订阅 DataReceived 事件
                    serialPort1.DataReceived -= SerialPort1_DataReceived;
                    serialPort1.Close();
                    checkBox1.Text = "打开";
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"关闭串口时发生错误: {ex.Message}");
                }
            }
        }

 
        /// <summary>
        /// 界面显示读取结果
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="result"></param>
        private void SetMsg<T>(List<T> result)
        {
            string msg = string.Empty;

            result.ForEach(m => msg += $"{m} ");

            rbxRWMsg.Text = msg.Trim();
        }
    }
}


  • 写回答

4条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-03-23 18:56
    关注
    让【宝码香车】来帮你解答,本回答参考DeepSeek编写,并整理提供,如果还有疑问可以点击头像关注私信或评论
    如果答案让您满意,请采纳、关注,非常感谢!

    你遇到的问题主要涉及两个方面:

    1. SerialPort1_DataReceived 事件未正确订阅:你提到在 checkBox1_CheckedChanged 中订阅 SerialPort1_DataReceived 事件失败,导致无法接收数据。
    2. 数据更新问题:你提到只有在重新连接串口时才能更新数据,而更改串口发送的值后,数据不会实时更新。

    问题1:SerialPort1_DataReceived 事件未正确订阅

    首先,确保 SerialPort1_DataReceived 事件在 checkBox1_CheckedChanged 中正确订阅。你已经在代码中正确地订阅了事件,但可能由于某些原因导致事件未触发。以下是一些可能的原因和解决方案:

    • 串口未正确打开:确保串口在订阅事件之前已经成功打开。你可以在 serialPort1.Open() 之后添加一个调试信息,确认串口是否成功打开。
    • 事件处理程序未正确绑定:确保 SerialPort1_DataReceived 方法的签名与 SerialDataReceivedEventHandler 委托匹配。

    问题2:数据更新问题

    你提到只有在重新连接串口时才能更新数据,这可能是因为 SerialPort1_DataReceived 事件中的数据处理逻辑存在问题。以下是一些可能的解决方案:

    • 确保 SetMsg 方法正确更新 UISetMsg 方法应该确保在 UI 线程上更新 rbxRWMsg.Text。你已经使用了 this.Invoke 来确保在 UI 线程上执行,这没有问题。
    • 检查数据接收逻辑:确保 ReadHoldingRegisters()ReadInputRegisters() 方法返回的数据是正确的,并且在每次接收到新数据时都会调用 SetMsg 方法。

    代码改进建议

    以下是对你代码的一些改进建议:

    1. 确保串口正确打开:在 checkBox1_CheckedChanged 中,确保串口在订阅事件之前已经成功打开。
    private void checkBox1_CheckedChanged(object sender, EventArgs e)
    {
        if (checkBox1.Checked)
        {
            label5.Text = PublicValue.code.ToString();
            try
            {
                // 波特率
                serialPort1.BaudRate = (int)comboBox2.SelectedItem;
                // 数据位
                serialPort1.DataBits = 8;
                serialPort1.PortName = comboBox1.Text;
                // 一个停止位
                serialPort1.StopBits = System.IO.Ports.StopBits.One;
                // 无奇偶校验位
                serialPort1.Parity = System.IO.Ports.Parity.None;
                serialPort1.ReadTimeout = 100;
    
                // 打开串口
                serialPort1.Open();
    
                if (!serialPort1.IsOpen)
                {
                    MessageBox.Show("端口打开失败");
                    return;
                }
                else
                {
                    MessageBox.Show("端口" + comboBox1.Text + "打开成功\r\n");
                    checkBox1.Text = "关闭";
    
                    // 创建 Modbus 主站
                    master1 = ModbusSerialMaster.CreateRtu(serialPort1);
    
                    // 打开串口成功后,订阅 DataReceived 事件
                    serialPort1.DataReceived += SerialPort1_DataReceived;
                }
            }
            catch (Exception ex)
            {
                serialPort1.Dispose();
                MessageBox.Show($"打开串口时发生错误: {ex.Message}");
            }
        }
        else
        {
            try
            {
                // 关闭串口前,取消订阅 DataReceived 事件
                serialPort1.DataReceived -= SerialPort1_DataReceived;
                serialPort1.Close();
                checkBox1.Text = "打开";
            }
            catch (Exception ex)
            {
                MessageBox.Show($"关闭串口时发生错误: {ex.Message}");
            }
        }
    }
    
    1. 确保 SetMsg 方法正确更新 UI:你已经使用了 this.Invoke 来确保在 UI 线程上执行 SetMsg 方法,这没有问题。
    private void SetMsg<T>(List<T> result)
    {
        string msg = string.Empty;
    
        result.ForEach(m => msg += $"{m} ");
    
        rbxRWMsg.Text = msg.Trim();
    }
    
    1. 检查数据接收逻辑:确保 ReadHoldingRegisters()ReadInputRegisters() 方法返回的数据是正确的,并且在每次接收到新数据时都会调用 SetMsg 方法。
    private void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        try
        {
            switch (PublicValue.code)
            {
                case 0x03:
                    this.Invoke((MethodInvoker)delegate { SetMsg(ReadHoldingRegisters().ToList()); });
                    PublicValue.UpdateResult0x03(ReadHoldingRegisters());
                    break; // "读取保持型寄存器"
                case 0x04:
                    this.Invoke((MethodInvoker)delegate { SetMsg(ReadInputRegisters().ToList()); });
                    PublicValue.UpdateResult0x04(ReadInputRegisters());
                    break; // "读取输入寄存器"
                case 0x10:
                    WriteArrayRegister();
                    break; // "写入多个寄存器"
                case 0x11:
                    QuerySetupData();
                    break; // "查询设置数据"
                case 0x12:
                    QueryHistory();
                    break; // "查询历史记录"
                default:
                    break;
            }
        }
        catch (Exception ex)
        {
            this.Invoke((MethodInvoker)delegate
            {
                MessageBox.Show($"接收数据时发生错误: {ex.Message}");
            });
        }
    }
    

    总结

    通过以上改进,你应该能够解决 SerialPort1_DataReceived 事件未正确订阅和数据更新问题。确保串口正确打开,并且在每次接收到新数据时都能正确更新 UI。如果问题仍然存在,建议进一步调试 ReadHoldingRegisters()ReadInputRegisters() 方法,确保它们返回的数据是正确的。

    评论

报告相同问题?

问题事件

  • 修改了问题 3月23日
  • 创建了问题 3月23日