前言

本着学习研究的态度,用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. Struts2 动态结果集

    1.index.jsp <body> 动态结果 一定不要忘了为动态结果的保存值设置set get方法 <ol> <li><a href="user/ ...

  2. Heat-AutoScaling

    在openstack的I版本号中,Heat中加入了对于AutoScaling资源的支持,github上也提供了相应的AutoScaling的模板,同一时候也支持使用ceilometer的alarm来触 ...

  3. 赵雅智_ListView_SimpleAdapter

    项目步骤 声明listView控件并获取显示的视图 获取显示的数据 设置显示的adapter 注冊点击事件 详细案例 实现效果: 查找的方法 public List<Map<String, ...

  4. ZOJ2724_Windows Message Queue(STL/优先队列)

    解题报告 题意: 看输入输出就非常明确. 思路: 优先队列. #include <algorithm> #include <iostream> #include <cst ...

  5. 仿支付宝/微信的password输入框效果GridPasswordView解析

    仿支付宝/微信的password输入框效果GridPasswordView解析,把一些设置和一些关键的地方列了出来,方便大家使用,可能能够省一部分的时间,也算是自己的积累吧. 1.password框能 ...

  6. 在Mac OSX上安装ffmpeg && ffmpeg命令行将h264封装为mp4

    ffmpeg功能强大,可以通过命令行来对音视频进行处理.为了使用其功能,我在Mac上对其进行了安装. 我的Mac OS X 系统版本:OS X Yosemite, 10.10.14 关于ffmpeg在 ...

  7. 整体刷新和局部刷新frameset窗口

    在项目中,经常会遇到页面分割,最常见的系统或网站的主界面.主页面分为,上面系统简介.下面作者简介.左边系统功能菜单.右边则是菜单真正展示的界面. 遇到这种这种分割页面,大家首先想到是frameset, ...

  8. Super超级ERP系统---(8)订单管理--订单创建

    订单管理是ERP系统中一个重要模块,客户下订单,ERP通过订单来为客户进行配送.订单模块主要包括订单创建,订单修改,订单审核,订单取消,订单分配,订单打印,订单拣货,订单出库.在随后的几节里我们看看这 ...

  9. (转)webpack用法

    前言 webpack前端工程中扮演的角色越来越重要,它也是前端工程化很重要的一环.本文将和大家一起按照项目流程学习使用wbepack,妈妈再也不用担心我不会使用webpack,哪里不会看哪里.这是一个 ...

  10. jQuery动画知识总结

    jQuery动画概述 我们之前实现的下拉菜单的案例,是没有动画效果的,但是在日常开发中,动画效果是经常会用到的,所以我们可以尝试使用jQuery动画,将下拉菜单案例实现的更动感一些. jQuery提供 ...