C# WinForm下利用虚拟串口工具进行串口通信
1、创建虚拟串口
虚拟串口工具的获取及使用:虚拟串口工具VSPD简单使用
2、创建WinForm应用程序
3、绘制WinForm窗体
界面确实不好看,不过这些都不重要
在这里说明一下用到了哪些控件及控件命名
1、接收数据及发送两个模块分别使用GroupBox容器控件包裹,发送部分容器命名gb_SendPanel,接口部分命名gb_ReceivePanel
2、接收栏内只有一个TextBox控件,命名为txt_Received,并设置属性允许多行,只读,显示垂直滚动条Multiline=true,ReadOnly = true,ScrollBars =Vertical
3、发送部分由6个文本Label,6个下拉框ComboBox,3个按钮Button,1个发送内容框TextBox组成
4、发送部分6个Label命名改不改都无所谓,后续不会再有用到
5、6个下拉框ComboBox分别命名为:串口下拉框cb_SelectComPort、波特率下拉框cb_baudSelect、停止位下拉框cb_StopSelect、数据位下拉框cb_DataSelect、检验位下拉框cb_CheckSelect、显示下拉框cb_DataShow
6、3个按钮Button命名分别为:检测串口btn_CheckPort、打开\关闭串口btn_OpenOrrEndCom、发送按钮btn_Send
7、发送内容文本框命名为txt_Send,设置属性允许多行Multiline = true
8、在这个窗体中字体上我这里是设置的宋体, 12pt(无关紧要)
9、窗体数据相关都是后台通过代码实现的,详情继续往下看
4、属性初始化
#region 初始话属性 /// <summary> /// 实例化串口资源类 /// </summary> private readonly System.IO.Ports.SerialPort serialPort = new System.IO.Ports.SerialPort(); /// <summary> /// 波特率数值范围 /// </summary> private readonly List<string> baudList = new List<string>() { "600","1200", "2400", "4800", "9600","14400", "19200", "28800","38400", "43000", "57600", "76800","115200","128000","230400","256000","460800","921600","1382400", "自定义" }; /// <summary> /// 停止位 /// </summary> private readonly List<string> stopList = new List<string> { "1", "1.5", "2" }; /// <summary> /// 数据位 /// </summary> private readonly List<string> dataList = new List<string> { "5", "6", "7", "8" }; /// <summary> /// 校验位 /// </summary> private readonly List<string> checkList = new List<string>() { "NONE", "ODD", "EVEN", "MARK", "SPACE" }; /// <summary> /// 数据显示 /// </summary> private readonly List<string> showList = new List<string>() { "16进制(HEX)", "字符显示(ASCII)" }; #endregion
5、界面加载与控件初始化事件
#region 界面加载 /// <summary> /// 界面加载事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_Load(object sender, EventArgs e) { //串口选择 InitPorts(); //波特率 InitBaud(); //停止位 InitStop(); //数据位 InitData(); //校验位 InitCheck(); //初始化数据显示 InitDataShow(); //实例化串口控件 InitSerialPort(); } #endregion #region 初始化控件 /// <summary> /// 初始化串口控件 /// </summary> public void InitSerialPort() { serialPort.DataReceived += Sp1_DataReceived;//指示已通过由 System.IO.Ports.SerialPort 对象表示的端口接收了数据 serialPort.DtrEnable = true;//获取或设置一个值,该值在串行通信过程中启用数据终端就绪 (DTR) 信号。 serialPort.RtsEnable = true;//获取或设置一个值,该值指示在串行通信中是否启用请求发送 (RTS) 信号 serialPort.ReadTimeout = 1000;//设置数据读取超时为1s serialPort.Close(); } /// <summary> /// 扫描可用串口,初始化串口选择 /// </summary> public void InitPorts() { this.cb_SelectComPort.DropDownStyle = ComboBoxStyle.DropDownList;//禁用输入功能 this.cb_SelectComPort.Items.Clear();//清除原有串口 var comPostList = System.IO.Ports.SerialPort.GetPortNames().ToList();//读取串口 if (comPostList != null && comPostList.Any()) { comPostList = comPostList.OrderBy(x => x).ToList(); foreach (var item in comPostList) { this.cb_SelectComPort.Items.Add(item); } this.cb_SelectComPort.Text = comPostList.FirstOrDefault();//默认选择第一个串口 this.cb_SelectComPort.Enabled = true;//打开选择框 } else { MessageBox.Show("本机未找到串口!!!", "Error"); } } /// <summary> /// 初始化校验位 /// </summary> public void InitCheck() { this.cb_CheckSelect.DropDownStyle = ComboBoxStyle.DropDownList; foreach (var item in checkList) { this.cb_CheckSelect.Items.Add(item); } this.cb_CheckSelect.SelectedItem = "NONE"; } /// <summary> /// 初始化数据位 /// </summary> public void InitData() { this.cb_DataSelect.DropDownStyle = ComboBoxStyle.DropDownList; foreach (var item in dataList) { this.cb_DataSelect.Items.Add(item); } this.cb_DataSelect.SelectedItem = "8"; } /// <summary> /// 初始化停止位 /// </summary> public void InitStop() { this.cb_StopSelect.DropDownStyle = ComboBoxStyle.DropDownList; foreach (var item in stopList) { this.cb_StopSelect.Items.Add(item); } this.cb_StopSelect.SelectedItem = "1"; } /// <summary> /// 初始化波特率 /// </summary> public void InitBaud() { this.cb_baudSelect.DropDownStyle = ComboBoxStyle.DropDownList;//不可手动输入 foreach (var item in baudList) { this.cb_baudSelect.Items.Add(item); } this.cb_baudSelect.SelectedItem = "9600"; } /// <summary> /// 初始化数据显示 /// </summary> public void InitDataShow() { this.cb_DataShow.DropDownStyle = ComboBoxStyle.DropDownList;//不可手动输入 foreach (var item in showList) { this.cb_DataShow.Items.Add(item); } this.cb_DataShow.SelectedItem = "字符显示(ASCII)"; } #endregion
6、波特率选择事件
/// <summary> /// 波特率选择,自定义时允许输入 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void cb_baudSelect_SelectedValueChanged(object sender, EventArgs e) { if (this.cb_baudSelect.SelectedItem?.ToString() == "自定义") { this.cb_baudSelect.DropDownStyle = ComboBoxStyle.DropDown; } else { this.cb_baudSelect.DropDownStyle = ComboBoxStyle.DropDownList; } }
7、检测串口
/// <summary> /// 检测串口 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_CheckPort_Click(object sender, EventArgs e) { this.cb_SelectComPort.Items.Clear();//清除原有串口选择数据 var comPostList = System.IO.Ports.SerialPort.GetPortNames().ToList();//读取串口 if (comPostList != null && comPostList.Any()) { comPostList = comPostList.OrderBy(x => x).ToList();//排序 foreach (var item in comPostList) { this.cb_SelectComPort.Items.Add(item); } this.cb_SelectComPort.Text = comPostList.FirstOrDefault();//默认选择第一个串口 } else { MessageBox.Show("本机未找到串口!!!", "Error"); } }
8、串口设置
#region 设置串口 /// <summary> /// 设置串口信息 /// </summary> private void SetSerialPort() { string strBuadRate = this.cb_baudSelect.Text.Trim(); string strDataRate = this.cb_DataSelect.Text.Trim(); string strCheckRate = this.cb_CheckSelect.Text.Trim(); string strPortRate = this.cb_SelectComPort.Text.Trim(); string strStopRate = this.cb_StopSelect.Text.Trim(); int IBaudBits = Convert.ToInt32(strBuadRate); int IDataBits = Convert.ToInt32(strDataRate); serialPort.PortName = strPortRate;//设置串口号 serialPort.BaudRate = IBaudBits;//设置波特率 serialPort.DataBits = IDataBits;//设置数据位 serialPort.Encoding = Encoding.UTF8; //设置校验位 switch (strCheckRate) { case "NONE": serialPort.Parity = Parity.None; break; case "ODD": serialPort.Parity = Parity.Odd; break; case "EVEN": serialPort.Parity = Parity.Even; break; case "MARK": serialPort.Parity = Parity.Mark; break; case "SPACE": serialPort.Parity = Parity.Space; break; default: serialPort.Parity = Parity.None; break; } //设置停止位 switch (strStopRate) { case "1": serialPort.StopBits = StopBits.One; break; case "1.5": serialPort.StopBits = StopBits.OnePointFive; break; case "2": serialPort.StopBits = StopBits.Two; break; default: serialPort.StopBits = StopBits.One; break; } serialPort.ReadTimeout = 5000;//读取超时时间5s } #endregion
9、打开\关闭串口
/// <summary> /// 打开\关闭串口按钮点击事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_OpenOrrEndCom_Click(object sender, EventArgs e) { //串口是否打开 if (!serialPort.IsOpen) { try { this.btn_OpenOrrEndCom.Text = "关闭串口"; //如果打开状态,则先关闭一下 if (serialPort.IsOpen) { serialPort.Close(); } //设置串口 SetSerialPort(); //打开串口后不允许更改连接属性 this.cb_baudSelect.Enabled = false; this.cb_CheckSelect.Enabled = false; this.cb_SelectComPort.Enabled = false; this.cb_StopSelect.Enabled = false; this.cb_DataSelect.Enabled = false; //打开 serialPort.Open(); } catch (Exception ex) { MessageBox.Show("打开串口失败:" + ex.Message); throw; } } else { this.cb_baudSelect.Enabled = true; this.cb_CheckSelect.Enabled = true; this.cb_SelectComPort.Enabled = true; this.cb_StopSelect.Enabled = true; this.cb_DataSelect.Enabled = true; this.btn_OpenOrrEndCom.Text = "打开串口"; serialPort.Close();//关闭 } }
10、发送与接收数据
/// <summary> /// 发送数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_Send_Click(object sender, EventArgs e) { if (serialPort.IsOpen) { try { byte[] sendData = null; string send = this.txt_Send.Text.Trim(); var showText = this.cb_DataShow.Text; //按照指定编码将string编程字节数组 byte[] b = Encoding.GetEncoding("GBK").GetBytes(send); if (showText == "16进制(HEX)") { string result = string.Empty; //逐字节变为16进制字符 for (int i = 0; i < b.Length; i++) { result += Convert.ToString(b[i], 16).ToUpper() + " "; } sendData = Encoding.GetEncoding("GBK").GetBytes(result); } else { sendData = b; } serialPort.Write(sendData, 0, sendData.Length); } catch (Exception ex) { MessageBox.Show("发送失败:" + ex.Message, "Error"); } } else { MessageBox.Show("请先打开串口", "Error"); } } /// <summary> /// 接收数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Sp1_DataReceived(object sender, SerialDataReceivedEventArgs e) { //异步委托一个线程,不然接收数据时会出现数据线程错误 this.Invoke((EventHandler)(delegate { var showText = this.cb_DataShow.Text; if (serialPort.IsOpen) { try { byte[] receivedData = new byte[serialPort.BytesToRead];//创建接收数据数组 serialPort.Read(receivedData, 0, receivedData.Length);//读取数据 var content = string.Empty; //显示形式 switch (showText) { case "16进制(HEX)": for (int i = 0; i < receivedData.Length; i++) { //ToString("X2") 为C#中的字符串格式控制符 //X为 十六进制 //2为 每次都是两位数 content += (receivedData[i].ToString("X2") + " "); } break; case "字符显示(ASCII)": content = Encoding.GetEncoding("GB2312").GetString(receivedData);//防止乱码 break; } //接收文本框 this.txt_Received.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss:ffff")}:{content} \r\n"); serialPort.DiscardInBuffer();//丢弃缓存区数据 } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } else { MessageBox.Show("请打开串口", "Error"); } })); }
11、完整代码
using System; using System.Collections.Generic; using System.Data; using System.IO.Ports; using System.Linq; using System.Text; using System.Windows.Forms; namespace 虚拟串口通信 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } #region 初始话属性 /// <summary> /// 实例化串口资源类 /// </summary> private readonly System.IO.Ports.SerialPort serialPort = new System.IO.Ports.SerialPort(); /// <summary> /// 波特率数值范围 /// </summary> private readonly List<string> baudList = new List<string>() { "600","1200", "2400", "4800", "9600","14400", "19200", "28800","38400", "43000", "57600", "76800","115200","128000","230400","256000","460800","921600","1382400","自定义" }; /// <summary> /// 停止位 /// </summary> private readonly List<string> stopList = new List<string> { "1", "1.5", "2" }; /// <summary> /// 数据位 /// </summary> private readonly List<string> dataList = new List<string> { "5", "6", "7", "8" }; /// <summary> /// 校验位 /// </summary> private readonly List<string> checkList = new List<string>() { "NONE", "ODD", "EVEN", "MARK", "SPACE" }; /// <summary> /// 数据显示 /// </summary> private readonly List<string> showList = new List<string>() { "16进制(HEX)", "字符显示(ASCII)" }; #endregion #region 界面加载 /// <summary> /// 界面加载事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_Load(object sender, EventArgs e) { //串口选择 InitPorts(); //波特率 InitBaud(); //停止位 InitStop(); //数据位 InitData(); //校验位 InitCheck(); //初始化数据显示 InitDataShow(); //实例化串口控件 InitSerialPort(); } #endregion #region 初始化 /// <summary> /// 初始化串口控件 /// </summary> public void InitSerialPort() { serialPort.DataReceived += Sp1_DataReceived;//指示已通过由 System.IO.Ports.SerialPort 对象表示的端口接收了数据 serialPort.DtrEnable = true;//获取或设置一个值,该值在串行通信过程中启用数据终端就绪 (DTR) 信号。 serialPort.RtsEnable = true;//获取或设置一个值,该值指示在串行通信中是否启用请求发送 (RTS) 信号 serialPort.ReadTimeout = 1000;//设置数据读取超时为1s serialPort.Close(); } /// <summary> /// 扫描可用串口,初始化串口选择 /// </summary> public void InitPorts() { this.cb_SelectComPort.DropDownStyle = ComboBoxStyle.DropDownList;//禁用输入功能 this.cb_SelectComPort.Items.Clear();//清除原有串口 var comPostList = System.IO.Ports.SerialPort.GetPortNames().ToList();//读取串口 if (comPostList != null && comPostList.Any()) { comPostList = comPostList.OrderBy(x => x).ToList(); foreach (var item in comPostList) { this.cb_SelectComPort.Items.Add(item); } this.cb_SelectComPort.Text = comPostList.FirstOrDefault();//默认选择第一个串口 this.cb_SelectComPort.Enabled = true;//打开选择框 } else { MessageBox.Show("本机未找到串口!!!", "Error"); } } /// <summary> /// 初始化校验位 /// </summary> public void InitCheck() { this.cb_CheckSelect.DropDownStyle = ComboBoxStyle.DropDownList; foreach (var item in checkList) { this.cb_CheckSelect.Items.Add(item); } this.cb_CheckSelect.SelectedItem = "NONE"; } /// <summary> /// 初始化数据位 /// </summary> public void InitData() { this.cb_DataSelect.DropDownStyle = ComboBoxStyle.DropDownList; foreach (var item in dataList) { this.cb_DataSelect.Items.Add(item); } this.cb_DataSelect.SelectedItem = "8"; } /// <summary> /// 初始化停止位 /// </summary> public void InitStop() { this.cb_StopSelect.DropDownStyle = ComboBoxStyle.DropDownList; foreach (var item in stopList) { this.cb_StopSelect.Items.Add(item); } this.cb_StopSelect.SelectedItem = "1"; } /// <summary> /// 初始化波特率 /// </summary> public void InitBaud() { this.cb_baudSelect.DropDownStyle = ComboBoxStyle.DropDownList;//不可手动输入 foreach (var item in baudList) { this.cb_baudSelect.Items.Add(item); } this.cb_baudSelect.SelectedItem = "9600"; } /// <summary> /// 初始化数据显示 /// </summary> public void InitDataShow() { this.cb_DataShow.DropDownStyle = ComboBoxStyle.DropDownList;//不可手动输入 foreach (var item in showList) { this.cb_DataShow.Items.Add(item); } this.cb_DataShow.SelectedItem = "字符显示(ASCII)"; } #endregion #region 设置串口 /// <summary> /// 设置串口信息 /// </summary> private void SetSerialPort() { string strBuadRate = this.cb_baudSelect.Text.Trim(); string strDataRate = this.cb_DataSelect.Text.Trim(); string strCheckRate = this.cb_CheckSelect.Text.Trim(); string strPortRate = this.cb_SelectComPort.Text.Trim(); string strStopRate = this.cb_StopSelect.Text.Trim(); int IBaudBits = Convert.ToInt32(strBuadRate); int IDataBits = Convert.ToInt32(strDataRate); serialPort.PortName = strPortRate;//设置串口号 serialPort.BaudRate = IBaudBits;//设置波特率 serialPort.DataBits = IDataBits;//设置数据位 serialPort.Encoding = Encoding.UTF8; //设置校验位 switch (strCheckRate) { case "NONE": serialPort.Parity = Parity.None; break; case "ODD": serialPort.Parity = Parity.Odd; break; case "EVEN": serialPort.Parity = Parity.Even; break; case "MARK": serialPort.Parity = Parity.Mark; break; case "SPACE": serialPort.Parity = Parity.Space; break; default: serialPort.Parity = Parity.None; break; } //设置停止位 switch (strStopRate) { case "1": serialPort.StopBits = StopBits.One; break; case "1.5": serialPort.StopBits = StopBits.OnePointFive; break; case "2": serialPort.StopBits = StopBits.Two; break; default: serialPort.StopBits = StopBits.One; break; } serialPort.ReadTimeout = 5000;//读取超时时间5s } #endregion #region 波特率选择 /// <summary> /// 波特率选择,自定义时允许输入 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void cb_baudSelect_SelectedValueChanged(object sender, EventArgs e) { if (this.cb_baudSelect.SelectedItem?.ToString() == "自定义") { this.cb_baudSelect.DropDownStyle = ComboBoxStyle.DropDown; } else { this.cb_baudSelect.DropDownStyle = ComboBoxStyle.DropDownList; } } #endregion #region 检测串口 /// <summary> /// 检测串口 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_CheckPort_Click(object sender, EventArgs e) { this.cb_SelectComPort.Items.Clear();//清除原有串口选择数据 var comPostList = System.IO.Ports.SerialPort.GetPortNames().ToList();//读取串口 if (comPostList != null && comPostList.Any()) { comPostList = comPostList.OrderBy(x => x).ToList(); foreach (var item in comPostList) { this.cb_SelectComPort.Items.Add(item); } this.cb_SelectComPort.Text = comPostList.FirstOrDefault();//默认选择第一个串口 } else { MessageBox.Show("本机未找到串口!!!", "Error"); } } #endregion #region 打开\关闭串口 /// <summary> /// 打开\关闭串口 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_OpenOrrEndCom_Click(object sender, EventArgs e) { //串口是否打开 if (!serialPort.IsOpen) { try { this.btn_OpenOrrEndCom.Text = "关闭串口"; //如果打开状态,则先关闭一下 if (serialPort.IsOpen) { serialPort.Close(); } //设置串口 SetSerialPort(); //打开串口后不允许更改 this.cb_baudSelect.Enabled = false; this.cb_CheckSelect.Enabled = false; this.cb_SelectComPort.Enabled = false; this.cb_StopSelect.Enabled = false; this.cb_DataSelect.Enabled = false; //打开 serialPort.Open(); } catch (Exception ex) { MessageBox.Show("打开串口失败:" + ex.Message); throw; } } else { this.cb_baudSelect.Enabled = true; this.cb_CheckSelect.Enabled = true; this.cb_SelectComPort.Enabled = true; this.cb_StopSelect.Enabled = true; this.cb_DataSelect.Enabled = true; this.btn_OpenOrrEndCom.Text = "打开串口"; serialPort.Close();//关闭 } } #endregion #region 发送与接收数据 /// <summary> /// 发送数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_Send_Click(object sender, EventArgs e) { if (serialPort.IsOpen) { try { byte[] sendData = null; string send = this.txt_Send.Text.Trim(); var showText = this.cb_DataShow.Text; //按照指定编码将string编程字节数组 byte[] b = Encoding.GetEncoding("GBK").GetBytes(send); if (showText == "16进制(HEX)") { string result = string.Empty; //逐字节变为16进制字符 for (int i = 0; i < b.Length; i++) { result += Convert.ToString(b[i], 16).ToUpper() + " "; } sendData = Encoding.GetEncoding("GBK").GetBytes(result); } else { sendData = b; } serialPort.Write(sendData, 0, sendData.Length); } catch (Exception ex) { MessageBox.Show("发送失败:" + ex.Message, "Error"); } } else { MessageBox.Show("请先打开串口", "Error"); } } /// <summary> /// 接收数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Sp1_DataReceived(object sender, SerialDataReceivedEventArgs e) { //异步委托一个线程,不然接收数据时会出现数据线程错误 this.Invoke((EventHandler)(delegate { var showText = this.cb_DataShow.Text; if (serialPort.IsOpen) { try { byte[] receivedData = new byte[serialPort.BytesToRead];//创建接收数据数组 serialPort.Read(receivedData, 0, receivedData.Length);//读取数据 var content = string.Empty; //显示形式 switch (showText) { case "16进制(HEX)": for (int i = 0; i < receivedData.Length; i++) { //ToString("X2") 为C#中的字符串格式控制符 //X为 十六进制 //2为 每次都是两位数 content += (receivedData[i].ToString("X2") + " "); } break; case "字符显示(ASCII)": content = Encoding.GetEncoding("GB2312").GetString(receivedData);//防止乱码 break; } //接收文本框 this.txt_Received.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss:ffff")}:{content} \r\n"); serialPort.DiscardInBuffer();//丢弃缓存区数据 } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } else { MessageBox.Show("请打开串口", "Error"); } })); } #endregion } }
12、测试结果
就以上测试结果来看需要的效果以达成。
13、Demo下载
Demo下载地址:C# WinForm虚拟串口通信
本文来自博客园,作者:流纹,转载请注明原文链接:https://www.cnblogs.com/lwk9527/p/17374419.html