串行接口(Serial port)又称“串口”,主要用于串行式逐位数据传输。常见的有一般电脑应用的RS-232(使用 25 针或 9 针连接器)和工业电脑应用的半双工RS-485与全双工RS-422。
串行接口按电气标准及协议来分,包括RS-232-C、RS-422、RS485、USB等。 RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。USB是近几年发展起来的新型接口标准,主要应用于高速数据传输领域。
RS-232-C
也称标准串口,是目前最常用的一种串行通讯接口。它是在1970年由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是“数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准”。传统的RS-232-C接口标准有22根线,采用标准25芯D型插头座。自IBM PC/AT开始使用简化了的9芯D型插座。至今25芯插头座现代应用中已经很少采用。电脑一般有两个串行口:COM1和COM2,9针D形接口通常在计算机后面能看到。现在有很多手机数据线或者物流接收器都采用COM口与计算机相连。RS-422
为改进RS-232通信距离短、速率低的缺点,RS-422定义了一种平衡通信接口,将传输速率提高到10Mb/s,传输距离延长到4000英尺(速率低于100kb/s时),并允许在一条平衡总线上连接最多10个接收器。RS- 422是一种单机发送、多机接收的单向、平衡传输规范,被命名为TIA/EIA-422-A标准。RS-485
为扩展应用范围,EIA又于1983年在RS-422基础上制定了RS-485 标准,增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上,同时增加了发送器的驱动能力和冲突保护特性,扩展了总线共模范围,后命名为 TIA/EIA-485-A标准。Universal Serial Bus(通用串行总线)
简称USB,是目前电脑上应用较广泛的接口规范,由Intel、Microsoft、Compaq、IBM、NEC、Northern Telcom等几家大厂商发起的新型外设接口标准。USB接口是电脑主板上的一种四针接口,其中中间两个针传输数据,两边两个针给外设供电。USB接口速度快、连接简单、不需要外接电源,传输速度12Mbps,新的USB 2.0可达480Mbps;电缆最大长度5米,USB电缆有4条线:2条信号线,2条电源线,可提供5伏特电源,USB电缆还分屏蔽和非屏蔽两种,屏蔽电缆传输速度可达12Mbps,价格较贵,非屏蔽电缆速度为1.5Mbps,但价格便宜;USB通过串联方式最多可串接127个设备;支持热插拔。最新的规格是USB 3.0。RJ-45接口
是以太网最为常用的接口,RJ45是一个常用名称,指的是由IEC(60)603-7标准化,使用由国际性的接插件标准定义的8个位置(8针)的模块化插孔或者插头。
串口属性
1. PortName 串口名 默认值COM1
串口对于操作系统来说是一个文件,如果设置PortName为本机不存在的串口名(即文件名),如“COM7”或“COMK”,Open()打开串口将失败,提示“端口COM7不存在”。
2. BaudRate 获取或设置串行波特率bit/s 默认值9600
比特率=波特率X单个调制状态对应的二进制位数。
RS232是要用在近距离传输上最大距离为30M
RS485用在长距离传输最大距离1200M
3. DataBits 获取或设置每个字节的标准数据位长度 默认值8
当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包
是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
4. StopBits 获取或设置每个字节的标准停止位数 默认值One
用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数
越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
5. Parity 获取或设置奇偶校验检查协议 默认值None
在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验
位为0,保证逻辑高的位数是偶数个。如果是奇校验,
校验位位1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收
串口(COM)读写操作的三种方式:
第1种方式是采用微软在.NET2.0推出了一个串口控件,SerialPort类,但必须是.NET2.0才可以。
第2种方式是用API写串口通信,虽然难度高,但可以方便实现自己想要的各种功能。
第3种方式是通过采用Visual Studio 6.0中原来的MSComm控件这是最简单的,最方便的方法,但需要注册。
在.NET下关于串口的操作以及相关方法事件:
在.NET 2.0+以上,已经提供了关于串口操作的组件
能够很方便的操作串口进行数据的读写
对于一些重要的方法以及事件解释如下:
1. Open() 打开一个新的串行端口连接
2. Close() 关闭端口连接,将 IsOpen 属性设置为 false,并释放内部 Stream 对象
3. Read(Byte[], int, int) 输入缓冲区读取一些字节并将那些字节写入字节数组中指定的偏移量处
4. ReadByte() 从 SerialPort 输入缓冲区中同步读取一个字节
5. ReadChar() 从 SerialPort 输入缓冲区中同步读取一个字符
6. ReadExisting() 在编码的基础上,读取 SerialPort 对象的流和输入缓冲区中所有立即可用的字节
6. ReadLine() 一直读取到输入缓冲区中的 NewLine 值
7. ReadTo() 一直读取到输入缓冲区中的指定 value 的字符串
8. Write(string) 将指定的字符串写入串行端口
9. Write(Byte[], int, int) 使用缓冲区的数据将指定数量的字符写入串行端口
10. WriteLine() 将指定字符串和NewLine值写入输出缓冲区
11. DiscardInBuffer() 丢弃接收缓冲区的数据
12. DiscardOutBuffer() 丢弃发送缓冲区的数据
12. static GetPortNanes() 获取当前计算机的串口名称数组
13. DataReceive事件 数据接收事件的方法
不保证对接收到的每个字节引发 DataReceived 事件。 使用 BytesToRead 属性确定缓冲区中剩余的要读取的数据量。从 SerialPort 对象接收数据时,将在辅助线程上引发 DataReceived 事件。
14. PinChanged事件 串行管脚更改事件的方法
在 SerialPort 对象进入 BreakState 时引发,但在端口退出 BreakState 时不引发。将在辅助线程上引发 PinChanged 事件。
15. ErrorReceived事件 错误事件的方法
如果在流的尾字节上出现奇偶校验错误,将向输入缓冲区添加一个值为 126 的额外字节。将在辅助线程上引发 PinChanged 事件。
代码例子:
#region 打开串口 private void btnOpenCom_Click(object sender, EventArgs e) { string _sCom = string.Empty;//串口号 if (CheckIsSltCom(ref _sCom)) { try { string _sDateBits = this.cbDateBits.Text.Trim();//数据位 string _sBondRate = this.cbBondRate.Text.Trim();//波特率 string _sStopBit = this.cbStopBit.Text.Trim();//停止位 string _sParity = this.cbParity.Text.Trim();//效验 InitOpenCom(_sCom, _sDateBits, _sBondRate, _sParity, _sStopBit); toolStripStatusLabel.Text = string.Format("成功打开串口号:{0}.", _sCom); } catch (Exception ex) { toolStripStatusLabel.Text = string.Format("打开串口号:{0}失败!原因:{1}.", _sCom, ex.Message.Trim()); serialPort.Dispose(); } finally { WriteLog(toolStripStatusLabel.Text, LogType.Wirtelog); } } } #endregion
#region 初始化串口 private void InitOpenCom(string _sCom, string _sDateBits, string _sBondRate, string _sParity, string _sStopBit) { if (serialPort.IsOpen) serialPort.Close(); serialPort.PortName = _sCom; serialPort.BaudRate = Convert.ToInt32(_sBondRate); serialPort.DataBits = Convert.ToInt32(_sDateBits); switch (_sStopBit) { case "1": serialPort.StopBits = StopBits.One; break; case "1.5": serialPort.StopBits = StopBits.OnePointFive; break; case "2": serialPort.StopBits = StopBits.Two; break; default: serialPort.StopBits = StopBits.None; break; } serialPort.DataBits = Convert.ToInt32(_sDateBits); switch (_sParity) { case "偶": serialPort.Parity = Parity.Even; break; case "奇": serialPort.Parity = Parity.Odd; break; case "空格": serialPort.Parity = Parity.Space; break; case "标志": serialPort.Parity = Parity.Mark; break; case "无": serialPort.Parity = Parity.None; break; } serialPort.Open(); } #endregion
#region 判断选择串口是否合法 private bool CheckIsSltCom(ref string _sCom) { if (string.IsNullOrEmpty(cbCom.Text.Trim())) { DevOperate.ShowToolTip<ComboBoxEdit>(toolTipController, "请选择串口号!", cbCom); return false; } _sCom = cbCom.Text.Trim(); return true; } #endregion
#region 关闭串口 private void btnCloseCom_Click(object sender, EventArgs e) { string _sCom = string.Empty;//串口号 if (CheckIsSltCom(ref _sCom)) { try { if (serialPort.IsOpen) serialPort.Close(); toolStripStatusLabel.Text = string.Format("成功关闭串口号:{0}.", _sCom); } catch (Exception ex) { toolStripStatusLabel.Text = string.Format("关门串口号:{0}失败!原因:{1}.", _sCom, ex.Message.Trim()); serialPort.Dispose(); } finally { WriteLog(toolStripStatusLabel.Text, LogType.Wirtelog); } } } #endregion
#region 串口数据接收事件 private string sComStr = string.Empty; private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { try { byte[] _readBuffer = new byte[serialPort.ReadBufferSize]; lock (_readBuffer.SyncRoot) { int _nBytesRead = serialPort.Read(_readBuffer, 0, _readBuffer.Length); string _sStrTmpCom = ByteHelper.byteToHexStr(_readBuffer, _nBytesRead); ; sComStr += _sStrTmpCom; if (OnSerialPortReceived != null) OnSerialPortReceived(_sStrTmpCom); if (!ckComReciveFilter.Checked) WriteLog(string.Format("接收:{0}.", _sStrTmpCom), LogType.Wirtelog); if (sComStr.StartsWith("68") && sComStr.EndsWith("16")) { if (ckComReciveFilter.Checked) WriteLog(string.Format("接收:{0}.", sComStr), LogType.Wirtelog); if (OnSerialPortReceivedBys != null) OnSerialPortReceivedBys(ByteHelper.HexStrToByteArray(sComStr), 0); sComStr = string.Empty; _readBuffer = null; } } } catch (Exception ex) { WriteLog(string.Format("接收异常,原因:{0}", ex.Message.Trim()), LogType.DialogLog); } } #endregion
运行效果