前言

本文记录一下用Visual Studio 2019 C# 写一个简单的串口助手的过程,由于没有先从小处学习,而是直接找相关资料就开始做,免不了很多奇怪的问题花了一些时间,基于此情况,我将尽可能整理出更多细节,尤其是我遇到的坑,以便和我一样的新手小白上手。后续我还准备单独分析Visual Studio上提供的一些控件的使用方法,以加深理解。话不多说,正文开始

先看成品:

制作过程

1.创建项目,及工程(我用的是Visual Studio 2019)选择windows 窗体应用;前面创建项目这几步有疑问可以参考:Visual Studio 2019 C# 上位机入门(1)


2. 设置项目名、保存路径、还有框架,最后点击创建;

3.创建项目完成后,进入工程管理器,左边白色部分就是Form编辑窗口;

4. 点击打开视图=》工具箱,左边栏就出现了,各控件列表,这就是我们做上位机最基本的部分,点开公共控件、容器等就可以看到我们常用到的各个组件了;


5.从工具箱中拖出一个控件比如Button,用鼠标点击选中,界面右下角则显示出其属性;

6.现在添加我们需要的控件,从工具箱中

先点击Form窗体,右下角出来的属性中找到Text改成串口调试助手,背景色改成喜欢的颜色

拖出5个GroupBox参考后面的成品图放到大概位置,调整大小,点击每个GroupBox分别将属性中的Text改为串口设置、定时发送、发送数据、接收数据,还有一个的Text填空格。GroupBox 控件是将其它的控件放入其中,形成一个控件组。

拖出6个Label参考后面的成品图放到大概位置,分别将其属性的Text设置为: 串口号:、波特率:、数据位:、停止位:、奇偶校验: 、时间:;

拖出5个ComboBox参考后面的成品图放到大概位置,分别将name(属性窗口往上拉就可以找到)设置为cbxCOMPort(对应串口号)、cbxBaudRate(对应波特率)、cbxDataBits(对应数据位)、cbxStopBits(对应停止位)、cbxParity(对应奇偶校验);

拖出2个RadioButton参考后面的成品图放到大概位置,属性中Text分别设置为:字符显示、HEX显示
name分别设置为rbnChar(对应字符显示)、rbnHex(对应HEX显示)Checked属性分别设置为:True、False(将这2个放在同一个GroupBox中,RadioButton控件只能被选中一个)

拖出4个Button参考后面的成品图放到大概位置,属性中Text分别设置为检测串口、打开串口、发送数据、清除数据,name分别设置为btnCheckCom(对应检测串口)、btnOpenCom(对应打开串口)、btnSendData(对应发送数据)、btnClearData(对应清除数据),注意对应关系千万别弄错

拖出3个CheckBox,Text分别设置为HEX发送、回车换行、自动发送;
自动发送的CheckBox的Name改为timeBox3,另外两个可以维持默认的Name

拖出一个NumericUpDown,放到“时间:”这个Label后面;

拖出2个TextBox,name分别设置为tbxRecvData、tbxSendData,接收的ReadOnly属性设置为True。
点击框上面的黑色小三角,选中MultiLine可任意伸缩大小。属性中的Font可修改字体大小等,ScrollBars设置为Both可垂直、水平拉伸信息;
BackColor设置成黑色,字体颜色设置成白色

拖出一个StatusStrip,放在窗体的底部位置,调整熟悉中背景色BackColor跟窗体一致,点击图中加号添加一个StatusLabel

拖出2个Timer放到窗体下面有个空白处;

点击Form1面板上边沿部分,选中面板,在属性中BackColor可修改背景颜色,AcceptButton设置为btnSendData,在窗体上回车关联到发送数据按钮,用户每次按“Enter”键都相当于按此按钮。Text可修改左上方名称,Icon可设置图标;

最终如图:

7.双击某个控件可跳转到对应的事件程序中,外框架搭好了,接下来就是写程序,完善其功能。
双击Form1面板上边沿部分进入Form1_Load中开始写程序
在Form1.cs中添加

using System.IO.Ports;
using System.Text.RegularExpressions;//正则表达式,加入命名空间。

程序源码:

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;
using System.Text.RegularExpressions;//正则表达式,加入命名空间。namespace 串口助手
{public partial class Form1 : Form{SerialPort sp = null;bool isOpen = false;bool isSetProperty = false;public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){this.MaximizeBox = false;this.MaximumSize = this.Size;this.MinimumSize = this.Size;for (int i = 0; i < 100; i++){cbxCOMPort.Items.Add("COM" + (i + 1).ToString());}cbxCOMPort.SelectedIndex = 0;cbxBaudRate.Items.Add("1200");cbxBaudRate.Items.Add("2400");cbxBaudRate.Items.Add("4800");cbxBaudRate.Items.Add("9600");cbxBaudRate.Items.Add("19200");cbxBaudRate.Items.Add("38400");cbxBaudRate.Items.Add("115200");cbxBaudRate.SelectedIndex = 6;cbxStopBits.Items.Add("0");cbxStopBits.Items.Add("1");cbxStopBits.Items.Add("1.5");cbxStopBits.Items.Add("2");cbxStopBits.SelectedIndex = 1;cbxParity.Items.Add("无");cbxParity.Items.Add("奇校验");cbxParity.Items.Add("偶校验");cbxParity.SelectedIndex = 0;cbxDataBits.Items.Add("8");cbxDataBits.Items.Add("7");cbxDataBits.Items.Add("6");cbxDataBits.Items.Add("5");cbxDataBits.SelectedIndex = 0;rbnChar.Checked = true;/*添加时间显示*/this.toolStripStatusLabel1.Text = "当前时间" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");timer1.Interval = 1000;timer1.Start();}private void btnCheckCom_Click(object sender, EventArgs e){bool comExistence = false;cbxCOMPort.Items.Clear();for (int i = 0; i < 100; i++){try{SerialPort sp = new SerialPort("COM" + (i + 1).ToString());sp.Open();sp.Close();cbxCOMPort.Items.Add("COM" + (i + 1).ToString());comExistence = true;}catch (Exception){continue;}}if (comExistence){cbxCOMPort.SelectedIndex = 0;}else{MessageBox.Show("没有找到可用串口!", "错误提示!");}}private bool CheckPortSetting(){if (cbxCOMPort.Text.Trim() == "") return false;if (cbxBaudRate.Text.Trim() == "") return false;if (cbxStopBits.Text.Trim() == "") return false;if (cbxParity.Text.Trim() == "") return false;if (cbxDataBits.Text.Trim() == "") return false;return true;}private bool CheckSendData(){if (tbxSendData.Text.Trim() == "") return false;return true;}private void SetProperty(){sp = new SerialPort();sp.PortName = cbxCOMPort.Text.Trim();sp.BaudRate = Convert.ToInt32(cbxBaudRate.Text.Trim());if (cbxStopBits.Text.Trim() == "0"){sp.StopBits = StopBits.None;}else if (cbxStopBits.Text.Trim() == "1.5"){sp.StopBits = StopBits.OnePointFive;}else if (cbxStopBits.Text.Trim() == "2"){sp.StopBits = StopBits.Two;}else{sp.StopBits = StopBits.One;}sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());if (cbxParity.Text.Trim() == "奇校验"){sp.Parity = Parity.Odd;}else if (cbxParity.Text.Trim() == "偶校验"){sp.Parity = Parity.Even;}else{sp.Parity = Parity.None;}sp.ReadTimeout = -1;sp.RtsEnable = true;sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);}private void sp_DataReceived(object sender, SerialDataReceivedEventArgs eg){System.Threading.Thread.Sleep(100);this.Invoke((EventHandler)delegate//异步执行 一个线程{if (!rbnHex.Checked)//如果未选中name为rbnHex的控件{//tbxRecvData.Text += sp.ReadLine();StringBuilder sb = new StringBuilder();long rec_count = 0;int num = sp.BytesToRead;byte[] recbuf = new byte[num];rec_count += num;sp.Read(recbuf, 0, num);sb.Clear();try{Invoke((EventHandler)(delegate{sb.Append(Encoding.ASCII.GetString(recbuf));  //将整个数组解码为ASCII数组tbxRecvData.AppendText(sb.ToString());}));}catch{MessageBox.Show("请勾选换行", "错误提示");}}else if (rbnHex.Checked)//如果选中{Byte[] ReceivedData = new Byte[sp.BytesToRead];sp.Read(ReceivedData, 0, ReceivedData.Length);String RecvDataText = null;for (int i = 0; i < ReceivedData.Length; i++){RecvDataText += (ReceivedData[i].ToString("X2") + " ");//数组里接收到的数据转化为16进制}tbxRecvData.Text += RecvDataText;}sp.DiscardInBuffer();});}private void btnOpenCom_Click(object sender, EventArgs e){if (isOpen == false){if (!CheckPortSetting()){MessageBox.Show("串口未设置", "错误提示");return;}if (!isSetProperty){SetProperty();isSetProperty = true;}try{sp.Open();isOpen = true;btnOpenCom.Text = "关闭串口";cbxCOMPort.Enabled = false;cbxBaudRate.Enabled = false;cbxDataBits.Enabled = false;cbxParity.Enabled = false;cbxStopBits.Enabled = false;rbnChar.Enabled = false;rbnHex.Enabled = false;}catch (Exception){isSetProperty = false;isOpen = false;MessageBox.Show("串口无效或已被占用", "错误提示");}}else if (isOpen == true){try{if (!timeBox3.Checked){sp.Close();//关闭端口isOpen = false;btnOpenCom.Text = "打开串口";cbxCOMPort.Enabled = true;cbxBaudRate.Enabled = true;cbxDataBits.Enabled = true;cbxParity.Enabled = true;cbxStopBits.Enabled = true;rbnChar.Enabled = true;rbnHex.Enabled = true;}else{MessageBox.Show("请先关闭自动发送", "错误提示");}}catch (Exception){MessageBox.Show("关闭串口时发生错误", "错误提示");}}}private void btnSendData_Click(object sender, EventArgs e){byte[] textchar = new byte[1];int num2 = 0;if (isOpen){try{if (!checkBox1.Checked)//如果没有选中十六进制发送{if (!checkBox2.Checked)//未选中回车换行{sp.Write(tbxSendData.Text);//串口发送 (发送框里的东西)}else{//sp.WriteLine(tbxSendData.Text);//用这个方法只会在后面加\n没有\r,下面的方法是验证可行的string res = tbxSendData.Text + "\r\n";byte[] byteArray = System.Text.Encoding.Default.GetBytes(res);sp.Write(byteArray, 0, byteArray.Length);}}else//选择十六进制发送的时候{string buf = tbxSendData.Text;string bartenm = @"\s";//正则表达式string replace = "";Regex rgx = new Regex(bartenm);string senddata = rgx.Replace(buf, replace);num2 = (senddata.Length - senddata.Length % 2) / 2;for (int a = 0; a < num2; a++){textchar[0] = Convert.ToByte(senddata.Substring(a * 2, 2), 16);sp.Write(textchar, 0, 1);}if (senddata.Length % 2 != 0){textchar[0] = Convert.ToByte(senddata.Substring(tbxSendData.Text.Length - 1, 2), 16);sp.Write(textchar, 0, 1);num2++;}}}catch{MessageBox.Show("发送数据时发生错误!", "错误提示");return;}}else{MessageBox.Show("串口未打开错误提示!", "错误提示");}if (!CheckSendData()){MessageBox.Show("请输入要发送的数据", "错误提示");}}private void btnClearData_Click(object sender, EventArgs e){if (!timeBox3.Checked){tbxRecvData.Text = "";tbxSendData.Text = "";}else{MessageBox.Show("请先关闭自动发送", "错误提示");}}private void timer1_Tick(object sender, EventArgs e){this.toolStripStatusLabel1.Text = "当前时间" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");}private void timeBox3_CheckedChanged(object sender, EventArgs e){System.Windows.Forms.Timer txTimer = new System.Windows.Forms.Timer();if (timeBox3.Checked){if (numericUpDown1.Value != 0){if (CheckSendData()){txTimer.Enabled = false;timer2.Interval = (int)numericUpDown1.Value; //定时器赋初值  timer2.Start();}else if (!CheckSendData()){timer2.Stop();}}else if (numericUpDown1.Value == 0){timer2.Stop();}}else{txTimer.Enabled = true;timer2.Stop();}}private void timer2_Tick(object sender, EventArgs e){btnSendData_Click(btnSendData, new EventArgs());}}
}

8.最后是很重要的一步,刚才我们已经把程序全部编辑好了,如果是直接把我提供的程序复制粘贴的话,那么还需要做一个工作是回到窗体设计页面把每个控件中对应的触发事件设置对应我们编辑的成员函数才行。因为正常的开发应该是放置好控件后,单击控件,在右下角属性栏中点击小闪电,快速跳转到事件选项,双击某个事件就会跳到程序编辑界面,系统自动为你添加一个默认名称的事件,然后编辑这个事件。

那么现在我们是已经先编辑完了程序,然后再去在窗体设计界面把对应控件的触发事件设置一下对应哪个已经编辑好的函数就行了。

点击检测串口按钮,设置Click对应btnCheckCom_Click

点击打开串口按钮,设置Click对应btnOpenCom_Click

点击清空数据按钮,设置Click对应btnClearData_Click

点击发送数据按钮,设置Click对应btnSendData_Click

点击time1控件,设置Tick对应timer1_Tick,同理,点击time2控件,设置Tick对应timer2_Tick

9.最后点击顶部菜单栏的启动就可以开始调试验证了

找一个USB转串口模块,其TX和RX对插,连上电脑,点击检测串口,选择端口,点击打开串口,发送数据栏输入数据,点击发送数据,看接收数据是否正常(由于tx和rx对插,这样相当于自发自收,接收数据栏应该也会收到和发送一样的数据)。其他功能自行验证。

10.将Debug改为Release,点击生成解决方案

工程路径下的Release文件夹就生成了可执行文件,这个文件就可在其他电脑上使用了

如果读者朋友在练习实践的过程中遇到问题可能是我的过程写得不够细导致结果出现偏差,可以在评论中反馈出来以便改进文章质量。

这个串口助手还有很多功能可以添加,比如保存LOG文件功能,发送和接收字节计数,时间戳功能等等,后面有空再研究。

参考
https://blog.csdn.net/Casey_shi/article/details/117965159

https://www.freesion.com/article/8653228957/

https://blog.csdn.net/weixin_42378319/article/details/118424816

https://blog.csdn.net/C_gyl/article/details/78134311

https://www.haolizi.net/example/view_28754.html

https://www.feiqueyun.cn/zixun/jishu/115812.html

https://blog.csdn.net/You_are_blind/article/details/127010206

Visual Studio 2019 C# 上位机入门(2):写一个简单的串口助手相关推荐

  1. Microsoft Visual Studio 2019介绍之使用入门

    Microsoft Visual Studio 2019介绍之使用入门 目录 1.前言: 2.Visual Studio安装: 3.项目的创建和代码的编译与运行: 4.常见问题及解决方法: 1.前言 ...

  2. Visual studio 2019 支持graphics.h 库的支持,简单易懂,妈妈再也不用担心你的学习

    Visual studio 2019 支持graphics.h 库的支持 首先找到EasyX 的官网,传送门 请老爷们看下面操作 点击安装就可以,就可以写一段图形代码看看是否成功 例: #includ ...

  3. 上位机开发C#语言:模拟STC串口助手接收单片机发送数据

    题目概述: 设计一个模拟STC串口助手,通过串口进行接收或发送数据. STC串口助手: 编程: using System; using System.Collections.Generic; usin ...

  4. [翻译] Visual Studio 2019 RC版发布

    今天,我们将分享 Visual Studio 2019 的发布候选版(RC 版) - 这是在 4 月 2 日的虚拟发布活动上正式发布之前的最后步骤之一. 您可以在 visualstudio.com/d ...

  5. 使用CLI模板 | Visual Studio 2019(16.10)新功能试用

    Visual Studio很早以前就有模板了,.NET Core命令行界面(CLI)也能够安装模板并通过dotnet new命令使用它们.然而,但是并没有什么简单的方法可以在Visual Studio ...

  6. Visual Studio 2019 RC入门——第1部分

    目录 介绍 背景 语言包 打开Visual Studio 2019 RC 克隆或检查我们的代码 打开项目或解决方案 打开本地文件夹 无代码继续 创建一个新项目 兴趣点 介绍 在本文中,让我们看看如何开 ...

  7. C#编程入门第一课 Visual studio 2019使用,cs文件构成,打开项目文件,两句最简单语句,运行前,运行程序,显示行号,设置字体

    C#编程入门第一课 文章目录 C#编程入门第一课 1 2 3 Visual Studio 2019 4. cs文件构成 5. 打开项目文件 6. 两句最简单语句 7. 运行前 8.运行程序 9.显示行 ...

  8. 汇编00-MASM 和 Visual Studio 2019 入门

    Qt源码解析 索引 汇编逆向--- MASM 和 Visual Studio入门 前提知识:需要对VS开发环境熟悉. 主题: VS环境准备 教程:构建和运行 32 位程序 教程:构建和运行 64 位程 ...

  9. Visual Studio 2019 RC入门——第2部分

    目录 介绍 背景 使用新项目模板 顶部的语言组合框 平台组合框 项目类型组合框 使用Visual Studio 2019 Web应用程序项目 使用Web应用程序 使用代码 创建我们的第一个ASP.NE ...

最新文章

  1. Python处理JSON数据
  2. 皮一皮:唉唉...这钱还没收呢...
  3. Java实现算法导论中最近点对问题分治法
  4. flink mysql connector_Flink JDBC Connector:Flink 与数据库集成最佳实践
  5. python IO编程-StringIO和BytesIO
  6. elementUI 分页组件的使用 - 踩坑篇
  7. php怎么给span赋值,php给一组指定关键词添加span标签的方法
  8. python高效编程15个利器_15个Python库,让你学习编程更轻松!
  9. oracle中对排序的总结(按照拼音、部首、笔画排序功能 )
  10. this_scope_call_apply_bind_柯里化 详细分析
  11. Unet实现文档图像去噪、去水印
  12. 斥资2.5亿美元,英特尔也开始玩车了
  13. Win10下VB6.0开发之串口通信基础(二)代码功能篇
  14. 怎么判断机械表上满弦_自动上弦的机械表,如何晃动就可以上满弦?
  15. 爱剪辑显示服务器繁忙,爱剪辑视频卡顿怎么回事 爱剪辑视频卡顿解决方法
  16. 一般描绘性形容词_英语中描绘性形容词是指哪种形容词?
  17. Spark-Sql源码解析之三 Analyzer:Unresolved logical plan – analyzed logical plan
  18. 完成有顺序约束的任务指派问题--应用模拟退火算法求解
  19. 四年级关于计算机的知识,小学四年级下册信息技术教案
  20. maven学习:maven 的入门

热门文章

  1. linux vi编译显示行号,Linux编辑器vi、vim、gedit显示行号
  2. 【HISI调试】hisi PQtool开机报错,提示无dll,找不到插件,比如:ISP calibration Tool等
  3. Typora + onedrive + gitee + PicGo,打造markdown类型完美笔记
  4. 最新“铁饭碗”排名,公务员排第六,国家烟草第四,医生第二,第一很多人却不知道...
  5. 这个团队厉害啦!只有“程序猿”没有“程序媛”
  6. 蓝桥杯备战-门派制作
  7. 【中台】漫画:什么是中台?
  8. npm install 常用参数说明
  9. Freehosting服务器绑定域名流程
  10. 只要单片机具有真正唯一ID,就可以让加密坚不可摧(转)