串口通信助手优化

本文介绍了如何在C#中实现串口通信的封装,包括串口读写、串口扫描和UI实时更新功能。针对串口数据错乱问题,提出了使用线程锁的解决方案。同时,探讨了两种UI实时刷新方法:定时器和委托+timer,并展示了异步处理串口数据的代码示例。此外,还提供了模拟串口测试的步骤。
摘要由CSDN通过智能技术生成

目录

UI布局

1.串口的读写

2.串口扫描函数

3.实时刷新UI

3.1定时器刷新

3.2委托+timer类

背景:在已有基础上将串口读写数据封装到一个类或者方法里,添加UI实时更新数据的功能,添加可设置参数的功能,添加可查询状态的功能

UI布局

引用Sunny UI包对UI界面重新绘制

Sunny UI官网

 串口的读写

        private bool WriteAndReadPort(string cmd, out string result)
        {
            //读写
            result = null;
            try
            {
                if (serialPort1?.IsOpen == true)
                {
                    lock (serialLock)
                    {
                        serialPort1.DiscardInBuffer();
                        serialPort1.DiscardOutBuffer();
                        serialPort1.Write(cmd);
                        Thread.Sleep(200);
                        result = serialPort1.ReadExisting();
                    }
                    return result?.Length > 0;
                }
                return false;
            }
            catch (Exception)
            {
                result = null;
                return false;
            }
        }

串口扫描

        private void SearchPortToComBox(ComboBox MyBox,SerialPort MyPort) 
        {
            //扫描 
            MyBox.DataSource = SerialPort.GetPortNames();           
        }

所遇问题及解决办法

因为是对串口返回的数据是以ReadExisting()来进行读取;

官网解释:

在编码的基础上,读取 SerialPort 对象的流和输入缓冲区中所有立即可用的字节。

返回 String 

  SerialPort 对象的流和输入缓冲区的内容。

1.当下位机返回的数据量大时,接受到的数据可能会发生错乱。比如A发送的指令本应该是返回给A的,结果因为B也做了发送指令的操作,此时原本是返回给A的数据被B接收到。

解决办法:对串口读写加上线程锁,这样就能确保每次的读写不会错乱

2.或是有特殊的帧头 帧尾,这时就需要对数据进行一个拆包的操作

(略...)

实时刷新UI

3.1定时器刷新(不推荐)

3.2委托+timer类(测试)

 private void Form1_Load(object sender, System.EventArgs e)
        {
            Thread t = new Thread(new ThreadStart(GetData));
            t.IsBackground = true;  //后台
            t.Start();
        }

        private void GetData()
        {
            var timer = new System.Timers.Timer();
            timer.Interval = 1000;
            timer.Enabled = true;
            timer.AutoReset = true;//设置是执行一次(false)还是一直执行(true);  
            timer.Start();
            timer.Elapsed += (o , e) =>
            {
                SetData();
                ShowMessage(string.Format("更新时间:" + DateTime.Now));
            };
        }

        // 声明委托
        private delegate void SetDataDelegate();
        private void SetData()
        {
            if (this.InvokeRequired)    //当InvokeRequired为true时,说明在非创建线程访问当前UI控件
            {
                this.Invoke(new SetDataDelegate(SetData));
            }
            else
            {
                LB1.Text = string.Format("更新时间:" + DateTime.Now);
            }
        }
        
        
        //声明委托
        private delegate void ShowMessageDelegate(string message);

        private void ShowMessage(string message)
        {
            if (this.InvokeRequired)
            {
                ShowMessageDelegate showMessageDelegate = ShowMessage;
                this.Invoke(showMessageDelegate, new object[] { message });
            }
            else
            {
                TB1.Text = message;
            }
        }

3.3异步

异步部分代码:

            Serial_OC();
            if (watchTaskRunning)
            {
                Source.Cancel();
                btTemp.BeginInvoke(new Action(() => { btTemp.Text = "Temp"; }));
                watchTaskRunning = false;
            }
            else
            {
                btTemp.BeginInvoke(new Action(() => { btTemp.Text = "Stop"; }));
                Source = new CancellationTokenSource();
                var token = Source.Token;
                Task.Run(() =>
                {
                    while (true)
                    {
                        if (token.IsCancellationRequested)
                        {
                            break;
                        }
                        else
                        {
                            if (WriteAndReadPort("Temp\r", out string res))
                            {                               
                                if (double.TryParse(res, out double tempRes))
                                {
                                    //查询控制板温度
                                    tb_Temp.BeginInvoke(new Action(() => { tb_Temp.Text = tempRes.ToString() + "℃"; }));
                                    Thread.Sleep(1000);
                                }
                            }
                        }
                    }
                }, token);
                watchTaskRunning = true;
            }

 最后F5跑起来,没有调试的机器,可以下一个有人虚拟串口

安装完毕后新建一个串口, 这里我们选择第一个

 

打开我们之前用Socket+WPF做好的模拟下位机的程序

 

 地址---Win+R---ipconfig---自己本机IP,端口就是创建出来模拟串口的的端口

测试上图:

选择COM1——打开串口:

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值