当前位置: 首页 > news >正文

C#学习笔记5:简单上位机串口助手的实现

今日尝试使用C#进行 自己编写一款上位机串口助手:

文章提供源码、解释、测试效果、整体工程下载......

以下为串口助手的界面效果:

目录

1、功能设计与摆放控件:

2.遇到的报错解决与基本提示:

C#System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox3”的线程访问它。

设置串口端口停止位波特率等时的数据强制类型转换规范:

串口接收触发异常:

 串口对象的一些常用属性赋值实例:

串口发送信息:

串口接收信息:

3、实际功能展示截图:

4、在没有下位机情况下调试上位机串口的方法:

5、整体工程代码:

6、整体测试工程下载:


 

1、功能设计与摆放控件:

与之前的串口发送不同,我们此次做的是兼顾接收与发送的串口助手,预设功能如下:

1、串口选择

2、波特率

3、接收显示窗口

4、切换十六进制发送接收功能

5、清除接收区

6、必要的交互按钮

最大的接收区我使用的组件是:richTextBox

清除接收区、打开关闭串口、发送等按键使用的组件是:button

16进制显示、发送我使用了可以框选和不选的组件:checkBox

单条发送后的文本输入区我使用的组件是:textBox(相比richTextBox只有一行)

2.遇到的报错解决与基本提示:

C#System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox3”的线程访问它。

这项报错是我  试图   将串口接收到的信息打印在richTextBox1组件时遇到的

解决方案 是在创建窗体时增加代码 禁止编译器对跨线程访问做检查:

          Control.CheckForIllegalCrossThreadCalls = false;

查阅资料网址:

C#System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox3”的线程访问它。” - 知乎

设置串口端口停止位波特率等时的数据强制类型转换规范:

串口接收触发异常:

 在串口部分,每个属性的数据类型不太相同,所以赋值时需要注意一下

我们一般将combox下拉框中的text属性作为设置的相应值赋值给seriesport的某些属性,但数据类型不一定对应,所以赋值的时候需要作对应的强制类型转换:

 对于停止位设置进入串口服务事件函数的相关问题,在以下代码注释中有所说明:

              serialPort1.PortName = comboBox1.Text;//设置端口号serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);//设置端口波特率serialPort1.StopBits = (StopBits)Convert.ToInt32(comboBox3.Text);//设置停止位serialPort1.DataBits = Convert.ToInt32(comboBox4.Text);//设置数据位/*  一定要加上下面这句话。*  意思是接收缓冲区其中假设有一个字节的话就出发接收函数。假设不加上这句话,那就有时候触发接收都发了好多次了也设有触发接收,有时候延时现象等等*/serialPort1.ReceivedBytesThreshold = 1;serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);serialPort1.Open();                   //打开串口

 串口对象的一些常用属性赋值实例:

这也是我查阅网上资料获取的笔记摘录,为的是

SerialPort mySerialPort = new SerialPort("COM2");//端口
mySerialPort.BaudRate = 9600;//波特率
mySerialPort.Parity = Parity.None;//校验位
mySerialPort.StopBits = StopBits.One;//停止位
mySerialPort.DataBits = 8;//数据位
mySerialPort.Handshake = Handshake.Non;
mySerialPort.ReadTimeout = 1500;
mySerialPort.DtrEnable = true;//启用数据终端就绪信息
mySerialPort.Encoding = Encoding.UTF8;
mySerialPort.ReceivedBytesThreshold = 1;//DataReceived触发前内部输入缓冲器的字节数
mySerialPort.DataReceived += new SerialDataReceivedEvenHandler(DataReceive_Method);mySerialPort.Open();//打开串口

串口发送信息:

Write(Byte[], Int32, Int32) :将指定数量的字节写入串行端口
Write(Char[], Int32, Int32) :将指定数量的字符写入串行端口
Write(String) :将指定的字符串写入串行端口
WriteLine(String) :将指定的字符串和NewLine值写入输出缓冲区

// Write a string
port.Write("Hello World");// Write a set of bytes
port.Write(new byte[] { 0x0A, 0xE2, 0xFF }, 0, 3);// Close the port关闭串口
port.Close();

串口接收信息:

Read(Byte[], Int32, Int32):从SerialPort输入缓冲区读取一些字节,并将那些字节写入字节数组中指定的偏移量处
ReadByte():从SerialPort输入缓冲区中同步读取一个字节
ReadChar(): 从SerialPort输入缓冲区中同步读取一个字符
ReadExisting() :在编码的基础上,读取SerialPort对象的流和输入缓冲区中所有立即可用的字节
ReadLine() :一直读取到输入缓冲区中的NewLine值
ReadTo(String) :一直读取到输入缓冲区中的指定value的字符串

查阅资料网址:

C#实现串口通信解析_c#串口通讯-CSDN博客

3、实际功能展示截图:

 主要是逻辑正确的弹窗提示,弹窗是调用函数实现的:

                MessageBox.Show("当前串口有设备连接,串口已成功打开", "串口发送提示");

打开串口检测端口有无设备:

关闭串口:

串口接收测试:

使用单片机发送字符串测试接收功能 :

接收测试成功,效果成功在richTextBox组件打印:

勾选16进制显示设置:

是否被勾选的判断语句如下示例:

                    //如果不是16进制发送,就直接string形式发送if (!checkBox2.Checked)

清除接收区:

清除richTextBox的接收区可以直接调用函数:

            richTextBox1.Clear();

4、在没有下位机情况下调试上位机串口的方法

这里我使用虚拟串口软件,给电脑的COM3与COM4搭建了虚拟的串口连接:

软件下载地址如下:

https://download.csdn.net/download/qq_64257614/89044980

如图使用软件对COM3与COM4进行虚拟连接:

 使用SSCOM串口助手软件成功检测到COM3与COM4的虚拟连接:

 发送测试:

5、整体工程代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Button;namespace Serial_Assistant_YZH
{public partial class Form1 : Form{int CXk1 = 0;//蔡徐坤图片计数器public Form1(){InitializeComponent();serialPort1.Encoding = Encoding.GetEncoding("GB2312");     //串口接收编码Control.CheckForIllegalCrossThreadCalls = false;}private void Form1_Load(object sender, EventArgs e){for(int i=1;i<10;i++)//初始化串口号下拉框内容{comboBox1.Items.Add("COM" + i.ToString()); //添加串口}for (int H = 0; H < 5; H++)//初始化串口波特率下拉框内容{switch (H){case 0: comboBox2.Items.Add("2400");   break;case 1: comboBox2.Items.Add("4800");   break;case 2: comboBox2.Items.Add("9600");   break;case 3: comboBox2.Items.Add("115200"); break;}}for(int j=0;j<3;j++){switch (j){case 0: comboBox3.Items.Add("1"); break;case 1: comboBox3.Items.Add("1.5"); break;case 2: comboBox3.Items.Add("2"); break;}}comboBox1.Text = "COM1";//端口下拉框初始值comboBox2.Text = "9600";//波特率下拉框初始值comboBox3.Text = "1";//停止位comboBox4.Text = "8";//数据位serialPort1.Close();   //关闭串行端口连接}//数据接收显示private void richTextBox1_TextChanged(object sender, EventArgs e){}//打开串口private void button1_Click(object sender, EventArgs e){try{serialPort1.PortName = comboBox1.Text;//设置端口号serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);//设置端口波特率serialPort1.StopBits = (StopBits)Convert.ToInt32(comboBox3.Text);//设置停止位serialPort1.DataBits = Convert.ToInt32(comboBox4.Text);//设置数据位/*  一定要加上下面这句话。*  意思是接收缓冲区其中假设有一个字节的话就出发接收函数。假设不加上这句话,那就有时候触发接收都发了好多次了也设有触发接收,有时候延时现象等等*/serialPort1.ReceivedBytesThreshold = 1;serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);serialPort1.Open();                   //打开串口MessageBox.Show("当前串口有设备连接,串口已成功打开", "串口发送提示");//按键状态置位button1.Enabled = false;button2.Enabled = true;}catch{MessageBox.Show("端口无设备连接", "错误警告");}}//关闭串口private void button2_Click(object sender, EventArgs e){try{serialPort1.Close(); //关闭串口        //按键状态置位button1.Enabled = true;button2.Enabled = false;MessageBox.Show("已关闭串口", "串口发送提示");}catch{}}private void label3_Click(object sender, EventArgs e){}//单条发送数据private void button3_Click(object sender, EventArgs e){byte[] Data = new byte[1];                                                         //单字节发数据     if (serialPort1.IsOpen){if (textBox1.Text != ""){//如果不是16进制发送,就直接string形式发送if (!checkBox2.Checked){try{serialPort1.Write(textBox1.Text);//serialPort1.WriteLine();                             //字符串写入}catch{MessageBox.Show("串口数据写入错误", "错误");}}else                                                                    //数据模式{try                                                                 //如果此时用户输入字符串中含有非法字符(字母,汉字,符号等等,try,catch块可以捕捉并提示){for (int i = 0; i < (textBox1.Text.Length - textBox1.Text.Length % 2) / 2; i++)//转换偶数个{Data[0] = Convert.ToByte(textBox1.Text.Substring(i * 2, 2), 16);           //转换serialPort1.Write(Data, 0, 1);}if (textBox1.Text.Length % 2 != 0){//单独处理最后一个字符Data[0] = Convert.ToByte(textBox1.Text.Substring(textBox1.Text.Length - 1, 1), 16);serialPort1.Write(Data, 0, 1);//写入}//Data = Convert.ToByte(textBox2.Text.Substring(textBox2.Text.Length - 1, 1), 16);}catch{MessageBox.Show("数据转换错误,请输入数字。", "错误");}}}}}//单条发送输入文本框private void textBox1_TextChanged(object sender, EventArgs e){}//串口接收//一个接收数据事件获取串口发送来的数据private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e){//处理事件这块可以加上延时确保不定数的数据可以全部收到缓冲后,才去读缓冲内容--单位: 毫秒Thread.Sleep(50);//如果16进制转换没被勾选if (!checkBox1.Checked){richTextBox1.AppendText(serialPort1.ReadExisting());  //串口类会自动处理汉字,所以不需要特别转换}//如果16进制转换被勾选了else{try{//定义缓冲区数组大小为串口缓冲区数据的字节数//因为串口事件触发时有可能收到不止一个字节byte[] data = new byte[serialPort1.BytesToRead];serialPort1.Read(data, 0, data.Length);foreach (byte Member in data)  //遍历用法{string str = Convert.ToString(Member, 16).ToUpper();richTextBox1.AppendText("0x" + (str.Length == 1 ? "0" + str : str) + " ");}}catch { }}}//蔡徐坤图片private void pictureBox1_Click(object sender, EventArgs e){int CXK = 0;CXk1++;CXK = CXk1 % 5;switch(CXK){case 0: MessageBox.Show("你干嘛~哎呦!", "蔡徐坤");label6.Text = "哎呦!"; break;case 1: MessageBox.Show("唱跳、RAP、篮球、Music!", "蔡徐坤"); label6.Text = "篮球"; break;case 2: MessageBox.Show("鸡你太美~Baby", "蔡徐坤"); label6.Text = "鸡你太美!"; break;case 3: MessageBox.Show("练习时长两年半", "蔡徐坤"); label6.Text = "两年半!"; break;case 4: MessageBox.Show("别点了,认真学习", "蔡徐坤"); label6.Text = "认真学习!"; break;}}//清除接收区private void button4_Click(object sender, EventArgs e){richTextBox1.Clear();MessageBox.Show("已清除接收区", "清除接收区");}//停止位private void comboBox3_SelectedIndexChanged(object sender, EventArgs e){}//数据位private void comboBox4_SelectedIndexChanged(object sender, EventArgs e){}//串口选择private void comboBox1_SelectedIndexChanged(object sender, EventArgs e){}//波特率选择private void comboBox2_SelectedIndexChanged(object sender, EventArgs e){}//private void checkBox1_CheckedChanged(object sender, EventArgs e){}//16进制发送按钮private void checkBox2_CheckedChanged(object sender, EventArgs e){}}
}

6、整体测试工程下载:

https://download.csdn.net/download/qq_64257614/89044991?spm=1001.2014.3001.5503

相关文章:

  • MySQL---触发器
  • Webpack生成企业站静态页面 - 增强数据处理能力
  • Windows前后端部署(达梦,东方通)
  • 数据结构——双向链表
  • 利用图像识别进行疾病诊断
  • 【数据结构】顺序表的实现——静态分配
  • 气象预测新篇章:Python人工智能的变革力量
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • 分类任务中的评估指标:Accuracy、Precision、Recall、F1
  • CUDA从入门到放弃(七):流( Streams)
  • ubuntu 不产生core dump 文件
  • 数据库内数据已清除,刷新后又出现
  • Google 邀请您参加 Build with AI 2024 线下活动
  • 55、Qt/事件机制相关学习20240326
  • Java 多态、包、final、权限修饰符、静态代码块
  • 时间复杂度分析经典问题——最大子序列和
  • 《Java编程思想》读书笔记-对象导论
  • 2017-08-04 前端日报
  • ES6简单总结(搭配简单的讲解和小案例)
  • GitUp, 你不可错过的秀外慧中的git工具
  • JavaScript类型识别
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • Python学习之路13-记分
  • SpingCloudBus整合RabbitMQ
  • Yii源码解读-服务定位器(Service Locator)
  • 成为一名优秀的Developer的书单
  • 服务器之间,相同帐号,实现免密钥登录
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 聊聊hikari连接池的leakDetectionThreshold
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 实现菜单下拉伸展折叠效果demo
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 使用docker-compose进行多节点部署
  • 手写双向链表LinkedList的几个常用功能
  • 一份游戏开发学习路线
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • ​用户画像从0到100的构建思路
  • #{}和${}的区别是什么 -- java面试
  • #Z2294. 打印树的直径
  • $ git push -u origin master 推送到远程库出错
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (力扣)循环队列的实现与详解(C语言)
  • (三十五)大数据实战——Superset可视化平台搭建
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • ..thread“main“ com.fasterxml.jackson.databind.JsonMappingException: Jackson version is too old 2.3.1
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .NET Standard 支持的 .NET Framework 和 .NET Core
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NET4.0并行计算技术基础(1)
  • .Net程序帮助文档制作
  • .net反编译工具