使用C#写一个简易的串口助手

文章目录

    • 概要
    • 整体架构流程
    • 1. Winform如何构建
    • 2. 串口获取及打开
    • 3. 如何注册事件
    • 4. 数据发送和清除
    • 5. 存储数据
    • 6. 总结

概要

串口助手是一个很好熟悉串口上位机的小项目,其中只包含对串口的应用,而不包含太多复杂业务逻辑,本文详细介绍了使用Visual Stuido2010创建一个串口通讯的上位机界面,包括使用groupBox、comboBox、label、button、checkBox和textbox,实现串口参数的设置,串口打开和关闭,数据发送和接收,数据16进制显示和数据清除等功能。

整体架构流程

在这里插入图片描述

该图像为串口通讯上位机的Form

1. Winform如何构建

1) 打开vs创建项目,选择Windows窗体应用程序,点击确定。
在这里插入图片描述

2) 选择视图中工具箱,按照需求在工具箱中选择。
在这里插入图片描述

3) 点击工具,例如groupBox、comboBox等,右键打开属性界面,在Text中改写显示在界面的文字。

2. 串口获取及打开

1)串口获取:添加方法text_Serial_port()来获取电脑的串口,然后在点击Form1中的空白处,跳转到Form1_load函数,在此函数中调用text_Serial_port();

private void text_Serial_port()
        {
            string[] ports = System.IO.Ports.SerialPort.GetPortNames(); //获取可用的串口
            for (int i = 0; i < ports.Length; i++)
            {
                comboBox1.Items.Add(ports[i]);
            }
            comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//如果里面有数据,显示第0个
        }

2)串口打开:双击“打开串口”这个按钮,跳转到button1_Click函数,在其中添加串口打开代码。

//打开串口
        private void button1_Click(object sender, EventArgs e)
        {
            if (button1.Text == "打开串口")
            {
                try
                {
                    serialPort1.PortName = comboBox1.Text;//获取要打开的串口
                    serialPort1.BaudRate = int.Parse(comboBox4.Text);//获取波特率 
                    serialPort1.DataBits = int.Parse(comboBox3.Text);//获取数据位
                    //设置停止位
                    if (comboBox2.Text == "1")
                    {
                        serialPort1.StopBits = StopBits.One;
                    }
                    else if (comboBox2.Text == "1.5")
                    {
                        serialPort1.StopBits = StopBits.OnePointFive;
                    }
                    else if (comboBox2.Text == "2")
                    {
                        serialPort1.StopBits = StopBits.Two;
                    }
                    //设置奇偶校验
                    if (comboBox5.Text == "无")
                    {
                        serialPort1.Parity = Parity.None;
                    }
                    else if (comboBox5.Text == "奇校验")
                    {
                        serialPort1.Parity = Parity.Odd;
                    }
                    else if (comboBox5.Text == "偶校验")
                    {
                        serialPort1.Parity = Parity.Even;
                    }
                    serialPort1.Open();//打开串口
                    button1.Text = "关闭串口";
                    //注册DataReceived事件处理
                    serialPort1.DataReceived += serialPort1_DataReceived;
                }
                catch (Exception err)
                {
                    MessageBox.Show("打开失败" + err.ToString(), "提示!");
                }
            }
            else
            {
                //关闭串口
                try
                {
                        serialPort1.Close();//关闭串口
                }
                catch (Exception) { }
                button1.Text = "打开串口"; //按钮显示打开
            }
        }

3. 如何注册事件

1)在工具箱中输入serialPort,将其拖拽到界面中。
在这里插入图片描述
2)在C#中,DataReceived事件通常是串口通信相关的类中使用的事件。当接收到数据时,会触发DataReceived事件,从而通知应用程序有新的数据可用。通常的步骤为:

数据到达:当串口接收到新的数据时,会触发底层的数据到达事件。
数据处理:接收到的数据会被处理和解析,以确保其完整性和正确性。
触发DataReceived事件:一旦数据被处理完毕,就会触发DataReceived事件,通知应用程序有新的数据可用。
事件处理:应用程序可以为DataReceived事件添加处理程序,以处理接收到的数据并进行相应的操作。

使用serialPort1_DataReceived处理接收到的数据,并且在serialPort1中添加事件。
在这里插入图片描述

//16进制显示字符串
        private string byteToHexstr(byte[] buff)
        {
            string str = "";
            try
            {
                if (buff != null)
                {

                    for (int i = 0; i < buff.Length; i++)
                    {
                        str += buff[i].ToString("x2");//转化为小写的16进制
                        str += " ";//两个之间用空格
                    }
                        return str;
                }
            }
            catch 
            {
                return str;
            }
            return str;
        }

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int len = serialPort1.BytesToRead; //获取可以读取的字节数
 
            byte[] buff = new byte[len];
            serialPort1.Read(buff, 0, len);//把数据读取到数组中
            string reslut = Encoding.Default.GetString(buff);
            //将byte值根据ASCII值转为string
            Invoke((new Action(() =>
            {
                if (checkBox1.Checked)//转化为16进制显示
                {
                    textBox1.AppendText(" " + byteToHexstr(buff));
                    //将接收到的数据存储到builder3中
                    builder3.Append(byteToHexstr(buff));
                }
                else
                {
                    textBox1.AppendText(" " + reslut);
                    builder3.Append(result);
                }
            }
            )));
        }

4. 数据发送和清除

1)字符串转化为16进制的代码,作为信息发送的子函数。

//字符串转为16进制
        private byte[] strToHexbytes(string str)
        {
            str = str.Replace(" ", "");//清除空格
            byte[] buff;
            if ((str.Length % 2) != 0)
            {
                buff = new byte[(str.Length + 1) / 2];
                try
                {
                    for (int i = 0; i < buff.Length; i++)
                    {
                        buff[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);
                    }
                    buff[buff.Length - 1] = Convert.ToByte(str.Substring(str.Length - 1, 1).PadLeft(2, '0'), 16);
                    return buff;
                }
                catch 
                {
                    MessageBox.Show("含有非16进制的字符""提示");
                    return null;
                }
            }
            else
            {
                buff = new byte[str.Length / 2];
                try
                {
                    for (int i = 0; i < buff.Length; i++)
                    {
                        buff[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);
                    }
                }
                catch 
                {
                    {
                        MessageBox.Show("含有非16进制的字符""提示");
                        return null;
                    }
                }
            }

            return buff;
        }

2)首先编写发送数据代码send_,双击“发送信息”这个按钮,跳转到button3_Click函数,在其中调用发送数据代码send_。

string data_;
        //发送数据 
        private void send_()
        {
            data_ = textBox2.Text.ToString();
                try
                {
                    if (data_.Length != 0)
                    {
                        data_ += " ";
                        if (checkBox2.Checked) //16进制发送
                        {
                     serialPort1.Write(strToHexbytes(data_), 0, strToHexbytes(data_).Length);

                        }
                        else
                        {
                            serialPort1.Write(data_);
                        }
                    }
                }
                catch (Exception) { }         
        }
private void button3_Click(object sender, EventArgs e)
           {
            Task task = Task.Factory.StartNew(() =>
            {
                //发送代码
                send_();
            });
            }

3)双击“清除信息”按钮button4打开button4_Click函数,将发送窗口清零,双击“清除数据”按钮button2打开button2_Click_1函数,将接收窗口清零。

private void button4_Click(object sender, EventArgs e)
        {
            textBox2.Clear();
        }
private void button2_Click_1(object sender, EventArgs e)
        {
            textBox1.Clear();
        }

5. 存储数据

主要指的是将通过串口通讯接收到的数据存储到TXT文档中,双击Form1中的“保存数据”按钮,具体实现代码如下,主要包括设置保存路径,然后定义builder,将数据存储在builder中,然后再将其写入到保存路径下的TXT文档中。
在这里插入图片描述

private void 保存数据_Click(object sender, EventArgs e)
        {
           
            if (保存数据.Text == "保存数据")
            {
                button2.Enabled = false;
                保存数据.Text = "停止存储";
                //FileStream用于文件的读写操作,这里设置一个要写入的文件地址,同时使用FileStream时,要在头文件中添加using System.IO;
                fs = new FileStream("C:\\Users\\Administrator.USER-20190520UY\\Desktop\\csh\\winform2024910\\data\\" + "Original编号" + "时间" + DateTime.Now.ToString("yyyyMMdd-hhmm") + ".txt", FileMode.Create);
                //StreamWriter用于将数据写入流的类‌
                sw = new StreamWriter(fs);
            }
            else
            {
                button2.Enabled = true;
                保存数据.Text = "保存数据";
                //close作用是释放与StreamWriter和FileStream相关联的系统资源。
                sw.Close();
                fs.Close();
            }
            if (button1.Text == "关闭串口")
                serialPort1.Open();
        }

使用定时器进行数据存储,相比在代码中书写UI变化,在定时器中书写UI变化处理数据更佳,当UI刷新数据比较多和比较快的时候,窗口不易被卡死。设置控件可用Enabled为True,设置时间间隔Interval为200ms。

//time控件刷新
        //*********************************************
         private void timer1_Tick(object sender, EventArgs e)
         {
             if (button1.Text == "关闭串口")
             {
                 MemoryTxt();//存储进Txt的更新
                 RenewUI();//数据更新时对应更新UI界面
             }
         }
//存储进Txt
//为减小IO的开销而设计,十组数据再存储进txt,若数据发送频率提高,则酌情增加textBox1行数
        private void MemoryTxt()
        {
            if (保存数据.Text == "停止存储")
            {
                sw.Write(builder3);
                //下面Flush的作用:当你向一个输出流写入数据时,数据可能会被缓存在内存中,而不是立即写入目标设备。调用Flush方法可以确保缓冲区中的所有数据都被写入目标设备,从而确保数据的完整性。	
                sw.Flush();
                builder3.Clear();
            }
        }
//数据更新时对应更新UI界面
        private void RenewUI()
        {

            //textbox1响应
            if ((builder1.Length > 5))
            {
                textBox1.AppendText(builder1.ToString());
                builder1.Clear();
            }
        }

6. 总结

总结:

  1. 串口输入缓冲区获得新数据后,会以DataReceived事件通知SerialPort对象,可以在此时读取串口数据。

  2. 发送和接收数据包括普通数据发送接收和16进制发送接收,其中16进制的发送和接收通过byte类型的数组将字符串转化为16进制buff[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);

  3. 在串口通讯中,尽量使用定时器,用于在指定的时间间隔内触发事件或执行代码。它可以用于执行定期的任务,如更新UI、计时等操作。,避免因为数据过多,每个数据来一次,界面就需要刷新一次,这样刷新过多可能会导致窗口卡死。

到此,一个简易的上位机就做好了,有什么不足还请指出,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值