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

目录

  • VS上位机开发——串口助手
    • 一、新建项目
    • 二、控件布局
    • 三、编写程序
      • 1、端口更新函数
      • 2、启动窗口加载函数
      • 3、"打开串口"按键回调函数
      • 4、"清除接收"按键回调函数
      • 5、"发送"按键回调函数
      • 6、串口接收函数
      • 7、定时器中断回调函数
    • 四、运行
    • 五、结束语

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

一、新建项目

第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;                               // 定义字符串数组,数组名为 ArryPortArryPort = 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;                               // 定义字符串数组,数组名为 ArryPortArryPort = 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

VS上位机开发——串口助手相关推荐

  1. 从零开始编写一个上位机(串口助手)QT Creator + Python

    提示:本博客作为学习笔记,有错误的地方希望指正,此文可能会比较长,作为学习笔记的积累,希望对来着有帮助.   绪论:笔者这里使用的是QTCreator和Python来实现一个简单的串口上位机的开发的简 ...

  2. C#上位机开发串口通信编程——倒计时器开发

    C#上位机开发串口通信编程--倒计时器开发 一.介绍 这是我按照B站上的一个上位机开发视频教程开发的倒计时器开发,本来只有开始计时功能,没有停止计时功能,停止计时功能后面我自己添加了. 视频网址:C# ...

  3. C#上位机开发串口通信

    一.创建一个Winform窗体,制作一个5s定时器 using System; using System.Windows.Forms;namespace 计时器 {public partial cla ...

  4. 从零开始编写一个上位机(串口助手)QT Creator + C++

    提示:本博客作为学习笔记,有错误的地方希望指正,此文可能会比较长,作为学习笔记的积累,希望对来着有帮助.   绪论:笔者这里使用的是QTCreator和C++来实现一个简单的串口上位机的开发的简单过程 ...

  5. 上位机软件-串口助手-带登陆界面

    前言: 这是我在大三的时候写的一个串口助手,觉得挺好用的就分享给大家.如果有什么bug的话,希望大家多多包涵,毕竟对于C#语言我只是新手. 制作串口助手 串口助手选用Visual C#开发语言和Vis ...

  6. 最简单DIY基于ESP32CAM的物联网相机系统⑤(用C#上位机实现串口图传)

    第一篇:最简单DIY基于ESP32CAM的物联网相机系统①(用网页实现拍照图传) 第二篇:最简单DIY基于ESP32CAM的物联网相机系统②(在JAVAWEB服务器实现图片查看器) 第三篇:最简单DI ...

  7. 485通信原理_上位机开发之单片机通信实践

    经常会有一些学员会问到上位机与单片机之间通信的问题,而我们经常会讲上位机与PLC之间通信,那么其实对上位机开发来说,不管是和PLC通信,还是和单片机通信,通信原理都是一样的.PLC的本质就是单片机,在 ...

  8. pythonqt4上位机开发_「新阁教育」自由口通信上位机实战案例

    1.引言 组态软件作为一种通用软件,体系结构较为庞大.功能软件包多.价格也比较昂贵,而且对于一些复杂的业务逻辑或自定义的协议,实现起来比较麻烦.近几年,C#/.NET上位机开发应用越来越广泛,相对于传 ...

  9. 上位机通过串口获取单片机数据

    上位机通过串口获取单片机数据(C#) 需求 准备 功能设计 界面设计 关键程序 打开串口 发送数据 接收数据 实现页面 完整源码 后续 需求 我们在平时使用单片机制作项目时,总是希望能够实时显示一些数 ...

  10. pythonqt4上位机开发_上位机开发之单片机通信实践(一)

    经常会有一些学员会问到上位机与单片机之间通信的问题,而我们经常会讲上位机与PLC之间通信,那么其实对上位机开发来说,不管是和PLC通信,还是和单片机通信,通信原理都是一样的.PLC的本质就是单片机,在 ...

最新文章

  1. 被称为“C#圣经”的权威著作!
  2. 【Windows 逆向】内存地址分析 ( 内存条 | 虚拟内存 | 内存地址及寻址范围 | 内存地址与数据的关系 )
  3. HDU 1853 HDU 3488【有向环最小权值覆盖问题 】带权二分图匹配 KM算法
  4. CodeForces - 1301D Time to Run(构造+模拟)
  5. 多线程是并行还是并发_并发,并行,线程,进程,异步和同步有相关性吗?
  6. 嵌入式操作系统内核原理和开发(实时系统中的定时器)
  7. Microsoft Loopback Adapter : Oracle 安装
  8. Leetcode每日一题 面试题56 - I.数组中数字出现的次数
  9. Cadence 16.6快速创建多引脚芯片原理图符号
  10. Android 和 Java 性能优化最佳实践
  11. 使用 spring.profiles.active 及 @profile 注解 动态化配置内部及外部配置
  12. 带你撸一台免费云服务器
  13. eclipse as 项目编码修改
  14. 静态网站以及动态网站
  15. 深圳大学算法实验一——排序算法性能分析
  16. IDE+vim,提高开发效率
  17. 【多目标优化】2. 非支配排序遗传算法 —(NSGA、NSGA-II)
  18. day 86 Vue学习之五DIY脚手架、webpack使用、vue-cli的使用、element-ui
  19. DirectX游戏开发之代码的框架简析
  20. 一家两制抗风雨 有得有失看未来(转)

热门文章

  1. CharNet阅读笔记
  2. 计算机病毒防治与信息安全知识300问,计算机病毒防治与信息安全知识300问.pdf...
  3. 【​观察】一部《天龙八部》,缘起“六脉神剑”
  4. 天龙八部,数据可视化分析虚竹和童姥居然关系非同一般
  5. lca_trajan
  6. 【ArcGIS小技巧视频教程】(5):在ArcGIS中挂接其他数据
  7. 【PS图像处理】PS软件提示不能完成命令,因为暂存盘已满的解决办法
  8. 离散Hopfield神经网络摘记
  9. 【Python】Pandas DataFrame 一维表二维表的转换
  10. 直播视频分辨率码率参考设置