C#开发串口调试助手的详细教程

news/2024/12/23 2:30:19/

一、串口助手是什么?

通过电脑串口(包括USB口)收发数据并且显示的应用软件,一般用于电脑与嵌入式系统的通讯,借助于它来调试串口通讯或者系统的运行状态。也可以用于采集其他系统的数据,用于观察系统的运行情况。

简单的串口助手只能用来接收可发送串口数据,但是当你学会了如何开发串口助手,就可以根据通信协议开发上位机控制硬件和查看硬件的状态,先介绍如何开发一个简单的串口助手。

二、开发环境

  1. Visual Stdio
  2. C#

开发串口助手会用到Winform的相关知识,想要学习Winform的相关知识可以点击链接学习:http://c.biancheng.net/csharp/winform/

三、开发步骤

1. 串口参数

要连接上串口让并实现串口通信我们首先要搜索并选择需要进行通信的串口号,然后设置波特率、数据位、停止位和检验位串口号、波特率、数据位、停止位和检验位相当于打开特定串口的简单的密码,设置好之后就能打开串口进行通信。

名称作用常用数值
波特率串口通信的速率(发送和接收各数据位的间隔时间)4800
9600
14400
19200
38400
56000
57600
115200
128000
256000
数据位表示一组数据实际包含的数据位数5
6
7
8
停止位停止位在最后,用以标志一个字符传送的结束,表示数据帧之间的间隔1
1.5
2
None
校验位用于数据验证Even
Odd
None
数据位概述图
数据位概述图

2.UI设计

一个串口助手界面主要由参数设置、接收框、发送框三个部分以及一些控制按钮组成,参数设置使用ComboBox组件实现,接收和发送数据框使用RichTextBox组件实现,按钮使用Button组件实现,还有串口可以使用自带的SerialPort组件。
在这里插入图片描述

3.创建工程

(1)点击新建项目->选择Windows窗体应用->点击下一步->填写好项目名称和什么位置->点击创建
在这里插入图片描述
在这里插入图片描述
(2)实现UI
创建好项目后左边是工具栏,可以直接拖拉组件,里面有我们要用到的Label、ComboBox、RichTextBox、Button组件,可以根据字母排序查找需要的组件。右边是用来设置组件参数,按F4可以出现该框,可以设置组件名称、文件内容、颜色、大小、位置、字体、图标等参数,点击闪电标志里面可以设置绑定事件。
创建好项目
注意UI实现的过程中要设置好组件的Name,下面是我设置的一些Name参数

组件Name
ComboBoxcomboBox_PortNames
comboBox_BaudRate
comboBox_DataBit
comboBox_StopBit
comboBox_Parity
RichTextBoxrichTextBox_Receive
richTextBox_Send
Buttonbutton_Serach
button_OpenOrClose
button_CLEAR
button_Send

注意问题
串口波特率、数据位、停止位和校验位参数可以预先设置好,这里给出波特率参数设置,其他参数设置的方法一样
在这里插入图片描述
SerialPort组件使用方法
请添加图片描述
基本实现好的UI(审美不在线),大家可以自行优化
串口助手
点击启动的运行效果
运行效果
(3)功能代码实现
代码要实现的功能主要有:
在这里插入图片描述

  • 1.初始化函数
    编写初始化函数时要在Form1.Designer.cs文件的Windows 窗体设计器生成的代码里面添加this.Load += new System.EventHandler(this.Form1_Load);如下图所示
    在这里插入图片描述
		/**************************************************************************/#region 初始化窗体private void Form1_Load(object sender, EventArgs e) //加载界面程序{try{string[] str = SerialPort.GetPortNames();   //获取连接到电脑的串口号并存进数组comboBox_PortNames.Items.Clear();           //清除串口号下拉框的内容comboBox_PortNames.Items.AddRange(str);     //将串口号添加到下拉框if (str.Length > 0){comboBox_PortNames.SelectedIndex = 0;   //设置ComboBox框的初始值comboBox_BaudRate.SelectedIndex = 7;comboBox_DataBit.SelectedIndex = 3;comboBox_StopBit.SelectedIndex = 1;comboBox_Parity.SelectedIndex = 2;}else{MessageBox.Show("当前无串口连接!");}}catch{MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");}}#endregion/**************************************************************************/
  • 2.搜索串口
/**************************************************************************/#region 搜索串口(与初始化函数一样)private void button_Serach_Click(object sender, EventArgs e){try{string[] str = SerialPort.GetPortNames();comboBox_PortNames.Items.Clear();comboBox_PortNames.Items.AddRange(str);if (str.Length > 0){comboBox_PortNames.SelectedIndex = 0;comboBox_BaudRate.SelectedIndex = 7;comboBox_DataBit.SelectedIndex = 3;comboBox_StopBit.SelectedIndex = 1;comboBox_Parity.SelectedIndex = 2;}else{MessageBox.Show("当前无串口连接!");}}catch{MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");}}#endregion/**************************************************************************/
  • 3.打开/关闭串口
/**************************************************************************/#region 打开串口private void button_OpenOrClose_Click(object sender, EventArgs e){if (!serialPort1.IsOpen){if (comboBox_PortNames.SelectedItem == null){MessageBox.Show("请选择正确的串口", "提示");return;}//设置串口参数serialPort1.PortName = comboBox_PortNames.Text.ToString();  //serialPort1是serialPort组件的Name serialPort1.BaudRate = Convert.ToInt32(comboBox_BaudRate.SelectedItem.ToString());serialPort1.DataBits = Convert.ToInt32(comboBox_DataBit.SelectedItem.ToString());//设置停止位if (comboBox_StopBit.Text == "One"){serialPort1.StopBits = StopBits.One;} else if (comboBox_StopBit.Text == "Two"){serialPort1.StopBits = StopBits.Two;} else if (comboBox_StopBit.Text == "OnePointFive"){serialPort1.StopBits = StopBits.OnePointFive;} else if (comboBox_StopBit.Text == "None"){serialPort1.StopBits = StopBits.None;}//设置奇偶校验位if (comboBox_Parity.Text == "Odd"){serialPort1.Parity = Parity.Odd;}else if (comboBox_Parity.Text == "Even"){serialPort1.Parity = Parity.Even;}else if (comboBox_Parity.Text == "None"){serialPort1.Parity = Parity.None;}try{//禁止操作组件comboBox_PortNames.Enabled = false;comboBox_BaudRate.Enabled = false;comboBox_DataBit.Enabled = false;comboBox_StopBit.Enabled = false;comboBox_Parity.Enabled = false;button_Serach.Enabled = false;serialPort1.Open(); //设置完参数后打开串口button_OpenOrClose.Text = "Close";  //更改Open按钮文本内容}catch{MessageBox.Show("串口打开失败!");}serialPort1.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceive);  //打开串口后绑定数据接收}else if (button_OpenOrClose.Text == "Close") {try{//允许操作组件comboBox_PortNames.Enabled = true;comboBox_BaudRate.Enabled = true;comboBox_DataBit.Enabled = true;comboBox_StopBit.Enabled = true;comboBox_Parity.Enabled = true;button_Serach.Enabled = true;serialPort1.DiscardInBuffer();  //清除缓冲区的数据serialPort1.Close();button_OpenOrClose.Text = "Open";   //更改Close按钮文本内容}catch{MessageBox.Show("串口打开失败!");}}}#endregion/**************************************************************************/
  • 4.接收数据
/**************************************************************************/#region 接收串口数据/*开辟缓存区 根据具体协议内容获取一帧数据进行接收中断处理*///变量List<byte> sp_buffer = new List<byte>(4096);    //串口缓存区int sp_buffer_max = 4096;   //串口缓存区最大缓存字节数private void SerialDataReceive(object sender, SerialDataReceivedEventArgs e){if (serialPort1.IsOpen == false){serialPort1.Close();return;}int Byte_len = serialPort1.BytesToRead; //读取缓存的数据长度byte[] Rc_byte = new byte[Byte_len];serialPort1.Read(Rc_byte,0,Byte_len);   //将缓存数据存储进字节数组里面if (sp_buffer.Count > sp_buffer_max)    //缓存超过字节数 先丢弃前面的字节  sp_buffer.RemoveRange(0,sp_buffer_max); //丢弃前面的字节0到sp_buffer_maxsp_buffer.AddRange(Rc_byte);    //存入缓存区byte[] ruffer = new byte[9192]; //用来存放缓冲区的数据流//对数据流进行筛选,缓冲区每一组数据个数大于4则为我们想要的数据流if (sp_buffer.Count > 4)    {sp_buffer.CopyTo(0,ruffer,0,sp_buffer.Count);Task.Run(()=>printf_data(ruffer, sp_buffer.Count, 1));  //打印数据流}}#endregion/**************************************************************************/
  • 5.打印数据流
/**************************************************************************/#region 打印数据流void printf_data(byte[] Frame, int Length, int T_R)   //打印串口数据{Int16 i_len;StringBuilder s = new StringBuilder();if (T_R == 0)s.Append("发送:");elses.Append("接收:");for (i_len = 0; i_len < Length; i_len++)       //打印字符串{s.Append(Frame[i_len].ToString("X2"));s.Append(" ");}s.Append("[" + DateTime.Now.ToString("HH:mm:ss fff") + "]");s.Append("\r\n");string str_show = s.ToString();MethodInvoker mi = new MethodInvoker(() =>{if (richTextBox_ReceiveBox.Lines.Count() > 20)richTextBox_ReceiveBox.Clear();richTextBox_ReceiveBox.AppendText(str_show);});BeginInvoke(mi);/*textBox_com_data.Focus(); //获取焦点textBox_com_data.Select(textBox_com_data.TextLength, 0);//光标textBox_com_data.ScrollToCaret(); //滚动条*/}#endregion/**************************************************************************/
  • 6.发送数据
/**************************************************************************/#region 发送数据private void button_Send_Click(object sender, EventArgs e){if (serialPort1.IsOpen){string[] sendbuff = richTextBox1.Text.Split();  //分割输入的字符串,判断有多少个字节需要发送int Buff_Len = sendbuff.Length;byte[] buff = new byte[Buff_Len];for (int i = 0; i < sendbuff.Length; i++){buff[i] = byte.Parse(sendbuff[i], System.Globalization.NumberStyles.HexNumber); //格式化字符串为十六进制数值}try{serialPort1.Write(buff, 0, buff.Length);    //写数据Task.Run(()=>printf_data(buff, Buff_Len, 0));}catch{MessageBox.Show("发送失败!!");}}else {MessageBox.Show("串口未打开!");}}#endregion/**************************************************************************/
  • 7.清除接收框
/**************************************************************************/#region 清除输出框private void Clear_Click(object sender, EventArgs e){richTextBox_ReceiveBox.Clear();     //清楚数据接收框数据}#endregion/**************************************************************************/

功能函数写好后绑定按钮和函数
在这里插入图片描述
(4)全部代码整合

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.IO;namespace Serial
{public partial class Form1 : Form{public Form1(){InitializeComponent();}/**************************************************************************/#region 初始化窗体private void Form1_Load(object sender, EventArgs e) //加载界面程序{try{string[] str = SerialPort.GetPortNames();   //获取连接到电脑的串口号并存进数组comboBox_PortNames.Items.Clear();           //清除串口号下拉框的内容comboBox_PortNames.Items.AddRange(str);     //将串口号添加到下拉框if (str.Length > 0){comboBox_PortNames.SelectedIndex = 0;   //设置ComboBox框的初始值comboBox_BaudRate.SelectedIndex = 7;comboBox_DataBit.SelectedIndex = 3;comboBox_StopBit.SelectedIndex = 0;comboBox_Parity.SelectedIndex = 2;}else{MessageBox.Show("当前无串口连接!");}}catch{MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");}}#endregion/**************************************************************************//**************************************************************************/#region 搜索串口(与初始化函数一样)private void button_Serach_Click(object sender, EventArgs e){try{string[] str = SerialPort.GetPortNames();comboBox_PortNames.Items.Clear();comboBox_PortNames.Items.AddRange(str);if (str.Length > 0){comboBox_PortNames.SelectedIndex = 0;comboBox_BaudRate.SelectedIndex = 7;comboBox_DataBit.SelectedIndex = 3;comboBox_StopBit.SelectedIndex = 0;comboBox_Parity.SelectedIndex = 2;}else{MessageBox.Show("当前无串口连接!");}}catch{MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");}}#endregion/**************************************************************************//**************************************************************************/#region 打开串口private void button_OpenOrClose_Click(object sender, EventArgs e){if (!serialPort1.IsOpen){if (comboBox_PortNames.SelectedItem == null){MessageBox.Show("请选择正确的串口", "提示");return;}//设置串口参数serialPort1.PortName = comboBox_PortNames.Text.ToString();  //serialPort1是serialPort组件的Name serialPort1.BaudRate = Convert.ToInt32(comboBox_BaudRate.SelectedItem.ToString());serialPort1.DataBits = Convert.ToInt32(comboBox_DataBit.SelectedItem.ToString());//设置停止位if (comboBox_StopBit.Text == "One"){serialPort1.StopBits = StopBits.One;}else if (comboBox_StopBit.Text == "Two"){serialPort1.StopBits = StopBits.Two;}else if (comboBox_StopBit.Text == "OnePointFive"){serialPort1.StopBits = StopBits.OnePointFive;}else if (comboBox_StopBit.Text == "None"){serialPort1.StopBits = StopBits.None;}//设置奇偶校验位if (comboBox_Parity.Text == "Odd"){serialPort1.Parity = Parity.Odd;}else if (comboBox_Parity.Text == "Even"){serialPort1.Parity = Parity.Even;}else if (comboBox_Parity.Text == "None"){serialPort1.Parity = Parity.None;}try{//禁止操作组件comboBox_PortNames.Enabled = false;comboBox_BaudRate.Enabled = false;comboBox_DataBit.Enabled = false;comboBox_StopBit.Enabled = false;comboBox_Parity.Enabled = false;button_Serach.Enabled = false;serialPort1.Open(); //设置完参数后打开串口button_OpenOrClose.Text = "Close";  //更改Open按钮文本内容}catch{MessageBox.Show("串口打开失败!");}serialPort1.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceive);  //打开串口后绑定数据接收}else if (button_OpenOrClose.Text == "Close"){try{//允许操作组件comboBox_PortNames.Enabled = true;comboBox_BaudRate.Enabled = true;comboBox_DataBit.Enabled = true;comboBox_StopBit.Enabled = true;comboBox_Parity.Enabled = true;button_Serach.Enabled = true;serialPort1.DiscardInBuffer();  //清除缓冲区的数据serialPort1.Close();button_OpenOrClose.Text = "Open";   //更改Close按钮文本内容}catch{MessageBox.Show("串口打开失败!");}}}#endregion/**************************************************************************//**************************************************************************/#region 接收串口数据/*开辟缓存区 根据具体协议内容获取一帧数据进行接收中断处理*///变量List<byte> sp_buffer = new List<byte>(4096);    //串口缓存区int sp_buffer_max = 4096;   //串口缓存区最大缓存字节数private void SerialDataReceive(object sender, SerialDataReceivedEventArgs e){if (serialPort1.IsOpen == false){serialPort1.Close();return;}int Byte_len = serialPort1.BytesToRead; //读取缓存的数据长度byte[] Rc_byte = new byte[Byte_len];serialPort1.Read(Rc_byte, 0, Byte_len);   //将缓存数据存储进字节数组里面if (sp_buffer.Count > sp_buffer_max)    //缓存超过字节数 先丢弃前面的字节  sp_buffer.RemoveRange(0, sp_buffer_max); //丢弃前面的字节0到sp_buffer_maxsp_buffer.AddRange(Rc_byte);    //存入缓存区byte[] ruffer = new byte[9192]; //用来存放缓冲区的数据流//对数据流进行筛选,缓冲区每一组数据个数大于4则为我们想要的数据流if (sp_buffer.Count > 4){sp_buffer.CopyTo(0, ruffer, 0, sp_buffer.Count);Task.Run(() => printf_data(ruffer, sp_buffer.Count, 1));  //打印数据流}}#endregion/**************************************************************************//**************************************************************************/#region 打印数据流void printf_data(byte[] Frame, int Length, int T_R)   //打印串口数据{Int16 i_len;StringBuilder s = new StringBuilder();if (T_R == 0)s.Append("发送:");elses.Append("接收:");for (i_len = 0; i_len < Length; i_len++)       //打印字符串{s.Append(Frame[i_len].ToString("X2"));s.Append(" ");}s.Append("[" + DateTime.Now.ToString("HH:mm:ss fff") + "]");s.Append("\r\n");string str_show = s.ToString();MethodInvoker mi = new MethodInvoker(() =>{if (richTextBox_ReceiveBox.Lines.Count() > 20)richTextBox_ReceiveBox.Clear();richTextBox_ReceiveBox.AppendText(str_show);});BeginInvoke(mi);/*textBox_com_data.Focus(); //获取焦点textBox_com_data.Select(textBox_com_data.TextLength, 0);//光标textBox_com_data.ScrollToCaret(); //滚动条*/}#endregion/**************************************************************************//**************************************************************************/#region 发送数据private void button_Send_Click(object sender, EventArgs e){if (serialPort1.IsOpen){string[] sendbuff = richTextBox_Send.Text.Split();  //分割输入的字符串,判断有多少个字节需要发送int Buff_Len = sendbuff.Length;byte[] buff = new byte[Buff_Len];for (int i = 0; i < sendbuff.Length; i++){buff[i] = byte.Parse(sendbuff[i], System.Globalization.NumberStyles.HexNumber); //格式化字符串为十六进制数值}try{serialPort1.Write(buff, 0, buff.Length);    //写数据Task.Run(() => printf_data(buff, Buff_Len, 0));}catch{MessageBox.Show("发送失败!!");}}else{MessageBox.Show("串口未打开!");}}#endregion/**************************************************************************//**************************************************************************/#region 清除输出框private void Clear_Click(object sender, EventArgs e){richTextBox_ReceiveBox.Clear();     //清楚数据接收框数据}#endregion/**************************************************************************/}
}

(5)实现效果
在这里插入图片描述
到这我们就完整地做一个简单的串口助手了,我们可以进行二次开发,添加按钮绑定特定的的指令,每次可以通过点击按钮控制硬件,或者对接收到的数据流进行解析并显示出来,这样就能够做出项目需要的上位机了。


http://www.ppmy.cn/news/901819.html

相关文章

C++11左值和右值、左值引用和右值引用浅析

从字面意思来讲&#xff0c;左值就是“能用在赋值语句等号左侧的内容&#xff08;它得代表一个地址&#xff09;”&#xff1b;右值就是不能作为左值的值&#xff0c;即右值不能出现在赋值语句中等号的左侧。C中的一条表达式&#xff0c;要么就是右值&#xff0c;要么就是左值&…

Windows上 万能的串口调试助手

之前自己找了很久的串口调试助手&#xff0c;还自己去编写&#xff0c;现在发现了一个非常好用的串口工具&#xff1a;ScriptCommunicator。 这是一个开源软件&#xff0c;功能强大。 ScriptCommunicator软件下载地址&#xff1a;https://github.com/szieke/ScriptCommunicat…

漏洞复现-CVE-2023-33246 Apache RocketMQ RCE漏洞原理与复现

目录 漏洞原理漏洞描述影响范围 Apache RocketMQ学习文档学习代码审计 漏洞复现docker环境搭建exp代码 总结参考 漏洞原理 漏洞描述 For RocketMQ versions 5.1.0 and below, under certain conditions, there is a risk of remote command execution. Several components of…

修复漏洞(二)离线升级Tomcat版本

前言 生产环境无法联网&#xff0c;只能通过下载离线版本更新Tomcat到小版本最新注意Tomcat10和11与jdk1.8都不兼容&#xff0c;只能更新到小版本的最新前提是按照我这种方法配置Tomcat开机自启的https://blog.csdn.net/qq_44648936/article/details/130022136 步骤 备份整个…

软件整理

一、 https://www.microsoft.com/zh-CN/download/details.aspx?id3702 下载虚拟pc。安装后&#xff0c;全部下一步&#xff0c;最后&#xff0c;右击虚拟pc&#xff0c;设置&#xff0c;在光驱里面选中你的iso文件。这样就可以安装一个虚拟的pc端。 还有修改mac的方法。 在打开…

从零开始整合spingMVC

111 转载于:https://www.cnblogs.com/doublekiller/p/5743233.html

可以清理掉重复文件的PC软件

下载并使用DoubleKiller进行重复文件的清理。

重复文件清理绿色工具——DoubleKiller

小巧实用的重复文件清理绿色工具——DoubleKiller DoubleKiller Pro Download