C# 实现简易的串口监视上位机功能附源码下载

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

  首先创建一个WInfrom窗体应用工程文件,创建过程可参考  https://www.jb51.net/article/150973.htm

  在创建好的工程下面,通过工具箱中已有的控件完成界面的搭建,如下图所示,为了方便初学者容易看懂程序,下图将控件的命名一并标注出来:

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

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.Diagnostics;

namespace Tem_Hum_Monitorring
{

 public partial class Form1 : Form
 {
 //实例化串口
 SerialPort s = new SerialPort();

 public Form1()
 {
  InitializeComponent();
  Control.CheckForIllegalCrossThreadCalls = false;
  button1.Text = "打开串口";
  int[] item = { 9600,115200}; //遍历
  foreach (int a in item)
  {
  comboBox2.Items.Add(a.ToString());
  }
  comboBox2.SelectedItem = comboBox2.Items[1];
 }

 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];
 }

 #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

 #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

 #region 串口发送
 private void button3_Click(object sender, EventArgs e)
 {
  string[] sendbuff = richTextBox2.Text.Split();
  Debug.WriteLine("发送字节数:" + sendbuff.Length);
  foreach (string item in sendbuff)
  {
  int count = 1;
  byte[] buff = new byte[count];
  buff[0] = byte.Parse(item, System.Globalization.NumberStyles.HexNumber);
  s.Write(buff,0,count);
  }
 }
 #endregion

 private void button2_Click(object sender, EventArgs e)
 {
  int count = 1;
  byte[] buff = new byte[count];
  buff[0] = byte.Parse("04", System.Globalization.NumberStyles.HexNumber);
  s.Write(buff, 0, count);
 }
 }
}

  在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/1MXVIFQHHsEmx4p28Pz-wcQ 提取码: ibu9

到此这篇关于C# 实现简易的串口监视上位机功能的文章就介绍到这了,更多相关c#上位机串口内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C#实现把指定数据写入串口

    public static bool WriteToSerialPort(byte[]byteArr) { SerialPort Com = newSerialPort(); try { Com.ReadTimeout = 5000; Com.WriteTimeout = 5000; Com.PortName = "Com1"; Com.BaudRate = 9600; Com.StopBits = StopBits.One; Com.Parity = Parity.None; Com

  • C#获取串口列表实现实时监控串口

    常用的两种方法 方法一: using Microsoft.Win32; RegistryKey keyCom = Registry.LocalMachine.OpenSubKey("Hardware//DeviceMap//SerialComm"); if (keyCom != null) { string[] sSubKeys = keyCom.GetValueNames(); foreach (string sName in sSubKeys) { string sValue =

  • C#串口通信程序实例详解

    创建C#串口通信程序之命名空间 System.IO.Ports命名空间中最重用的是SerialPort 类. 创建C#串口通信程序之创建SerialPort 对象 通过创建SerialPort 对象,我们可以在程序中控制串口通信的全过程. 我们将要用到的SerialPort 类的方法: ReadLine():从输入缓冲区读一新行的值,如果没有,会返回NULLWriteLine(string):写入输出缓冲Open():打开一个新的串口连接Close():关闭 复制代码 代码如下: SerialP

  • C#串口编程实例代码

    由于工作需要,第一次接触串口编程.所以不得不在网上查阅各种编程实例.最后结合自己的理解与实践,最终有了如下代码. 本代码只经过了简单的软件测试,与简单的硬件测试. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Threading; using System.IO.Port

  • C# WPF上位机实现和下位机TCP通讯的方法

    下位机使用北京大华程控电源DH1766-1,上位机使用WPF.实现了电压电流实时采集,曲线显示.上午在公司调试成功,手头没有程控电源,使用TCP服务端模拟.昨天写的TCP服务端正好排上用场. 界面如下: 服务端 服务端实在上篇基础上实现的.需要做如下更改: while (true) { try { byte[] bufferDate = new byte[1024]; int realLen = pSocket.Receive(bufferDate); if (realLen <= 0) { t

  • C#串口通信实现方法

    本文实例讲述了C#串口通信实现方法.分享给大家供大家参考.具体方法如下: 通过COM1发送数据,COM2接收数据.当COM2接收完本次发送的数据后,向COM1发送信息通知COM1本次数据已发完,COM1接到通知后,再发下一段数据.这样可以确保每次发送的数据都可以被正确接收. 代码如下: 复制代码 代码如下: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data;

  • C# 实现简易的串口监视上位机功能附源码下载

    实现上位机和下位机之间的通信,通常使用的是串口通信,接下来实现一个通过上位机和串口调试助手来完成串口通信测试. 首先创建一个WInfrom窗体应用工程文件,创建过程可参考  https://www.jb51.net/article/150973.htm 在创建好的工程下面,通过工具箱中已有的控件完成界面的搭建,如下图所示,为了方便初学者容易看懂程序,下图将控件的命名一并标注出来: 直接进入正题,将完整的工程代码黏贴出来: using System; using System.Collection

  • java实现简易超市管理系统 附源码下载

    java超市管理系统 1.0(含源文件,后续会继续优化~) 前言 一个月零零散散的时间学习了java,通过这次"超市管理系统"的练习,希望可以给一同开始学习java的朋友一些参考,更希望大佬们多多指点和批评~ 一.确定需求 程序概述: 小型超市商品销售管理系统选择小型超市的四类商品进行管理. 这四类商品是:食品.化妆品.生活用品和饮料(四个类). 每类商品都包含有商品名和商品利润 (其中包括商品的售价.进价.库存量).(五个属性) 每类不同的商品还有区别于其他商品的特殊信息(子类特有属

  • JSP实用教程之简易页面编辑器的实现方法(附源码)

    前言 实现一个简易的页面编辑器是大家在学习jsp的时候经常会遇到的一个需求,发现网上这方便的资料不多,所以想着自己总结下,本文详细介绍了JSP简易页面编辑器的实现方法,下面话不多说,来一起看看详细的介绍: 需求 提供一页面,放置"帮助"."版权"文字内容,特点:静态页面,无须读数据库,只是应付字眼上频繁的修改:没有复杂的交互,无须 JavaScript:没有图片,不需要文件上传. 给出的方案:提供一页面和简易的后台管理,功能单一,只是编辑页面(只是修改字体.大小.粗

  • C#实现简易计算器功能(附源码)

    本文实例为大家分享了C#实现简易计算器功能的具体代码,供大家参考,具体内容如下 剖析: 1.先设计界面(按钮.文本框(一个显示算式,一个显示结果))布局 2.单击按钮将其对应内容显示在文本框中 3.单击符号(+.-.×.÷.%)时将第一次输入的数储存起来 4.单击等号时将第二次输入的数存储起来并将第一次输入的数与第二次输入的数按照所单击的符号进行运算将结果显示在第一个文本框中 5.单击C时将两个文本框中的内容清空 重点: 1.声明一个bool类型的变量用于实现单击符号再次输入数字时第一次输入的数

  • JSP实用教程之简易图片验证码的实现方法(附源码)

    前言 很多新手对图片验证码不是很了解,所以本文尝试通过一个简单的 JSP 小程序来实现验证码功能.文中给出了详细的示例代码,文末给出了完整实例代码的下载地址,下面话不多说了,来一起看看详细的介绍吧. 效果图 示例代码 前台代码如下: <form action="action.jsp" method="POST"> <label> 用户名: <input type="text" name="name"

  • JSP实用教程之简易文件上传组件的实现方法(附源码)

    前言 本文主要给大家介绍的是关于JSP简易文件上传组件的实现方法,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧. 文件上传,包括但不限于图片上传,是 Web 开发中司空见惯的场景,相信各位或多或少都曾写过这方面相关的代码.Java 界若说文件上传,则言必称 Apache Commons FileUpload,论必及  SmartUpload.更甚者,Servlet 3.0 将文件上传列入 JSR 标准,使得通过几个注解就可以在 Servlet 中配置上传,无须依赖任何组件.使用第

  • Java实现简易版联网坦克对战小游戏(附源码)

    介绍 通过本项目能够更直观地理解应用层和运输层网络协议, 以及继承封装多态的运用. 网络部分是本文叙述的重点, 你将看到如何使用Java建立TCP和UDP连接并交换报文, 你还将看到如何自己定义一个简单的应用层协议来让自己应用进行网络通信. 获取源码 (本地下载) 基础版本 游戏的原理, 图形界面(非重点) 多张图片快速连续地播放, 图片中的东西就能动起来形成视频, 对视频中动起来的东西进行操作就变成游戏了. 在一个坦克对战游戏中, 改变一辆坦克每一帧的位置, 当多帧连续播放的时候, 视觉上就有

  • JavaScript实现简易的天数计算器实例【附demo源码下载】

    本文实例讲述了JavaScript实现简易的天数计算器.分享给大家供大家参考,具体如下: 运行效果图如下: 功能: 1. 支持选择日期: 2. 自动计算闰年: 3. 支持使用当前日期. 代码: (1)html文件: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>简易天数计算器</title> <s

  • Android编程实现简易弹幕效果示例【附demo源码下载】

    本文实例讲述了Android编程实现简易弹幕效果.分享给大家供大家参考,具体如下: 首先上效果图,类似于360检测到骚扰电话页面: 布局很简单,上面是一个RelativeLayout,下面一个Button. 功能: (1)弹幕生成后自动从右侧往左侧滚动(TranslateAnimation),弹幕消失后立刻被移除. (2)弹幕位置随机出现,并且不重复(防止文字重叠). (3)字体大小在一定范围内随机改变,字体颜色也可以设置. (4)自定义先减速,后加速的Interpolator,弹幕加速进入.减

随机推荐