前言

本着学习研究的态度,用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. 具体解释linux文件处理的的经常使用命令

    原创Blog.转载请注明出处 附上之前訪问量比較高的几篇linux博客 本人使用shell的8个小技巧 grep的九个经典使用场景 sed命令具体解释 awk命令具体解释 linux中全部的东西都是文 ...

  2. Cocos2d-x《雷电大战》(3)-子弹无限发射

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 本文要实现雷电游戏中,游戏一開始,英雄飞机就无限发射子弹的功能. 这里的思想是单独给子弹弄一个 ...

  3. 【BZOJ】2140 稳定婚姻

    [解析]Hash,离散化.Tarjan [分析] 对于每一个名字.首先离散化成编号. 用hash或者其它,反正不要最主要的即可了.否则O(N^2L)会爆掉. 然后请參考:http://www.cnbl ...

  4. 复制DropDownList

    DropDownList ddlA; ListItem[] ar = new ListItem[ddlB.Items.Count]; ddlB.Items.CopyTo(ar,0); ddlA.Dat ...

  5. 搜索分析(DFS、BFS、递归、记忆化搜索)

    搜索分析(DFS.BFS.递归.记忆化搜索) 1.线性查找 在数组a[]={0,1,2,3,4,5,6,7,8,9,10}中查找1这个元素. (1)普通搜索方法,一个循环从0到10搜索,这里略. (2 ...

  6. C# Log4Net简单使用方法

    log4net库是Apache log4j框架在Microsoft .NET平台的实现,是一个帮助程序员将日志信息输出到各种目标(控制台.文件.数据库等)的工具. 使用log4net,一个很明显的好处 ...

  7. js设计模式-门面模式

    适用场景:门面模式在DOM脚本编程这种需要对各种不一致的浏览器接口的环境中很常用. 例子:阻止模式事件 var DED = widow.DED || {}; DED.util = { stopProp ...

  8. H3BPM实例分享——金额规则大写

    v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...

  9. Pop3协议详解

      POP3全称为Post Office Protocol version3,即邮局协议第3版.它被用户代理用来邮件服务器取得邮件.POP3采用的也是C/S通信 模型 用户从邮件服务器上接收邮件的典型 ...

  10. 使用WebGL + Three.js制作动画场景

    使用WebGL + Three.js制作动画场景 3D图像,技术,打造产品,还有互联网:这些只是我爱好的一小部分. 现在,感谢WebGL的出现-一个新的JavaScriptAPI,它可以在不依赖任何插 ...