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

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

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

一、新建项目

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

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

二、控件布局

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

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

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

控件类型控件名称Text说明
TextBoxTextBox1TextBox1接收显示窗口
TextBoxTextBox2TextBox2发送输入窗口
labellabel1端口号文本提示
labellabel2波特率文本提示
comboBoxcomboBox1comboBox1端口号下拉菜单
comboBoxcomboBox2comboBox2波特率下拉菜单
buttonbutton1打开串口打开串口按键
buttonbutton2清除接收清除接收按键
buttonbutton3发送发送按键
checkBoxcheckBox1hex发送切换发送格式
checkBoxcheckBox2hex接收切换接收格式
serialPortserialPort1serialPort1串口通信控件
TimeTiime1Tiime1定时器,用于定时刷新端口

先把控件从工具箱里面拉出来,调整好大小和布局。
提示: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

  • 67
    点赞
  • 467
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值