基于winform的串口调试助手

目录

一、串口助手界面设计

1.1 串口配置

1.2 接收配置

1.3 发送配置

1.4 接收窗口和发送窗口

1.5 状态显示窗口

1.6 串口通讯控件

二、程序编写

2.1 端口号自动识别并显示在端口号下拉框

功能说明:

2.2  波特率下拉框显示

2.3 数据位下拉框显示

2.4 校验位下拉框显示

2.5 停止位下拉框显示

2.6 停止位列表数字与serialport1使用的枚举类型对应

2.7 选择的串口名称、波特率、数据位、校验位、停止位参数赋值给serialport1对应的变量

2.8 编写串口打开按钮的点击事件

2.9 串口数据发送程序编写

2.10 串口数据接收程序编写


一、串口助手界面设计

1.1 串口配置

采用groupbox控件,其中包括了串口配置、波特率、数据位、校验位、停止位、RTS、DTR和打开串口控件。

① 串口配置、波特率、数据位、校验位、停止位:combobox控件和对应文字描述的label控件

② RTS、DTR:checkbox控件

③ 打开串口:最熟悉的button控件 哈哈哈

1.2 接收配置

同样采用groupbox控件,支持清空数据、暂停接收、保存数据等功能;可选择十六进制显示,便于查看原始数据格式;可指定保存路径,便于数据。

① 自动清空、16进制:checkbox控件

② 手动清空、暂停、保存数据和选择路径:button控件

③ 文件保存路径:textbox控件

1.3 发送配置

同样采用 GroupBox 控件,支持手动输入数据发送、文件发送以及自动循环发送功能;可选择十六进制格式发送,适用于不同通信协议的调试需求;可设置发送时间间隔,满足周期性发送场景。

① 自动发送、16进制:CheckBox 控件

② 手动发送、清除数据、发送文件和选择路径:Button 控件

③ 文件路径选择及发送周期设定:TextBox 控件

1.4 接收窗口和发送窗口

接收窗口和发送窗口采用的是richtextbox控件

1.5 状态显示窗口

状态接收采用的是statustrip控件,主要包括了状态、接收数量、发送数量和清空计数功能。

1.6 串口通讯控件

使用serialport控件进行串口通讯,本次编写的串口软件主要是依托于serialport控件及其对应的类,本次串口软件中serialport空间名字名为serialport1,后续程序中都是使用该名称进行编写程序。

二、程序编写

2.1 端口号自动识别并显示在端口号下拉框

private void save_btn_Load(object sender, EventArgs e)
{// 将电脑上的串口资源加载到 port_cbb(端口选择下拉框)中this.port_cbb.Items.AddRange(SerialPort.GetPortNames());// 初始化发送和接收区域的提示文字this.sent_rtb.Text = "我是发送区域";this.receive_rtb.Text = "我是接收区域";
}

其中的serialport是system.IO.ports命名空间下的serialport类的一个实例对象,在串口软件页面加载事件中进行创建的实例对象。

 

功能说明:
  • 🔍 自动识别串口:通过 SerialPort.GetPortNames() 获取当前计算机所有可用串口号,并添加到 ComboBox 控件中,供用户选择,提升了使用的便捷性。

  • ✍️ 区域提示信息:在发送区(sent_rtb)和接收区(receive_rtb)初始化时分别添加默认提示文字,让用户一目了然地了解每个区域的功能。

2.2  波特率下拉框显示

直接在波特率下拉框控件中的item属性中设置常用的波特率数值:19200、9600、4800、2400,程序运行以后直接可以根据通讯要求进行选择对应波特率。

2.3 数据位下拉框显示

同上述②,直接在控件的item属性中设置常用的数据长度:5、6、7、8

2.4 校验位下拉框显示

同上述③,直接控件的item属性设置常用的校验位类型:Odd、Even、None、Mark、Space

2.5 停止位下拉框显示

同上述④,直接在控件的item属性中设置常用的停止位类型:1、2、1.5

注意事项:当数据为6、7、8时,停止位只能配置成1或者2;当数据位为5时,停止位只能设置成1或者1.5位。另外,serialport1中使用的停止位是枚举类型,需要将停止位列表中的数字于serialport1中使用的停止位枚举类型进行一一对应。

2.6 停止位列表数字与serialport1使用的枚举类型对应

停止位下拉列表中的数字实际是字符串,serialport1的停止位设置使用“serialport.stopbits”枚举类型进行设置,将字符串和stopbits枚举中的选项进行对应,具体如下所示。

switch (stopbit_cbb.SelectedItem.ToString()){case "1.5":this.serialPort1.StopBits = StopBits.OnePointFive;break;case "1":this.serialPort1.StopBits = StopBits.One;break;case "2":this.serialPort1.StopBits = StopBits.Two;break;}

2.7 选择的串口名称、波特率、数据位、校验位、停止位参数赋值给serialport1对应的变量

this.serialPort1.PortName = this.port_cbb.SelectedItem.ToString();this.serialPort1.BaudRate = Convert.ToInt32(baud_cbb.SelectedItem);this.serialPort1.DataBits = Convert.ToInt32(databit_cbb.SelectedItem);this.serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), checkbit_cbb.Text.ToString());serialPort1.Encoding = Encoding.UTF8;

 其中,serialport.encoding是编码格式,本次的串口助手中使用的utf-8,用于后续串口数据的发送和接收;由于控件中item参数默认的数据类型是字符串,需要将字符串转换成serialport需要的数据,涉及字符串转数字,字符串转枚举。校验位参数是枚举类型,其在serialport1中可设置的值如下所示:

2.8 编写串口打开按钮的点击事件

基本流程如下所示

开始|v
[点击“打开串口”按钮]|v
{openport_btnisopen == false?}| 是v
[配置串口参数]- 串口号(PortName)- 波特率(BaudRate)- 数据位(DataBits)- 校验位(Parity)- 停止位(StopBits,根据选择设置)- 编码格式(UTF-8)|v
[打开串口 serialPort1.Open()]|v
{串口是否成功打开? serialPort1.IsOpen == true}| 是v
[按钮背景设为绿色]
[按钮文字设为“关闭串口”]
[设置 openport_btnisopen = true]|v
结束{openport_btnisopen == false?}| 否v
[关闭串口 serialPort1.Close()]
[按钮背景设为灰色]
[按钮文字设为“打开串口”]
[设置 openport_btnisopen = false]|v
结束

注意:其中的openport_btnisopen时一个全局变量,在串口软件界面初次加载的时候赋值为false。

打开串口按钮事件具体代码如下所示:

private void openport_btn_Click(object sender, EventArgs e){try{if (openport_btnisopen==false){//串口名称、波特率、数据位、校验位、停止位参数设置this.serialPort1.PortName = this.port_cbb.SelectedItem.ToString();this.serialPort1.BaudRate = Convert.ToInt32(baud_cbb.SelectedItem);this.serialPort1.DataBits = Convert.ToInt32(databit_cbb.SelectedItem);this.serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), checkbit_cbb.Text.ToString());serialPort1.Encoding = Encoding.UTF8;//当数据位为6、7、8位时,停止位只能配置成1或2位;同样当数据位为5位时,停止位只能为1或1.5位switch (stopbit_cbb.SelectedItem.ToString()){case "1.5":this.serialPort1.StopBits = StopBits.OnePointFive;break;case "1":this.serialPort1.StopBits = StopBits.One;break;case "2":this.serialPort1.StopBits = StopBits.Two;break;}//打开端口this.serialPort1.Open();if (this.serialPort1.IsOpen ==true){this.openport_btn.BackColor = Color.Green;this.openport_btn.Text = "关闭串口";}openport_btnisopen = true;}else{this.serialPort1.Close();this.openport_btn.BackColor = Color.Gainsboro;this.openport_btn.Text = "打开串口";openport_btnisopen = false;}           //关闭端口}catch (Exception xc){}}

2.9 串口数据发送程序编写

在发送数据之前,需要判断发送框内的数据是否为空和串口通讯是否打开这两个条件。如果条件满足,使用“encoding.Getbytes”函数将发送框内的数据换成字节数据,使用serialport.write函数将转换得到的字节数据发送给对方。

注意:本次串口助手软件中没有补充不满足条件的执行代码,小伙伴可以根据自己的要求进行补充完善。

private void hand_send_btn_Click(object sender, EventArgs e){if (sent_rtb.Text !=null && this.serialPort1.IsOpen){Encoding Chinese = System.Text.Encoding.UTF8;//定义一个可以进行中文编码的变量byte[] writeBytes = Chinese.GetBytes(sent_rtb.Text);this.serialPort1.Write(writeBytes,0, writeBytes.Length);//this.serialPort1.Write(writeBytes,0, writeBytes.Length);}}

2.10 串口数据接收程序编写

这个是B站上up主编写的数据接收程序,使用的编码格式是GB2312,首先在串口数据接收事件中声明了个字节数组,字节数组长度是串口缓存区中可以读取字节的数目,使用串口的read函数,将串口缓存区中的可读字节数据读取并存到字节数组中。同时将字节数组数据存储到全局的列表recivebuffer中,使用invoke将读取的数据在UI界面里的窗口显示区域进行显示(涉及到UI线程和非UI线程的问题,有兴趣的小伙伴可以查点资料看看)。

2.10.1 VSPD软件

这里介绍一个虚拟串口软件Virtual Serial Port Driver,它是 Eltima Software 出品的一款 虚拟串口工具,可以在系统中创建成对的虚拟串口(如 COM3 <-> COM4),广泛用于串口通信调试、设备模拟和串口软件开发中。

具体原理如下:

1.串口通讯调试

开发者可用它来模拟串口设备之间的通信,实现无需真实硬件的调试环境。

  • 例如:开发一款串口通信的软件,但没有真实的串口设备,就可以用 COM3 <-> COM4 虚拟一对串口,一端运行你的软件,另一端用串口助手发送测试数据。


2.串口数据转发、转接协议调试

✅ 可用于协议桥接、数据透传、协议中间件调试

  • 比如:

    • A 程序向 COM5 发数据;

    • B 程序从 COM6 接收;

    • COM5 和 COM6 通过 VSPD 连接;

    • 就可以验证 A 和 B 之间串口协议是否兼容。

具体使用教程可以参考博客:

VSPD工具 - Virtual Serial Port Driver v6.9 激活教程&使用教程 - Citrusliu - 博客园

具体下载链接(网上很多资源):

https://gitcode.com/open-source-toolkit/29e06/?utm_source=tools_gitcode&index=top&type=cardhttps://gitcode.com/open-source-toolkit/29e06/?utm_source=tools_gitcode&index=top&type=card

2.10.2 串口通讯实验

根据上述截图中的串口数据接收代码,自己进行了一定的改写,使得程序能够满足自己串口软件的使用。具体程序如下所示:

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e){//如果暂停按钮被按下则停止接收数据if (receiveisopen == false){return;}if (serialPort1.BytesToRead==0){return;}byte[] z = new byte[this.serialPort1.BytesToRead];this.serialPort1.Read(z,0,z.Length);datahuanchun.AddRange(z);this.Invoke(new EventHandler(delegate{if (hex_receive_ckb.Checked == true){//编写数据采用16进制进行显示}else{//编写数据采用字符串进行显示if (serialPort1.BytesToRead == 0){//判断串口缓存区没有可以在读的字节数据string xx = Encoding.UTF8.GetString(datahuanchun.ToArray());receive_rtb.AppendText(xx);//接收完成后清空datahaunchundatahuanchun.Clear();}}}));

具体实验结果如下图所示:

这个问题如果你用单步调试可能查不出问题,因为单步调试执行一步,中间存在间隔时间,串口传输的线程可以在间隔时间内将全部数据发送给你,当你解码的时候,数据往往是一段完整的数据。

注意事项:在使用串口方式进行数据收发过程中,需要知道串口数据并不是一次性全部发送,它可能是分多批次地给你发送。这个结果就可能导致中文转码时发生乱码问题,为什么这么说呢,听我细细道来:

(1)单个字母和数字对应一个字节

(2)一个中文(UTF8编码)对应三个字节;一个中文(GB2312编码)对应两个字节

举个例子:

举个例子:

假设你串口接收 "你" 字(UTF-8编码为 E4 BD A0):

  • 第一次读取:收到 E4 BD(2个字节,还差1个字节)

  • 第二次读取:收到 A0

程序连续执行时(正常运行):
  • 程序执行速度快,串口缓冲区数据还没完全到齐你就开始处理

  • 比如 UTF-8 中文“你”应为 E4 BD A0,但你先只收到 E4 BD,立刻尝试解码就会报错或乱码。

2.10.3 中文乱码解决方法

以下是程序编写流程图:需要用到lock锁

开始|v
[进入 serialPort1_DataReceived 事件]|v
{是否按下“暂停接收”按钮? (receiveisopen == false)}| 是v
[直接 return,结束接收]|v
结束|否v
{串口接收缓存是否为空? (BytesToRead == 0)}| 是v
[直接 return,结束接收]|否v
[定义接收字节数组 z,并读取数据到 z]|v
[将读取数据加入 datahuanchun 缓存(加锁 lock)]|v
[Invoke 主线程处理接收显示逻辑]|v
{是否勾选“16进制显示”? (hex_receive_ckb.Checked == true)}| 是v
[此处未写实现逻辑,可扩展为 hex 格式显示]|否v
[进入文本显示分支(加锁 datahuanchun)]|v[UTF8 解码缓存数据 → charBuffer]|v[将解码结果追加到接收显示区 receive_rtb]|v[清空 datahuanchun 缓存]|v[更新接收数据统计 senddata_num + data长度]|v[更新状态栏 toolStripStatusLabel4]|v
结束

等后面串口软件写好后,再上传下一部分内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/75751.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker基础2

如需转载&#xff0c;标记出处 本次我们将下载一个 Docker 镜像&#xff0c;从镜像中启动容器 上一章&#xff0c;安装 Docker 时&#xff0c;获得两个主要组件&#xff1a; Docker 客户端 Docker 守护进程&#xff08;有时称为“服务器”或“引擎”&#xff09; 守护进程实…

Rocketmq2

一、生产者端防丢失 1. 发送方式选择 同步发送&#xff1a;使用 send() 方法&#xff0c;等待 Broker 确认响应&#xff08;SendResult&#xff09;&#xff0c;确保消息已成功发送。异步发送&#xff1a;使用 sendAsync() 方法并设置回调函数&#xff0c;处理发送成功 / 失败…

RabbitMQ详解,RabbitMQ是什么?架构是怎样的?

目录 一,RabbitMQ是什么? 二,RabbitMQ架构 2.1 首先我们来看下RabbitMQ里面的心概念Queue是什么? 2.2 交换器Exchange 2.3 RabbitMQ是什么? 2.4 重点看下优先级队列是什么? 三,RabbitMQ集群 3.1 普通集群模式 3.2 镜像队列集群 一,RabbitMQ是什么? 假设我们程序…

【一步步开发AI运动APP】六、运动计时计数能调用

之前我们为您分享了【一步步开发AI运动小程序】开发系列博文&#xff0c;通过该系列博文&#xff0c;很多开发者开发出了很多精美的AI健身、线上运动赛事、AI学生体测、美体、康复锻炼等应用场景的AI运动小程序&#xff1b;为了帮助开发者继续深耕AI运动领域市场&#xff0c;今…

MySQL——DQL的多表查询

一、交叉连接 标准语法&#xff1a;select * from 表1 cross join 表2 where 表1.公共列 表2.公共列; 简单语法&#xff1a;select * from 表1 , 表2 where 表1.公共列 表2.公共列; 公共列&#xff1a;两张表具有相同含义的列&#xff0c;不是列名一样。 …

【Linux内核】如何更加优雅阅读Linux内核源码(vscode)

1. 前言 因为已经习惯在Ubuntu下进行嵌入式工作开发&#xff0c;但Linux源码在Source Insight下进行阅读&#xff0c;一直很苦恼Linux/Windows来回切换的开发方式&#xff0c;当前发现可以通过 vscode clangd(扩展组件) 方式进行更好的内核源码阅读。 2. 环境 操作系统&…

21.OpenCV获取图像轮廓信息

OpenCV获取图像轮廓信息 在计算机视觉领域&#xff0c;识别和分析图像中的对象形状是一项基本任务。OpenCV 库提供了一个强大的工具——轮廓检测&#xff08;Contour Detection&#xff09;&#xff0c;它能够帮助我们精确地定位对象的边界。这篇博文将带你入门 OpenCV 的轮廓…

LETTERS(DFS)

【题目描述】 给出一个rowcolrowcol的大写字母矩阵&#xff0c;一开始的位置为左上角&#xff0c;你可以向上下左右四个方向移动&#xff0c;并且不能移向曾经经过的字母。问最多可以经过几个字母。 【输入】 第一行&#xff0c;输入字母矩阵行数RR和列数SS&#xff0c;1≤R,S≤…

Day2-2:前端项目uniapp壁纸实战

再在wallpaper新建一个目录components 在components下新建组件common-title 记得点击创建同名目录 在index加 <view class"select"><common-title></common-title></view> 图片换了下&#xff0c;原来的有点丑&#xff0c;图片可按自己喜欢…

其他 vector 操作详解(四十)

介绍 除去向 vector 添加元素&#xff08;如 push_back&#xff09;之外&#xff0c;vector 还提供了许多其他操作&#xff0c;这些操作大多与 string 的操作类似。通过掌握这些操作&#xff0c;我们可以方便地查询、修改和比较 vector 中的元素&#xff0c;从而构建灵活、高效…

【Leetcode 每日一题】368. 最大整除子集

问题背景 给你一个由 无重复 正整数组成的集合 n u m s nums nums&#xff0c;请你找出并返回其中最大的整除子集 a n s w e r answer answer&#xff0c;子集中每一元素对 ( a n s w e r [ i ] , a n s w e r [ j ] ) (answer[i], answer[j]) (answer[i],answer[j]) 都应当…

python基础-13-处理excel电子表格

文章目录 【README】【13】处理Excel电子表格【13.1】Excel文档【13.2】安装openpyxl模块【13.3】读取Excel文档【13.3.1】使用openpyxl模块打开excel文档【13.3.2】从工作簿取得工作表【13.3.3】从工作表sheet获取单元格cell【13.3.5】从表中获取行和列【13.3.6】工作簿、工作…

ABS函数c++

简介&#xff1a; abs 函数用于计算一个数的绝对值&#xff0c;在 C 中它继承自 C 语言的标准库&#xff0c;其历史可以追溯到早期的 C 语言发展历程&#xff0c;以下是详细介绍&#xff1a; 早期编程语言的需求 在计算机编程的早期阶段&#xff0c;处理数学运算就是一项基本…

闭环SOTA!北航DiffAD:基于扩散模型实现端到端自动驾驶「多任务闭环统一」

端到端自动驾驶目前是有望实现完全自动驾驶的一条有前景的途径。然而&#xff0c;现有的端到端自动驾驶系统通常采用主干网络与多任务头结合的方式&#xff0c;但是它们存在任务协调和系统复杂度高的问题。为此&#xff0c;本文提出了DiffAD&#xff0c;它统一了各种驾驶目标并…

整车CAN网络和CANoe

车载网络中主要包含有Can网络,Lin网络,FlexRay,Most,以太网。 500kbps:500波特率,表示的数据传输的速度。表示的是最大的网速传输速度。也就是每秒 500kb BodyCan车身Can InfoCan娱乐信息Can 车身CAN主要连接的是ESB电动安全带 ADB自适应远光灯等 PTCan动力Can 底盘Can

实战设计模式之迭代器模式

概述 与上一篇介绍的解释器模式一样&#xff0c;迭代器模式也是一种行为设计模式。它提供了一种方法来顺序访问一个聚合对象中的各个元素&#xff0c;而无需暴露该对象的内部表示。简而言之&#xff0c;迭代器模式允许我们遍历集合数据结构中的元素&#xff0c;而不必了解这些集…

JVM 垃圾回收器是如何判断一个对象是否要回收?

JVM 垃圾回收器&#xff08;Garbage Collector&#xff09;需要判断哪些对象是“垃圾”&#xff0c;即不再被程序使用的对象&#xff0c;以便回收它们占用的内存。JVM 主要使用以下两种方法来判断对象是否是垃圾&#xff1a; 1. 引用计数算法 (Reference Counting): 原理&…

kali——httrack

目录 前言 使用教程 前言 HTTrack 是一款运行于 Kali Linux 系统中的开源网站镜像工具&#xff0c;它能将网站的页面、图片、链接等资源完整地下载到本地&#xff0c;构建出一个和原网站结构相似的离线副本。 使用教程 apt install httrack //安装httrack工具 httrac…

kotlin函数类型

一 函数类型定义 1 定义 函数类型就是 (Int, Int) -> Int 函数类型其实就是将函数的 “参数类型” 和 “返回值类型” 抽象出来 2 示例 &#xff1a; (Int, Int) -> Int 表示接收两个 Int 参数并返回 Int 的函数类型&#xff1b; (String) -> Unit 表示接收 Strin…

C# Winform 入门(9)之如何封装并调用dll

封装dll 首先创建 .Net平台 类库 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace _09.Encapsulation_dll {public class Program{/// <summary>/// 求两个double类型的数值的和/// &l…