前言

本着学习研究的态度,用c#语言实现简单的串口通信工具。

一、串口通信原理

串口通信

串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。

常用术语介绍

波特率:这是一个衡量符号传输速率的参数。指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。一般调制速率大于波特率,比如曼彻斯特编码)。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。

数据位这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语"包"指任何通信的情况。

停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

奇偶校验位:串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。

串口引脚图解

1 载波检测(DCD) 2 接受数据(RXD) 3 发出数据(TXD) 4 数据终端准备好(DTR)

5 信号地线(SG) 6 数据准备好(DSR) 7 请求发送(RTS) 8 清除发送(CTS) 9 振铃指示(RI)

    

二、使用System.IO.Port.SerialPort类实现串口通信

System.IO.Port.SerialPort类介绍

    System.IO.Port.SerialPort.NET Framework提供的操作串行端口的类,里面提供了一些方法、属性和和事件供开发者调用操作串口。

调用流程

1. 直接调用SerialPort的静态方法GetPortNames()获取当前计算机的串行端口名称数组

2.根据串口名称,初始化SerialPort对象,设置参数,调用Open()方法打开串口

3.调用Write()方法发送数据

4.注册接收数据的监听,获取数据(或者另起线程循环读取接收数据,本文使用注册监听方式接收数据)

具体代码实现

using System;
using System.IO.Ports;
using System.Text; namespace PortControlDemo
{
public class PortControlHelper
{
#region 字段/属性/委托
/// <summary>
/// 串行端口对象
/// </summary>
private SerialPort sp; /// <summary>
/// 串口接收数据委托
/// </summary>
public delegate void ComReceiveDataHandler(string data); public ComReceiveDataHandler OnComReceiveDataHandler = null; /// <summary>
/// 端口名称数组
/// </summary>
public string[] PortNameArr { get; set; } /// <summary>
/// 串口通信开启状态
/// </summary>
public bool PortState { get; set; } = false; /// <summary>
/// 编码类型
/// </summary>
public Encoding EncodingType { get; set; } = Encoding.ASCII;
#endregion #region 方法
public PortControlHelper()
{
PortNameArr = SerialPort.GetPortNames();
sp = new SerialPort();
sp.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
} /// <summary>
/// 打开端口
/// </summary>
/// <param name="portName">端口名称</param>
/// <param name="boudRate">波特率</param>
/// <param name="dataBit">数据位</param>
/// <param name="stopBit">停止位</param>
/// <param name="timeout">超时时间</param>
public void OpenPort(string portName , int boudRate = , int dataBit = , int stopBit = , int timeout = )
{
try
{
sp.PortName = portName;
sp.BaudRate = boudRate;
sp.DataBits = dataBit;
sp.StopBits = (StopBits)stopBit;
sp.ReadTimeout = timeout;
sp.Open();
PortState = true;
}
catch (Exception e)
{
throw e;
}
} /// <summary>
/// 关闭端口
/// </summary>
public void ClosePort()
{
try
{
sp.Close();
PortState = false;
}
catch (Exception e)
{
throw e;
}
} /// <summary>
/// 发送数据
/// </summary>
/// <param name="sendData"></param>
public void SendData(string sendData)
{
try
{
sp.Encoding = EncodingType;
sp.Write(sendData);
}
catch (Exception e)
{
throw e;
}
} /// <summary>
/// 接收数据回调用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[sp.BytesToRead];
sp.Read(buffer, , buffer.Length);
string str = EncodingType.GetString(buffer);
if (OnComReceiveDataHandler != null)
{
OnComReceiveDataHandler(str);
}
}
#endregion
}
}

三、编写Winform串口通信工具界面

界面预览

操作介绍

本界面主要功能是操作两个串口,一个发送数据另一个接收数据。左侧设置两串口的一些参数,设置完成后点击"打开发送接收串口",如两串口成功打开,右侧便可操作发送和接受数据。

具体代码实现

using System;
using System.Windows.Forms; namespace PortControlDemo
{
public partial class FrmPortControl : Form
{
#region 字段/属性
int[] BaudRateArr = new int[] { , , , , , };
int[] DataBitArr = new int[] { , , };
int[] StopBitArr = new int[] { , , };
int[] TimeoutArr = new int[] { , , , , };
object[] CheckBitArr = new object[] { "None"};
private bool ReceiveState = false;
private PortControlHelper pchSend;
private PortControlHelper pchReceive;
#endregion #region 方法
/// <summary>
/// 初始化控件
/// </summary>
private void InitView()
{
cb_portNameSend.DataSource = pchSend.PortNameArr;
cb_portNameReceive.DataSource = pchReceive.PortNameArr;
cb_baudRate.DataSource = BaudRateArr;
cb_dataBit.DataSource = DataBitArr;
cb_stopBit.DataSource = StopBitArr;
cb_checkBit.DataSource = CheckBitArr;
cb_timeout.DataSource = TimeoutArr;
FreshBtnState(pchSend.PortState && pchReceive.PortState);
} /// <summary>
/// 刷新按钮状态
/// </summary>
/// <param name="state"></param>
private void FreshBtnState(bool state)
{
if (state)
{
Btn_open.Text = "关闭发送接收串口";
Btn_send.Enabled = true;
Btn_receive.Enabled = true;
}
else
{
Btn_open.Text = "打开发送接收串口";
Btn_send.Enabled = false;
Btn_receive.Enabled = false;
}
}
#endregion #region 事件
public FrmPortControl()
{
InitializeComponent();
pchSend = new PortControlHelper();
pchReceive = new PortControlHelper();
InitView();
} /// <summary>
/// 点击 发送数据 按钮,发送文本内数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Btn_send_Click(object sender, EventArgs e)
{
pchSend.SendData(tb_send.Text);
} /// <summary>
/// 点击 开始接收 按钮,开始监听串口接收入口数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Btn_receive_Click(object sender, EventArgs e)
{
if (ReceiveState)
{
pchReceive.OnComReceiveDataHandler -= new PortControlHelper.ComReceiveDataHandler(ComReceiveData);
Btn_receive.Text = "开始接收";
ReceiveState = false;
}
else
{ pchReceive.OnComReceiveDataHandler += new PortControlHelper.ComReceiveDataHandler(ComReceiveData);
Btn_receive.Text = "停止接收";
ReceiveState = true;
}
} /// <summary>
/// 开启或关闭 两个通信的串口,刷新按钮状态
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Btn_open_Click(object sender, EventArgs e)
{
if (pchSend.PortState)
{
pchSend.ClosePort();
pchReceive.ClosePort();
}
else
{
pchSend.OpenPort(cb_portNameSend.Text, int.Parse(cb_baudRate.Text),
int.Parse(cb_dataBit.Text), int.Parse(cb_stopBit.Text),
int.Parse(cb_timeout.Text));
pchReceive.OpenPort(cb_portNameReceive.Text, int.Parse(cb_baudRate.Text),
int.Parse(cb_dataBit.Text), int.Parse(cb_stopBit.Text),
int.Parse(cb_timeout.Text)); }
FreshBtnState(pchSend.PortState && pchReceive.PortState);
pchReceive.OnComReceiveDataHandler += new PortControlHelper.ComReceiveDataHandler(ComReceiveData);
Btn_receive.Text = "停止接收";
ReceiveState = true;
} /// <summary>
/// 接收到的数据,写入文本框内
/// </summary>
/// <param name="data"></param>
private void ComReceiveData(string data)
{
this.Invoke(new EventHandler(delegate
{
tb_receive.AppendText($"接收:{data}\n");
}));
}
#endregion
}
}

总结

 有框架提供的帮助类,我们实现串口通信虽然不难,但是还是有很多细节要注意的。写好任何一样东西都不容易,一起加油吧!

附录

搭建一个串口调试环境的工具和本文源码地址,有需要的筒靴自提吧 (ง •̀_•́)ง

串口调试Demo源码地址:https://files.cnblogs.com/files/ElijahZeng/PortControlDemo.rar

虚拟串口工具vspd:https://files.cnblogs.com/files/ElijahZeng/VSPD%E8%99%9A%E6%8B%9F%E4%B8%B2%E5%8F%A3%E5%B7%A5%E5%85%B7.rar

C#实现简单的串口通信的更多相关文章

  1. C#--简单的串口通信程序

    前几天做毕业设计,其中要用到串口和下位机进行通信,于是自己捣鼓了一个简单的串口通信程序. 在做通信之前要先弄一个SerialPort组件出来,当然也可以通过程序来创建.本次设计中采用的是拖的winfo ...

  2. VS2008基于对话框的MFC上位机串口通信(C++实现)简单例程

    首先,在 vs2008 环境下创建 MFC 运用程序 设置项目名称为 ComTest(这个地方随意命名,根据个人习惯),点击确定后,点击下一步 出现如下界面 选择"基于对话框"模式 ...

  3. C#串口介绍以及简单串口通信程序设计实现

    C#串口介绍以及简单串口通信程序设计实现 周末,没事干,写个简单的串口通信工具,也算是本周末曾来过,废话不多,直接到主题 串口介绍 串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口) ...

  4. Mac下的串口通信-ORSSerialPort

    ================================2015/11/05======================================= 最近在工作中遇到有关Mac下串口通信 ...

  5. LabVIEW串口通信

    Instrument I/O 利用LabVIEW内置的驱动程序库和具有工业标准的设备驱动软件,可对 GPIB(通用接口总线).Ethernet(以太网)接口.RS-232(标准串行接口总线)/RS-4 ...

  6. .net串口通信

    背景: 前一段时间需要写一个向蓝牙模块发消息的功能. 对蓝牙的机制不太了解,所以一直在查资料, 但始终没找到我需要的东西,还误以为需要配套的一套开发模板和开发包, 偶然间发现只需要简单的串口通信,并且 ...

  7. Qt编写串口通信程序全程图文解说

    (说明:我们的编程环境是windows xp下,在Qt Creator中进行,假设在Linux下或直接用源代码编写,程序稍有不同,请自己修改.) 在Qt中并没有特定的串口控制类,如今大部分人使用的是第 ...

  8. 转:Qt编写串口通信程序全程图文讲解

    转载:http://blog.csdn.net/yafeilinux/article/details/4717706  作者:yafeilinux (说明:我们的编程环境是windows xp下,在Q ...

  9. Qt编写串口通信程序全程图文讲解 .

    在Qt中并没有特定的串口控制类,现在大部分人使用的是第三方写的qextserialport类,我们这里也是使用的该类.我们可以去 http://sourceforge.net/projects/qex ...

随机推荐

  1. UVA 11383 - Golden Tiger Claw(二分图完美匹配扩展)

    UVA 11383 - Golden Tiger Claw 题目链接 题意:给定每列和每行的和,给定一个矩阵,要求每一个格子(x, y)的值小于row(i) + col(j),求一种方案,而且全部行列 ...

  2. 为了世界的和平~一起上caioj~~~!

    打Call~打Call~打Call~~~!!! 世界毁灭了你在哪???不要犹豫,快去caioj!!! 无比优质的oj,未来大牛的明智之选----就是caioj~~~

  3. 如何用jQuery实现div随鼠标移动而移动?(详解)----2017-05-12

    重点是弄清楚如何获取鼠标现位置与移动后位置,div现在位置与移动后位置: 用jQuery实现div随鼠标移动而移动,不是鼠标自身的位置!!而是div相对于之前位置的移动 代码如下:(注意看绿色部分的解 ...

  4. Blender插件之Panel

    目标 [x] 总结Blender之Panel 总结 Blender之Panel需要从Blender界面组成开始理解. 直观上Blender的界面层次为 Editors ‣ Regions ‣ (Tab ...

  5. VS2015启动显示无法访问此网站

    之前启动项目发生过几次,也忘了怎么解决了,今天记录一下:将应用文件夹下的vs目录删除,重新生成解决方案后,程序正常启动. 原文链接:https://blog.csdn.net/upi2u/articl ...

  6. 在AndroidManifest(清单文件)中注册activity(活动)及配置主活动、更改App图标、App名称、修改隐藏标题栏

    打开app/src/main/AndroidManifest. <?xml version="1.0" encoding="utf-8"?> < ...

  7. 3) 十分钟学会android--建立第一个APP,建立简单的用户界面

    在本小节里,我们将学习如何用 XML 创建一个带有文本输入框和按钮的界面.下一节课将学会使 APP 对按钮做出响应——按钮被按下时,文本框里的内容被发送到另外一个 Activity. Android ...

  8. MongoDB 博客截图之二

    使用内置帮助help() 基本命令示例 来源:MongoDB基本管理命令 - 千与的专栏 - 博客频道 - CSDN.NET

  9. PHP中的字符串类型

    PHP支持两种类型的字符串,这些字符串用引号说明. 1.如果希望赋值一个字面意义的字符串,精确保存这个字符串的内容,应该用单引号标注,例如: $info='You are my $sunshine'; ...

  10. 继承&封装

    扩展一个已有的类,并且继承该类的属性和行为这种方式成为继承. 实例 public class Polygon { public int sides; public double area; publi ...