VS上位机开发——串口助手

VS上位机开发——串口助手

第一次接触上位机的开发,单纯是为了玩一下,浅度学习,参考了一下其他文章,做了一个简单的串口助手,算是迈出了第一步。写博客记录一下学习的过程。

一、新建项目

第1步:创建一个Window窗体应用(.NET Framework)
我安装的是vs2019版本。
在这里插入图片描述

第2步:配置项目
提示:框架要选.NET Framework 4以上,如果没有,先确认项目选的是不是Window窗体应用,再确认是否安装NET Framework。
在这里插入图片描述

二、控件布局

我们先利用控件把串口助手的界面搭建出来。
提示:我们用到的控件都在工具箱里面。
在这里插入图片描述

我这里主要用了以下几个控件:

提示:控件名称是一个比较关键的参数,因为后面的代码要根据名称来写。

控件类型 控件名称 Text 说明
TextBox TextBox1 TextBox1 接收显示窗口
TextBox TextBox2 TextBox2 发送输入窗口
label label1 端口号 文本提示
label label2 波特率 文本提示
comboBox comboBox1 comboBox1 端口号下拉菜单
comboBox comboBox2 comboBox2 波特率下拉菜单
button button1 打开串口 打开串口按键
button button2 清除接收 清除接收按键
button button3 发送 发送按键
checkBox checkBox1 hex发送 切换发送格式
checkBox checkBox2 hex接收 切换接收格式
serialPort serialPort1 serialPort1 串口通信控件
Time Tiime1 Tiime1 定时器,用于定时刷新端口

先把控件从工具箱里面拉出来,调整好大小和布局。
提示:TextBox要自由调整窗口大小的话需要把属性里面的MultiLine设置为True。
在这里插入图片描述

修改控件属性里面的Text,串口助手的界面就出来了。
在这里插入图片描述
再添加serialPort和Time控件,这两个是隐藏的控件,在窗口下方,实际运行的时候是看不见的。
在这里插入图片描述
选择波特率对应的comboBox控件,在Items属性里面添加常用的波特率。
在这里插入图片描述

三、编写程序

提示:可以双击控件窗口打开代码,也可以选中From,右键,选中查看代码
在这里插入图片描述

在实际应用中,最常用到的代码在Form1.cs和Form1.Designer.cs两个文件中。
注:Form1是新建窗体默认的名称,实际使用也可能不是这个名字。

Form1.cs是应用部分的代码,也就是我后面要编写的代码所在的文件。
Form1.Designer.cs里面存放各种控件的参数定义,在我们双击某一个控件的时候,实际上这个文件会在相应的生成一行代码,意思是在这个控件里面添加一个事件。

1、端口更新函数

这个函数是自定义的,需要自己添加进去

/* 新增自定义函数:更新可用串口 */
private void Updata_Serialport_Name(ComboBox MycomboBox)
{
    
    
	string[] ArryPort;                               // 定义字符串数组,数组名为 ArryPort
    ArryPort = SerialPort.GetPortNames();            // SerialPort.GetPortNames()函数功能为获取计算机所有可用串口,以字符串数组形式输出
    MycomboBox.Items.Clear();                        // 清除当前组合框下拉菜单内容                  
    for (int i = 0; i < ArryPort.Length; i++)
    {
    
    
        MycomboBox.Items.Add(ArryPort[i]);           // 将所有的可用串口号添加到端口对应的组合框中
    }
}

引用命名空间System.IO.Ports:
因为上面调用了SerialPort.GetPortNames()函数,需要引用这个命名空间才能使用

using System.IO.Ports;

在这里插入图片描述

2、启动窗口加载函数

双击设计界面窗口的空白区域,会自动生成一个Form1_Load空函数。
在Form1_Load函数里面添加以下代码:

Updata_Serialport_Name(comboBox1); // 调用更新可用串口函数,comboBox1为端口号组合框的名称

在默认启动函数里添加以下代码:

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;//设置该属性 为false

在这里插入图片描述
提示:函数名不一定是Form1,这个函数名和自己的使用的窗体名称是一致的

3、"打开串口"按键回调函数

双击“打开串口”按键,会自动生成一个空函数。
在函数里面添加以下代码:

if (button1.Text == "打开串口")                                  // 如果当前是串口设备是关闭状态
{
    
    
    try                                                          // try 是尝试部分,如果尝试过程中出现问题,进入catch部分,执行错误处理代码  
    {
    
    
        serialPort1.PortName = comboBox1.Text;                   // 将串口设备的串口号属性设置为comboBox1复选框中选择的串口号
        serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);  // 将串口设备的波特率属性设置为comboBox2复选框中选择的波特率
        serialPort1.Open();                                      // 打开串口,如果打开了继续向下执行,如果失败了,跳转至catch部分
        comboBox1.Enabled = false;                               // 串口已打开,将comboBox1设置为不可操作
        comboBox2.Enabled = false;                               // 串口已打开,将comboBox2设置为不可操作
        button1.BackColor = Color.Red;                           // 将串口开关按键的颜色,改为红色
        button1.Text = "关闭串口";                               // 将串口开关按键的文字改为“关闭串口”
    }
    catch
    {
    
    
        MessageBox.Show("打开串口失败,请检查串口", "错误");     // 弹出错误对话框
    }
}
else                                             // 如果当前串口设备是打开状态
{
    
    
    try
    {
    
    
        serialPort1.Close();                     // 关闭串口
        comboBox1.Enabled = true;                // 串口已关闭,将comboBox1设置为可操作
        comboBox2.Enabled = true;                // 串口已关闭,将comboBox2设置为可操作
        button1.BackColor = Color.Lime;          // 将串口开关按键的颜色,改为青绿色
        button1.Text = "打开串口";               // 将串口开关按键的文字改为“打开串口”
    }
    catch
    {
    
    
        MessageBox.Show("关闭串口失败,请检查串口", "错误");   // 弹出错误对话框
    }
}

4、"清除接收"按键回调函数

双击“清除接收”按键,会自动生成一个空函数。
在函数里面添加以下代码:

textBox1.Text = "";

5、"发送"按键回调函数

双击“发送”按键,会自动生成一个空函数。
在函数里面添加以下代码:

if (serialPort1.IsOpen)            // 如果串口设备已经打开了
{
    
    
    if (!checkBox1.Checked)        // 如果是以字符的形式发送数据
    {
    
    
        char[] str = new char[1];  // 定义一个字符数组,只有一位

        try
        {
    
    
            for (int i = 0; i < textBox2.Text.Length; i++)
            {
    
    
                str[0] = Convert.ToChar(textBox2.Text.Substring(i, 1));  // 取待发送文本框中的第i个字符
                serialPort1.Write(str, 0, 1);                            // 写入串口设备进行发送
            }
        }
        catch
        {
    
    
            MessageBox.Show("串口字符写入错误!", "错误");   // 弹出发送错误对话框
            serialPort1.Close();                          // 关闭串口
            button1.BackColor = Color.Lime;               // 将串口开关按键的颜色,改为青绿色
            button1.Text = "打开串口";                    // 将串口开关按键的文字改为“打开串口”
        }
    }
    else                                                  // 如果以数值的形式发送
    {
    
    
        byte[] Data = new byte[1];                        // 定义一个byte类型数据,相当于C语言的unsigned char类型
        int flag = 0;                                     // 定义一个标志,标志这是第几位
        try
        {
    
    
            for (int i = 0; i < textBox2.Text.Length; i++)
            {
    
    
                if (textBox2.Text.Substring(i, 1) == " " && flag == 0)                // 如果是第一位,并且为空字符
                {
    
    
                    continue;
                }

                if (textBox2.Text.Substring(i, 1) != " " && flag == 0)                // 如果是第一位,但不为空字符
                {
    
    
                    flag = 1;                                                         // 标志转到第二位数据去
                    if (i == textBox2.Text.Length - 1)                                // 如果这是文本框字符串的最后一个字符
                    {
    
    
                        Data[0] = Convert.ToByte(textBox2.Text.Substring(i, 1), 16);  // 转化为byte类型数据,以16进制显示
                        serialPort1.Write(Data, 0, 1);                                // 通过串口发送
                        flag = 0;                                                     // 标志回到第一位数据去
                    }
                    continue;
                }
                else if (textBox2.Text.Substring(i, 1) == " " && flag == 1)           // 如果是第二位,且第二位字符为空
                {
    
    
                    Data[0] = Convert.ToByte(textBox2.Text.Substring(i - 1, 1), 16);  // 只将第一位字符转化为byte类型数据,以十六进制显示
                    serialPort1.Write(Data, 0, 1);                                    // 通过串口发送
                    flag = 0;                                                         // 标志回到第一位数据去
                    continue;
                }
                else if (textBox2.Text.Substring(i, 1) != " " && flag == 1)           // 如果是第二位字符,且第一位字符不为空
                {
    
    
                    Data[0] = Convert.ToByte(textBox2.Text.Substring(i - 1, 2), 16);  // 将第一,二位字符转化为byte类型数据,以十六进制显示
                    serialPort1.Write(Data, 0, 1);                                    // 通过串口发送
                    flag = 0;                                                         // 标志回到第一位数据去
                    continue;
                }
            }
        }
        catch
        {
    
    
            MessageBox.Show("串口数值写入错误!", "错误");
            serialPort1.Close();
            button1.BackColor = Color.Lime;   // 将串口开关按键的颜色,改为青绿色
            button1.Text = "打开串口";        // 将串口开关按键的文字改为  “打开串口”
        }
    }
}

6、串口接收函数

点击serialPort控件,在该控件的事件里面有一个DataReceived事件,双击它会生成一个数据接收的空函数
在这里插入图片描述

在函数里面添加以下代码:

if (!checkBox2.Checked)                        // 如果以字符串形式读取
{
    
    
    string str = serialPort1.ReadExisting();   // 读取串口接收缓冲区字符串

    textBox1.AppendText(str + "");             // 在接收文本框中进行显示
}
else                                           // 以数值形式读取
{
    
    
    int length = serialPort1.BytesToRead;      // 读取串口接收缓冲区字节数

    byte[] data = new byte[length];            // 定义相同字节的数组

    serialPort1.Read(data, 0, length);         // 串口读取缓冲区数据到数组中

    for (int i = 0; i < length; i++)
    {
    
    
        string str = Convert.ToString(data[i], 16).ToUpper();                          // 将数据转换为字符串格式
        textBox1.AppendText("0X" + (str.Length == 1 ? "0" + str + " " : str + " "));   // 添加到串口接收文本框中
    }
}

7、定时器中断回调函数

在timer控件的属性里面打开使能,设置定时时间为500ms
在这里插入图片描述
双击timer控件,会自动生成一个空函数
在函数里面添加以下代码:

Updata_Serialport_Name(comboBox1);   // 定时刷新可用串口,可以保证在程序启动之后连接的设备也能被检测到

最后再贴一个完整的代码:
提示:不能直接跳过前面的步骤直接把完整的代码拷贝过去,因为前面双击控件的操作不仅仅是生成空函数,也会在Designer里面添加对应的事件,如果直接拷贝就不会产生事件。当然,如果非要这样操作也不是不行,只要在Form1.Designer.cs文件里面把每个控件对应的事件加上即可。

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 串口测试工具
{
    
    
    public partial class Form1 : Form
    {
    
    
        public Form1()
        {
    
    
            InitializeComponent();
            System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;//设置该属性 为false
        }

        /* 新增自定义函数:更新可用串口 */
        private void Updata_Serialport_Name(ComboBox MycomboBox)
        {
    
    
            string[] ArryPort;                               // 定义字符串数组,数组名为 ArryPort
            ArryPort = SerialPort.GetPortNames();            // SerialPort.GetPortNames()函数功能为获取计算机所有可用串口,以字符串数组形式输出
            MycomboBox.Items.Clear();                        // 清除当前组合框下拉菜单内容                  
            for (int i = 0; i < ArryPort.Length; i++)
            {
    
    
                MycomboBox.Items.Add(ArryPort[i]);           // 将所有的可用串口号添加到端口对应的组合框中
            }
        }

        /* 启动窗口加载函数 */
        private void Form1_Load(object sender, EventArgs e)
        {
    
    
            Updata_Serialport_Name(comboBox1);               // 调用更新可用串口函数,comboBox1为端口号组合框的名称
        }

        /* "打开串口"按键回调函数 */
        private void button1_Click(object sender, EventArgs e)
        {
    
    
            if (button1.Text == "打开串口")                                  // 如果当前是串口设备是关闭状态
            {
    
    
                try                                                          // try 是尝试部分,如果尝试过程中出现问题,进入catch部分,执行错误处理代码  
                {
    
    
                    serialPort1.PortName = comboBox1.Text;                   // 将串口设备的串口号属性设置为comboBox1复选框中选择的串口号
                    serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);  // 将串口设备的波特率属性设置为comboBox2复选框中选择的波特率
                    serialPort1.Open();                                      // 打开串口,如果打开了继续向下执行,如果失败了,跳转至catch部分
                    comboBox1.Enabled = false;                               // 串口已打开,将comboBox1设置为不可操作
                    comboBox2.Enabled = false;                               // 串口已打开,将comboBox2设置为不可操作
                    button1.BackColor = Color.Red;                           // 将串口开关按键的颜色,改为红色
                    button1.Text = "关闭串口";                               // 将串口开关按键的文字改为“关闭串口”
                }
                catch
                {
    
    
                    MessageBox.Show("打开串口失败,请检查串口", "错误");     // 弹出错误对话框
                }
            }
            else                                             // 如果当前串口设备是打开状态
            {
    
    
                try
                {
    
    
                    serialPort1.Close();                     // 关闭串口
                    comboBox1.Enabled = true;                // 串口已关闭,将comboBox1设置为可操作
                    comboBox2.Enabled = true;                // 串口已关闭,将comboBox2设置为可操作
                    button1.BackColor = Color.Lime;          // 将串口开关按键的颜色,改为青绿色
                    button1.Text = "打开串口";               // 将串口开关按键的文字改为“打开串口”
                }
                catch
                {
    
    
                    MessageBox.Show("关闭串口失败,请检查串口", "错误");   // 弹出错误对话框
                }
            }
        }

        /* "清除接收"按键回调函数 */
        private void button2_Click(object sender, EventArgs e)
        {
    
    
            textBox1.Text = "";
        }

        /* "发送"按键回调函数 */
        private void button3_Click(object sender, EventArgs e)
        {
    
    
            if (serialPort1.IsOpen)            // 如果串口设备已经打开了
            {
    
    
                if (!checkBox1.Checked)        // 如果是以字符的形式发送数据
                {
    
    
                    char[] str = new char[1];  // 定义一个字符数组,只有一位

                    try
                    {
    
    
                        for (int i = 0; i < textBox2.Text.Length; i++)
                        {
    
    
                            str[0] = Convert.ToChar(textBox2.Text.Substring(i, 1));  // 取待发送文本框中的第i个字符
                            serialPort1.Write(str, 0, 1);                            // 写入串口设备进行发送
                        }
                    }
                    catch
                    {
    
    
                        MessageBox.Show("串口字符写入错误!", "错误");   // 弹出发送错误对话框
                        serialPort1.Close();                          // 关闭串口
                        button1.BackColor = Color.Lime;               // 将串口开关按键的颜色,改为青绿色
                        button1.Text = "打开串口";                    // 将串口开关按键的文字改为“打开串口”
                    }
                }
                else                                                  // 如果以数值的形式发送
                {
    
    
                    byte[] Data = new byte[1];                        // 定义一个byte类型数据,相当于C语言的unsigned char类型
                    int flag = 0;                                     // 定义一个标志,标志这是第几位
                    try
                    {
    
    
                        for (int i = 0; i < textBox2.Text.Length; i++)
                        {
    
    
                            if (textBox2.Text.Substring(i, 1) == " " && flag == 0)                // 如果是第一位,并且为空字符
                            {
    
    
                                continue;
                            }

                            if (textBox2.Text.Substring(i, 1) != " " && flag == 0)                // 如果是第一位,但不为空字符
                            {
    
    
                                flag = 1;                                                         // 标志转到第二位数据去
                                if (i == textBox2.Text.Length - 1)                                // 如果这是文本框字符串的最后一个字符
                                {
    
    
                                    Data[0] = Convert.ToByte(textBox2.Text.Substring(i, 1), 16);  // 转化为byte类型数据,以16进制显示
                                    serialPort1.Write(Data, 0, 1);                                // 通过串口发送
                                    flag = 0;                                                     // 标志回到第一位数据去
                                }
                                continue;
                            }
                            else if (textBox2.Text.Substring(i, 1) == " " && flag == 1)           // 如果是第二位,且第二位字符为空
                            {
    
    
                                Data[0] = Convert.ToByte(textBox2.Text.Substring(i - 1, 1), 16);  // 只将第一位字符转化为byte类型数据,以十六进制显示
                                serialPort1.Write(Data, 0, 1);                                    // 通过串口发送
                                flag = 0;                                                         // 标志回到第一位数据去
                                continue;
                            }
                            else if (textBox2.Text.Substring(i, 1) != " " && flag == 1)           // 如果是第二位字符,且第一位字符不为空
                            {
    
    
                                Data[0] = Convert.ToByte(textBox2.Text.Substring(i - 1, 2), 16);  // 将第一,二位字符转化为byte类型数据,以十六进制显示
                                serialPort1.Write(Data, 0, 1);                                    // 通过串口发送
                                flag = 0;                                                         // 标志回到第一位数据去
                                continue;
                            }
                        }
                    }
                    catch
                    {
    
    
                        MessageBox.Show("串口数值写入错误!", "错误");
                        serialPort1.Close();
                        button1.BackColor = Color.Lime;   // 将串口开关按键的颜色,改为青绿色
                        button1.Text = "打开串口";        // 将串口开关按键的文字改为  “打开串口”
                    }
                }
            }
        }

        /* 串口接收函数 */
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
    
    
            if (!checkBox2.Checked)                        // 如果以字符串形式读取
            {
    
    
                string str = serialPort1.ReadExisting();   // 读取串口接收缓冲区字符串

                textBox1.AppendText(str + "");             // 在接收文本框中进行显示
            }
            else                                           // 以数值形式读取
            {
    
    
                int length = serialPort1.BytesToRead;      // 读取串口接收缓冲区字节数

                byte[] data = new byte[length];            // 定义相同字节的数组

                serialPort1.Read(data, 0, length);         // 串口读取缓冲区数据到数组中

                for (int i = 0; i < length; i++)
                {
    
    
                    string str = Convert.ToString(data[i], 16).ToUpper();                          // 将数据转换为字符串格式
                    textBox1.AppendText("0X" + (str.Length == 1 ? "0" + str + " " : str + " "));   // 添加到串口接收文本框中
                }
            }
        }

        /* 定时器中断回调函数 */
        private void timer1_Tick(object sender, EventArgs e)
        {
    
    
            Updata_Serialport_Name(comboBox1);   // 定时刷新可用串口,可以保证在程序启动之后连接的设备也能被检测到
        }
    }
}

四、运行

在vs里面调试运行结果如下:
在这里插入图片描述
我这里连接了一个树莓派,数据收发测试正常

如果需要在其他PC端运行,可以把工程目录下bin文件里面的Debug拷贝出来,运行exe文件即可,不需要再安装vs
在这里插入图片描述

五、结束语

简单的做了一个串口助手,总体来说其实不难,不熟悉C#语法也没关系,我也是第一次接触C#,根据C语言的经验去摸索,代码基本都能看的懂,有些语法也是即学即用的。好了,关于这一讲的内容就到这里,如果有什么问题,欢迎在评论区留言讨论,谢谢。

源码下载:https://download.csdn.net/download/ShenZhen_zixian/21712034

猜你喜欢

转载自blog.csdn.net/ShenZhen_zixian/article/details/119865123
今日推荐