C# 简易的串口监视上位机实现
实现上位机和下位机之间的通信,通常使用的是串口通信,接下来实现一个通过上位机和串口调试助手来完成串口通信测试。
首先创建一个WInfrom窗体应用工程文件,创建过程可参考https://www.cnblogs.com/xionglaichuangyichuang/p/13734179.html;
在创建好的工程下面,通过工具箱中已有的控件完成界面的搭建,如下图所示,为了方便初学者容易看懂程序,下图将控件的命名一并标注出来:

直接进入正题,将完整的工程代码黏贴出来:

1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
10 using System.IO.Ports;
11 using System.Diagnostics;
12
13 namespace Tem_Hum_Monitorring
14 {
15
16 public partial class Form1 : Form
17 {
18 //实例化串口
19 SerialPort s = new SerialPort();
20
21 public Form1()
22 {
23 InitializeComponent();
24 Control.CheckForIllegalCrossThreadCalls = false;
25 button1.Text = "打开串口";
26 int[] item = { 9600,115200}; //遍历
27 foreach (int a in item)
28 {
29 comboBox2.Items.Add(a.ToString());
30 }
31 comboBox2.SelectedItem = comboBox2.Items[1];
32 }
33
34 private void Form1_Load(object sender, EventArgs e)
35 {
36 portInit();
37 }
38
39 /// <summary>
40 /// 串口初始化
41 /// </summary>
42 private void portInit()
43 {
44 string[] ports = SerialPort.GetPortNames();
45 comboBox1.Items.AddRange(ports);
46 comboBox1.SelectedItem = comboBox1.Items[0];
47 }
48
49 #region 开关串口
50 private void button1_Click(object sender, EventArgs e)
51 {
52 try
53 {
54 if (!s.IsOpen)
55 {
56 s.PortName = comboBox1.SelectedItem.ToString();
57 s.BaudRate = Convert.ToInt32(comboBox2.SelectedItem.ToString());
58 s.Open();
59 s.DataReceived += s_DataReceived; //"+="代表指定响应事件时要调用的方法
60 button1.Text = "关闭串口";
61 }
62 else
63 {
64 s.Close();
65 s.DataReceived -= s_DataReceived;
66 button1.Text = "打开串口";
67 }
68 }
69 catch(Exception ee)
70 {
71 MessageBox.Show(ee.ToString());
72 }
73 }
74 #endregion
75
76 #region 串口接收
77 void s_DataReceived(object sender, SerialDataReceivedEventArgs e)
78 {
79 int count = s.BytesToRead;
80 string str = null;
81 if (count == 8)
82 {
83 //数据解析
84 byte[] buff = new byte[count];
85 s.Read(buff, 0, count);
86 foreach (byte item in buff)
87 {
88 str += item.ToString("X2") + " ";
89 }
90 richTextBox1.Text = "[" + System.DateTime.Now.ToString() + "] " + str + "\n" + richTextBox1.Text;
91 if (buff[0] == 0x04)
92 {
93 ID.Text = buff[0].ToString();
94 switch (buff[2])
95 {
96 case 0x01:
97 {
98 Tem.Text = (buff[5] * 4 + buff[4] * 0.05 - 30).ToString();
99 Hum.Text = (buff[6] + buff[7]).ToString();
100 break;
101 }
102 case 0x02:
103 {
104 Light.Text = (buff[6] + buff[7]).ToString();
105 break;
106 }
107 case 0x04:
108 {
109 Dust.Text = (buff[6] + buff[7]).ToString();
110 break;
111 }
112 default:
113 break;
114 }
115 }
116 }
117 else
118 {
119 //当接收数据不在设定的数据位范围之内时,会出现接受到的数据一直保存在接收缓存区之内,后续每次接手数据都会将上一次的数据进行叠加,造成只能通过关闭串口的方法来清除缓冲区的数据
120 s.DiscardInBuffer(); //丢弃来自串行驱动程序的接收缓冲区的数据
121 }
122 }
123 #endregion
124
125 #region 串口发送
126 private void button3_Click(object sender, EventArgs e)
127 {
128 string[] sendbuff = richTextBox2.Text.Split();
129 Debug.WriteLine("发送字节数:" + sendbuff.Length);
130 foreach (string item in sendbuff)
131 {
132 int count = 1;
133 byte[] buff = new byte[count];
134 buff[0] = byte.Parse(item, System.Globalization.NumberStyles.HexNumber);
135 s.Write(buff,0,count);
136 }
137 }
138 #endregion
139
140 private void button2_Click(object sender, EventArgs e)
141 {
142 int count = 1;
143 byte[] buff = new byte[count];
144 buff[0] = byte.Parse("04", System.Globalization.NumberStyles.HexNumber);
145 s.Write(buff, 0, count);
146 }
147 }
148 }
在Winfrom窗体设计中,实现串口可以通过工具箱中的串口控件来实现,不过一般推荐直接通过代码来实例化串口,实例化串口需使用如下代码来实现:
//实例化串口
SerialPort s = new SerialPort();
串口初始化可以在窗体的Load函数中实现,以下初始化可以自动化取当前设备中的存在的串口,包括真实串口和虚拟串口:
private void Form1_Load(object sender, EventArgs e)
{
portInit();
} /// <summary>
/// 串口初始化
/// </summary>
private void portInit()
{
string[] ports = SerialPort.GetPortNames();
comboBox1.Items.AddRange(ports);
comboBox1.SelectedItem = comboBox1.Items[0];
}
通过对开关按键button1控件的点击事件,实现串口的开关,通过对控件的文字修改,可以实现一个控件机能实现开又能实现关串口的作用:
#region 开关串口
private void button1_Click(object sender, EventArgs e)
{
try
{
if (!s.IsOpen)
{
s.PortName = comboBox1.SelectedItem.ToString();
s.BaudRate = Convert.ToInt32(comboBox2.SelectedItem.ToString());
s.Open();
s.DataReceived += s_DataReceived; //"+="代表指定响应事件时要调用的方法
button1.Text = "关闭串口";
}
else
{
s.Close();
s.DataReceived -= s_DataReceived;
button1.Text = "打开串口";
}
}
catch(Exception ee)
{
MessageBox.Show(ee.ToString());
}
}
#endregion
串口数据接收和数据解析,首先获取数据接收缓存区数据的字节长度,通过确认长度是否是设定中的长度大小,如果是设定的8位数据长度则对接收的数据进行解析:
#region 串口接收
void s_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int count = s.BytesToRead;
string str = null;
if (count == 8)
{
//数据解析
byte[] buff = new byte[count];
s.Read(buff, 0, count);
foreach (byte item in buff)
{
str += item.ToString("X2") + " ";
}
richTextBox1.Text = "[" + System.DateTime.Now.ToString() + "] " + str + "\n" + richTextBox1.Text;
if (buff[0] == 0x04)
{
ID.Text = buff[0].ToString();
switch (buff[2])
{
case 0x01:
{
Tem.Text = (buff[5] * 4 + buff[4] * 0.05 - 30).ToString();
Hum.Text = (buff[6] + buff[7]).ToString();
break;
}
case 0x02:
{
Light.Text = (buff[6] + buff[7]).ToString();
break;
}
case 0x04:
{
Dust.Text = (buff[6] + buff[7]).ToString();
break;
}
default:
break;
}
}
}
else
{
//当接收数据不在设定的数据位范围之内时,会出现接受到的数据一直保存在接收缓存区之内,后续每次接手数据都会将上一次的数据进行叠加,造成只能通过关闭串口的方法来清除缓冲区的数据
s.DiscardInBuffer(); //丢弃来自串行驱动程序的接收缓冲区的数据
}
}
#endregion
当接收到的数据长度不等于8的时候,将丢弃来自串行驱动程序的接收缓冲区的数据,接下来通过断点调试来分析丢弃缓冲区和不丢弃缓冲区数据两种情况进行仿真,分析如下几点。
- 使用串口助手给上位机发送数据数据位长度为8位的数据,串口调试助手和上位机的终端的显示界面如下,发送端数据和接收端数据一样,并未出现异常:

- 将串口调试助手发送数据位修改成9位之后,进行发送,可以发现上位机并未接收到相关的数据:

- 接着修改串口调试助手的发送数据位,修改成8位,可以发现上位机尚未能接收到来自串口调试助手发来的数据,这是为什么呢?

- 接下来将通过断点逐步进行调试,来解释是为啥上位机没有接收到调试助手发来的数据,当串口调试助手发来的数据长度位9位时,通过监视器可以查看到接收缓冲器中的数据长度长度是9

- 第一次点击完发送之后,上位机未能成功接收到数据,我们就会好奇,并且一般都会点击第二次、第三次、甚至一直点下去,观察是否会出现啥异常现象,当点击第二次时,通过监视窗口,可以观察到到串口缓冲区的数据长度变成了18,这是因为缓冲区将上一次接收的数据给保留了下来并没有删除,就算下次发送的数据长度为8位的时候,也一样是通过叠加的方式将其保存到缓冲区,这样就会造成缓冲区的数据位长度会一直大于8;如果不通过 s.DiscardInBuffer()方法丢弃来自串行驱动程序的接收缓冲区的数据,就只能通过关闭串口然后重新打开相应的串口来实现缓冲区的数据清除。

- 使用s.DiscardInBuffer()对不符合长度的数据进行丢弃,实现的效果如下所示:

需要完整源码的朋友可以通过以下链接进行下载,如有大佬有更好的优化意见欢迎一块进行讨论,谢谢!
链接:https://pan.baidu.com/s/1Yb1YjdAZfficRtx2srBpzw
提取码:7yc2
C# 简易的串口监视上位机实现的更多相关文章
- 开源串口 Ymodem 上位机软件
概述 上位机使用Qt开发,计划整合多个工具为一体,用作以后的调试工具. 当前完成功能: 1.串口调试 支持hex和ascii 码发送,接受. 支持自动添加回车换行. 支持定时发送,最短间隔100ms, ...
- VC++编写简单串口上位机程序
VC++编写简单串口上位机程序 转载: http://blog.sina.com.cn/s/articlelist_1809084904_0_1.html VC++编写简单串口上位机程序 串口通信 ...
- 基于Arduino和python的串口通信和上位机控制
引言 经常的时候我们要实现两个代码之间的通信,比如说两个不同不同人写的代码要对接,例如将python指令控制Arduino控件的开关,此处使用串口通信是非常方便的,下面笔者将结合自己踩过的坑来讲述下自 ...
- 快速设计一个简单的WPF串口上位机
最近一直在学习UWP,其中有的技术参考了WPF,所以又回头再来学习WPF,感觉学的东西很杂,必须记录一下,不然时间长了还得忘掉,于是申请开始写博客,将学习的心得记录一下,以备后用.这次是因为公司内训, ...
- C#上位机串口控制12864显示
实现的效果 上面是用Proteus仿真的,,对了如果自己想用proteus仿真需要安装下面这个软件 再看一下实物显示效果 先做上位机部分........... 为了程序一启动就把电脑上能用的串口号显示 ...
- c#上位机与三菱PLC(FX3U)串口通讯
项目中会经常用到上位机与PLC之间的串口通信,本文介绍一下C#如何编写上位机代码 与三菱FX3U进行通讯 1. 第一种方法是自己写代码实现,主要代码如下: //对PLC的Y7进行置1 byte[] Y ...
- [python] 3 、基于串口通信的嵌入式设备上位机自动测试程序框架(简陋框架)
星期一, 20. 八月 2018 01:53上午 - beautifulzzzz 1.前言 做类似zigbee.ble mesh...无线网络节点性能测试的时候,手动操作然后看表象往往很难找出真正的原 ...
- LabVIEW上位机与串口通信
渊源 大一的时候,学校开了门公共选修课,叫LabVIEW编程,当时的我当然还不知道LabVIEW是啥东东,但还是选了.上课的老师是机械学院的一个副教授.他给我们展示了好几个用LabVIEW做的项目.譬 ...
- C#做一个简单的进行串口通信的上位机
C#做一个简单的进行串口通信的上位机 1.上位机与下位机 上位机相当于一个软件系统,可以用于接收数据.控制数据.即可以对接收到的数据直接发送操控命令来操作数据.上位机可以接收下位机的信号.下位机是 ...
随机推荐
- 使用composer 显示错误美化
新建comoser.json { "name": "brady_frmwork", "description":"php fram ...
- 编写C语言的两种方法----Visual Studio/CodeBlocks
1.CodeBlock(安装简单) 参考这个博客的:https://blog.csdn.net/jjjjkkjkk/article/details/80331625?utm_medium=distri ...
- 基于node.js的爬虫框架 node-crawler简单尝试
百度爬虫这个词语,一般出现的都是python相关的资料. py也有很多爬虫框架,比如scrapy,Portia,Crawley等. 之前我个人更喜欢用C#做爬虫. 随着对nodejs的熟悉.发现做这种 ...
- origin生成直方图
1. 导入数据 2. 选择一列,右键生成Frequent Count 3. 如果要显示相对频率,勾选Relative Frequency 4. 选择第一列和最后一列并生成柱状图 5. 双击生成的图形, ...
- 安装npm全局包提示没有写入权限: npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules
方法一 安装npm全局包提示没有写入权限: npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules ...
- Jenkins配置,tomacat版本输出乱码和页面打开报404的问题
1.打开tomact下的startup.bat,tomcat版本控制台中文输出乱码,解决方法是去tomacat安装路径下的conf目录,打开logging.properties文件,将java.uti ...
- model基础操作(下)
3.Django多对多表结构操作 3.1 第一种: ManyToManyField 自己不创建第三张关系表,有m2m字段: 根据queryset对象增删改查(推荐) from django ...
- python数据类型互相转换
类型转换 关注公众号"轻松学编程"了解更多. 主要针对几种存储工具:list.tuple.dict.set 特殊之处:dict是用来存储键值对的. 1.list 转换为set l1 ...
- React中useLayoutEffect和useEffect的区别
重点: 1.二者函数签名相同,调用方式是一致的 2. 怎么简单进行选择: 无脑选择useEffect,除非运行效果和你预期的不一致再试试useLayoutEffect 区别详解:useEffect是异 ...
- Tomcat 总结
JavaWeb简介 JavaWeb,是用Java技术来解决相关web互联网领域的技术总和. Web包括:web服务器和web客户端两个部分,有两种软件架构 C/S:客户端/服务器端 B/S:浏 ...