实现上位机和下位机之间的通信,通常使用的是串口通信,接下来实现一个通过上位机和串口调试助手来完成串口通信测试。

  首先创建一个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# 简易的串口监视上位机实现的更多相关文章

  1. 开源串口 Ymodem 上位机软件

    概述 上位机使用Qt开发,计划整合多个工具为一体,用作以后的调试工具. 当前完成功能: 1.串口调试 支持hex和ascii 码发送,接受. 支持自动添加回车换行. 支持定时发送,最短间隔100ms, ...

  2. VC++编写简单串口上位机程序

    VC++编写简单串口上位机程序   转载: http://blog.sina.com.cn/s/articlelist_1809084904_0_1.html VC++编写简单串口上位机程序 串口通信 ...

  3. 基于Arduino和python的串口通信和上位机控制

    引言 经常的时候我们要实现两个代码之间的通信,比如说两个不同不同人写的代码要对接,例如将python指令控制Arduino控件的开关,此处使用串口通信是非常方便的,下面笔者将结合自己踩过的坑来讲述下自 ...

  4. 快速设计一个简单的WPF串口上位机

    最近一直在学习UWP,其中有的技术参考了WPF,所以又回头再来学习WPF,感觉学的东西很杂,必须记录一下,不然时间长了还得忘掉,于是申请开始写博客,将学习的心得记录一下,以备后用.这次是因为公司内训, ...

  5. C#上位机串口控制12864显示

    实现的效果 上面是用Proteus仿真的,,对了如果自己想用proteus仿真需要安装下面这个软件 再看一下实物显示效果 先做上位机部分........... 为了程序一启动就把电脑上能用的串口号显示 ...

  6. c#上位机与三菱PLC(FX3U)串口通讯

    项目中会经常用到上位机与PLC之间的串口通信,本文介绍一下C#如何编写上位机代码 与三菱FX3U进行通讯 1. 第一种方法是自己写代码实现,主要代码如下: //对PLC的Y7进行置1 byte[] Y ...

  7. [python] 3 、基于串口通信的嵌入式设备上位机自动测试程序框架(简陋框架)

    星期一, 20. 八月 2018 01:53上午 - beautifulzzzz 1.前言 做类似zigbee.ble mesh...无线网络节点性能测试的时候,手动操作然后看表象往往很难找出真正的原 ...

  8. LabVIEW上位机与串口通信

    渊源 大一的时候,学校开了门公共选修课,叫LabVIEW编程,当时的我当然还不知道LabVIEW是啥东东,但还是选了.上课的老师是机械学院的一个副教授.他给我们展示了好几个用LabVIEW做的项目.譬 ...

  9. C#做一个简单的进行串口通信的上位机

    C#做一个简单的进行串口通信的上位机   1.上位机与下位机 上位机相当于一个软件系统,可以用于接收数据.控制数据.即可以对接收到的数据直接发送操控命令来操作数据.上位机可以接收下位机的信号.下位机是 ...

随机推荐

  1. python中实现格式化输入(史上最简单、最高效的实现方法,不借助任何模块)

    今天我在写python作业时突然想到格式化输入一段文字,譬如只需读取输入的前几个字符就行,而不幸的是,python中的输入并没有c中的read().getchar()函数,于是我网上搜了一下,网上的解 ...

  2. abstract关键字的说法

    含有abstract修饰符的class即为抽象类,abstract 类不能创建的实例对象.含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必 ...

  3. Phoenix的一些问题

    date: 2020-09-10 13:50:00 updated: 2020-09-14 16:30:00 1. Phoenix索引 全局索引:适合读多写少的场景.写数据时因为索引表分布在不同数据节 ...

  4. SP1772 Find The Determinant II

    题意 \(T\) 组数据,每组给定两个整数 \(n,k\),求 \(\det A\),其中 \(A\) 为一个 \(n\times n\) 的矩阵且 \(A_{i,j}=\gcd(i,j)^k\),对 ...

  5. 如何修改hosts并保存

    Hosts文件用于本地调试,或手动设置一个域名应该被解析到哪个IP地址,在修改时会发现需要管理员权限才能修改保存,这个时候我们可以这样做 找到Hosts文件,将Hosts文件复制到桌面.(Window ...

  6. Python使用JsAPI发起微信支付 Demo

    Python使用JsAPI发起微信支付 Demo 这个是基于Django框架. 了解更多,可以关注公众号"轻松学编程" 1.公众号设置.微信商户号设置 这些都可以在官网查得到, 公 ...

  7. python实现类的多态

    多态 关注公众号"轻松学编程"了解更多. 1.多态使用 一种事物的多种体现形式,举例:动物有很多种 注意: 继承是多态的前提 函数重写就是多态的体现形式 演示:重写Animal类 ...

  8. 快速傅里叶变换学习笔记(FFT)

    什么是FFT FFT是用来快速计算两个多项式相乘的一种算法. 如果我们暴力计算两个多项式相乘,复杂度必然是\(O(n^2)\)的,而FFT可以将复杂度降至\(O(nlogn)\) 如何FFT 要学习F ...

  9. 被巴菲特看中的Snowflake,是怎样深刻改变云计算产业的?

    众所周知,在很长一段时间里,巴菲特都从来不碰科技股.但人总是会变的,他在2016年开始首次持仓苹果,并在此后一再增持,目前苹果为伯克希尔第一大重仓股. 前不久,巴菲特持股了人生中的又一家科技公司--S ...

  10. 【Kubernetes学习笔记】-使用Minikube快速部署K8S单机学习环境

    介绍 https://minikube.sigs.k8s.io/docs/ Minikube 用于快速在本地搭建 Kubernetes 单节点集群环境,它对硬件资源没有太高的要求,方便开发人员学习试用 ...