当需要和以串口连接到计算机上的设备进行通信时,就需要用到串口编程,在.net中已经将串口的操作封装到了SerialPort 类中,所以我们在利用这个类进行对串口进行访问比较方便。
SerialPort类主要包括以下属性:
PortName :获取或设置通信端口,包括但不限于所有可用的 COM 端口。
BaudRate :串口通信的波特率。串口每秒钟传输的信号数。指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数。它是 对符号传输速率的一种度量,1波特即指每秒传输1个符号。
波特率与比特率的关系:
每秒钟通过信道传输的信息量称为位传输速率,也就是每秒钟传送的二进制位数,简称比特率。比特率表示有效数据的传输速率,用b/s 、bit/s、比特/秒,读作:比特每秒。
比特率=波特率*单个调制状态对应的二进制位数
DataBits :串口通信的数据位。串口通信时,发送一个信息包实际的数据位数,值范围是从 5 到 8。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。
StopBits :停止位,用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
Parity :奇偶校验位。用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位位1,这样就有3个逻辑高位。高位和低位不是真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
BytesToRead: 获取接收缓冲区中数据的字节数。
BytesToWrite: 获取发送缓冲区中数据的字节数。
Handshake: 获取或设置串行端口数据传输的握手协议。有如下4个值:
None = 0 没有用于握手的协议。
XOnXOff = 1 使用 XON/XOFF 软件控制协议。发送XOFF 控制以停止数 据传输。发送 XON 控制以继续传输。使用这些软件控制,而不是使用请求发送 (RTS) 和清除发送 (CTS) 硬件控制。
RequestToSend =2 使用请求发送 (RTS) 硬件流控制。RTS 发出信号,指出数据可用于传输。如果输入缓冲区已满,RTS 行将被设置为 false。当输入 缓冲区中有更多可用空间时,RTS行将被设置为 true。
RequestToSendXOnXOff= 3 同时使用请求发送 (RTS) 硬件控制和 XON/XOFF 软件控制。
RtsEnable : 获取或设置一个值,该值指示在串行通信中是否启用请求发送 (RTS) 信号。
CtsHolding: 获取“可以发送”行的状态。在请求发送/可以发送(RTS/CTS) 硬件握手中使用可以发送 (CTS) 行。发送数据之前端口会查询 CTS 行。
DsrHolding: 获取数据设置就绪 (DSR) 信号的状态。在数据设置就绪/数据终端就绪(DSR/DTR) 握手中使用此属性。通常由调制解调器将数据设置就绪 (DSR) 信号发送到端口,以表明它已经为数据传输或数据接收做好准备。
DtrEnable: 获取或设置一个值,该值在串行通信过程中启用数据终端就绪 (DTR) 信号。
Encoding: 获取或设置传输前后文本转换的字节编码。
IsOpen: 获取一个值,该值指示SerialPort 对象的打开或关闭状态。
NewLine: 获取或设置用于解释 ReadLine 和 WriteLine 方法调用结束的值。此属性定义 ReadLine 和 WriteLine 方法的行尾。
ReadBufferSize: 获取或设置 SerialPort 输入缓冲区的大小。
ReadTimeout: 获取或设置读取操作未完成时发生超时之前的毫秒数。
ReceivedBytesThreshold: 获取或设置 DataReceived 事件发生前内部输入缓冲区中的字节数。
RtsEnable: 获取或设置一个值,该值指示在串行通信中是否启用请求发送 (RTS) 信号。
WriteBufferSize: 获取或设置串行端口输出缓冲区的大小。
WriteTimeout: 获取或设置写入操作未完成时发生超时之前的毫秒数。
DataReceived: 表示将处理 SerialPort 对象的数据接收事件的方法。
ErrorReceived: 表示处理 SerialPort 对象的错误事件的方法。
Close: 关闭端口连接,将 IsOpen 属性设置为 false,并释放内部 Stream对象。
DiscardInBuffer: 丢弃来自串行驱动程序的接收缓冲区的数据。
DiscardOutBuffer: 丢弃来自串行驱动程序的传输缓冲区的数据。
Dispose: 已重载。 释放 SerialPort 对象使用的非托管资源。
Finalize: 在通过垃圾回收将 Component 回收之前,释放非托管资源并执行其他清理操作。
Open: 打开一个新的串行端口连接。
Read: 已重载。 从 SerialPort 输入缓冲区中读取。
ReadByte: 从 SerialPort 输入缓冲区中同步读取一个字节。
ReadChar: 从 SerialPort 输入缓冲区中同步读取一个字符。
ReadExisting: 在编码的基础上,读取 SerialPort对象的流和输入缓冲区中所有立即可用的字节。
ReadLine: 一直读取到输入缓冲区中的 NewLine 值。
ReadTo: 一直读取到输入缓冲区中的指定 value 的字符串。
Write: 已重载。 将数据写入串行端口输出缓冲区。
WriteLine: 将指定的字符串和 NewLine 值写入输出缓冲区。
对串口操作一般分为以下几步:
1.设置串口属性
2.打开串口
3.发送数据
4.接受数据
5.关闭串口
至于属性的详细设定,数据的发送方式,因为各种设备制定的协议和规则各不相同,要因具体情况而定。下面的代码能说明一些基本情况:
using System; using System.Collections.Generic; using System.Text; using System.IO.Ports; namespace Protocol { /// <summary> /// 提供对串口的统一访问 /// </summary> public sealed class SerialPortDao { #region 事件和字段定义 public event PortDataReceivedEventHandle Received; public SerialPort serialPort = null; public bool ReceiveEventFlag = false; //接收事件是否有效 false表示有效 private static readonly SerialPortDao instance = new SerialPortDao(); #endregion #region 属性定义 private string protName; public string PortName { get { return serialPort.PortName; } set { serialPort.PortName = value; protName = value; } } #endregion #region 构造函数 private SerialPortDao() { LoadSerialPort(); } private void LoadSerialPort() { serialPort = new SerialPort(); serialPort.BaudRate = 9600; serialPort.Parity = Parity.Even; serialPort.DataBits = 8; serialPort.StopBits = StopBits.One; serialPort.Handshake = Handshake.None; serialPort.RtsEnable = true; serialPort.ReadTimeout = 2000; serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceived); } #endregion #region 串口操作集合 /// <summary> /// 返回串口对象的单个实例 /// </summary> /// <returns></returns> public static SerialPortDao GetSerialPortDao() { return instance; } /// <summary> /// 释放串口资源 /// </summary> ~SerialPortDao() { Close(); } /// <summary> /// 打开串口 /// </summary> public void Open() { try { if (!serialPort.IsOpen) { serialPort.Open(); } } catch (Exception ex) { throw ex; } } /// <summary> /// 关闭串口 /// </summary> public void Close() { if (serialPort.IsOpen) { serialPort.Close(); } } /// <summary> /// 串口是否打开 /// </summary> /// <returns></returns> public bool IsOpen() { return serialPort.IsOpen; } /// <summary> /// 数据发送 /// </summary> /// <param name="data">要发送的数据字节</param> public void SendData(byte[] data) { try { serialPort.DiscardInBuffer(); serialPort.Write(data, 0, data.Length); } catch (Exception ex) { throw ex; } } /// <summary> /// 发送命令 /// </summary> /// <param name="SendData">发送数据</param> /// <param name="ReceiveData">接收数据</param> /// <param name="Overtime">超时时间</param> /// <returns></returns> public int SendCommand(byte[] SendData, ref byte[] ReceiveData, int Overtime) { if (serialPort.IsOpen) { try { ReceiveEventFlag = true; //关闭接收事件 serialPort.DiscardInBuffer(); //清空接收缓冲区 serialPort.Write(SendData, 0, SendData.Length); System.Threading.Thread.Sleep(50); int num = 0, ret = 0; while (num++ < Overtime) { if (serialPort.BytesToRead >= ReceiveData.Length) { break; } System.Threading.Thread.Sleep(50); } if (serialPort.BytesToRead >= ReceiveData.Length) { ret = serialPort.Read(ReceiveData, 0, ReceiveData.Length); } else { ret = serialPort.Read(ReceiveData, 0, serialPort.BytesToRead); } ReceiveEventFlag = false; //打开事件 return ret; } catch (Exception ex) { ReceiveEventFlag = false; throw ex; } } return -1; } ///<summary> ///数据发送 ///</summary> ///<param name="data">要发送的数据字符串</param> public void SendData(string data) { //禁止接收事件时直接退出 if (ReceiveEventFlag) { return; } if (serialPort.IsOpen) { serialPort.Write(data); } } ///<summary> ///将指定数量的字节写入输出缓冲区中的指定偏移量处。 ///</summary> ///<param name="data">发送的字节数据</param> ///<param name="offset">写入偏移量</param> ///<param name="count">写入的字节数</param> public void SendData(byte[] data, int offset, int count) { //禁止接收事件时直接退出 if (ReceiveEventFlag) { return; } if (serialPort.IsOpen) { serialPort.Write(data, offset, count); } } /// <summary> /// 数据接收 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void DataReceived(object sender, SerialDataReceivedEventArgs e) { //禁止接收事件时直接退出 if (ReceiveEventFlag) { return; } try { byte[] data = new byte[serialPort.BytesToRead]; serialPort.Read(data, 0, data.Length); if (Received != null) { Received(sender, new PortDataReciveEventArgs(data)); } } catch (Exception ex) { //throw ex; } } } }
using System; using System.Collections.Generic; using System.Text; namespace LY.FuelStationPOS.Protocol { public delegate void PortDataReceivedEventHandle(object sender, PortDataReciveEventArgs e); public class PortDataReciveEventArgs : EventArgs { private byte[] data; public byte[] Data { get { return data; } set { data = value; } } public PortDataReciveEventArgs() { this.data = null; } public PortDataReciveEventArgs(byte[] data) { this.data = data; } } }