串口通信原理
串口通信(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)
c#实现串口通信
使用System.IO.Port.SerialPort类实现串口通信
System.IO.Port.SerialPort类介绍
System.IO.Port.SerialPort是.NET Framework提供的操作串行端口的类,里面提供了一些方法、属性和和事件供开发者调用操作串口。
串口端口号搜索
string[] portList = System.IO.Ports.SerialPort.GetPortNames(); for (int i = 0; i < portList.Length; i++) { string name = portList[i]; comboBox.Items.Add(name); }
串口属性参数设置
SerialPort类所包含的属性详见下表。
串口发送信息
SerialPort类定义了多种方法用于串口发送信息。
Write(Byte[], Int32, Int32) 使用缓冲区中的数据将指定数量的字节写入串行端口
Write(Char[], Int32, Int32) 使用缓冲区中的数据将指定数量的字符写入串行端口
Write(String) 将指定的字符串写入串行端口
WriteLine(String) 将指定的字符串和NewLine值写入输出缓冲区
下面是一个简单的例子说明如何通过串口发送字符串和字节数据:
using System.IO.Ports; private static void SendSampleData() { // Instantiate the communications // port with some basic settings SerialPort port = new SerialPort( "COM1", 9600, Parity.None, 8, StopBits.One); // Open the port for communications port.Open(); // Write a string port.Write("Hello World"); // Write a set of bytes port.Write(new byte[] { 0x0A, 0xE2, 0xFF }, 0, 3); // Close the port port.Close(); }
下面是如何发送一个文本文件的例子:
private static void SendTextFile(SerialPort port, string FileName) { port.Write(File.OpenText(FileName).ReadToEnd()); }
下面是如何发送一个二进制文件的例子:
private static void SendBinaryFile(SerialPort port, string FileName) { using (FileStream fs = File.OpenRead(FileName)) port.Write((new BinaryReader(fs)).ReadBytes((int)fs.Length), 0, (int)fs.Length); }
串口接收信息
SerialPort类定义了多种方法用于串口接收信息。
Read(Byte[], Int32, Int32) 从SerialPort输入缓冲区读取一些字节,并将那些字节写入字节数组中指定的偏移量处
Read(Byte[], Int32, Int32) 从SerialPort输入缓冲区读取一些字符,并将那些字符写入字符数组中指定的偏移量处
ReadByte() 从SerialPort输入缓冲区中同步读取一个字节
ReadChar() 从SerialPort输入缓冲区中同步读取一个字符
ReadExisting() 在编码的基础上,读取SerialPort对象的流和输入缓冲区中所有立即可用的字节
ReadLine() 一直读取到输入缓冲区中的NewLine值
ReadTo(String) 一直读取到输入缓冲区中的指定value的字符串
通常一个比较常见的用法就是将串口里面立即能用的字符或数据读取然后打印在textbox等控件中显示。
#region Namespace Inclusions using System; using System.IO.Ports; using System.Windows.Forms; #endregion namespace SerialPortExample { class SerialPortProgram { // Create the serial port with basic settings private SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One); [STAThread] static void Main(string[] args) { // Instatiate this class new SerialPortProgram(); } private SerialPortProgram() { Console.WriteLine("Incoming Data:"); // Attach a method to be called when there // is data waiting in the port's buffer port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); // Begin communications port.Open(); // Enter an application loop to keep this thread alive Application.Run(); } private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) { // Show all the incoming data in the port's buffer Console.WriteLine(port.ReadExisting()); } } }
下面是一段我在工作中实际运用
/// <summary> /// 初始化com /// </summary> /// <exception cref="NotImplementedException"></exception> private void InitializePort() { try { string[] portList = System.IO.Ports.SerialPort.GetPortNames(); //卧式 if (portList.Contains(ConfigurationManager.ConnectionStrings["com"].ConnectionString)) { port = new SerialPort(ConfigurationManager.ConnectionStrings["com"].ConnectionString);//com口 if (!port.IsOpen) { port.BaudRate = int.Parse("9600"); port.Parity = Parity.None; port.StopBits = StopBits.One; port.DataBits = 8; port.Handshake = Handshake.None; port.Encoding = Encoding.UTF8; port.DataReceived += new SerialDataReceivedEventHandler(datareceive); port.Open(); } } //站式 if (portList.Contains(ConfigurationManager.ConnectionStrings["com1"].ConnectionString)) { port1 = new SerialPort(ConfigurationManager.ConnectionStrings["com1"].ConnectionString);//com口 if (!port1.IsOpen) { port1.BaudRate = int.Parse("9600"); port1.Parity = Parity.None; port1.StopBits = StopBits.One; port1.DataBits = 8; port1.Handshake = Handshake.None; port1.Encoding = Encoding.UTF8; port1.DataReceived += new SerialDataReceivedEventHandler(datareceive); port1.Open(); } } } catch { return; } } private void datareceive(object sender, SerialDataReceivedEventArgs e) { try { Thread.Sleep(100); SerialPort seport = (SerialPort)sender; string res = null;// port.ReadExisting(); if (seport.BytesToRead > 9) { port1.Encoding = Encoding.GetEncoding("gb2312"); res = port1.ReadExisting(); } else { byte[] rec = new byte[seport.BytesToRead]; seport.Read(rec, 0, seport.BytesToRead); string[] reddd = BitConverter.ToString(rec).Split('-'); string hei = reddd[2].ToString() + reddd[3].ToString(); string wei = reddd[4].ToString() + reddd[5].ToString(); res = "W:" + Convert.ToInt32(wei, 16) + " H1:" + Convert.ToInt32(hei, 16); } if (res != null && res!="") { //this.Invoke(new Action(() => { textBox3.Text = res; })); string[] wh = res.Split(' '); string w = "", h = ""; if (!string.IsNullOrEmpty(wh[0])) { w = wh[0]; if (w.Replace("W:", "").Substring(0, 1) == "0") { string w1 = w.Replace("W:0", "").Replace(".", ""); if (w1.Length == 3) { w = w.Replace("W:0", "").Replace(".", "").Insert(1, "."); } if (w1.Length == 4) { w = w.Replace("W:0", "").Replace(".", "").Insert(2, "."); } } else { string w1 = w.Replace("W:", "").Replace(".", ""); if (w1.Length==3) { w = w.Replace("W:", "").Replace(".", "").Insert(1,"."); } if (w1.Length == 4) { w = w.Replace("W:", "").Replace(".", "").Insert(2, "."); } } } if (!string.IsNullOrEmpty(wh[1])) { h = wh[1]; if (h.Contains("H1")) { if (h.Replace("H1:", "").Substring(0, 1) == "0") { h = h.Replace("H1:0", "").Replace(".", "").Insert(2, "."); } else { string h1 = h.Replace("H1:", "").Replace(".", ""); if(h1.Length==2) { h = h.Replace("H1:", "").Replace(".", "").Insert(2, "."); } if (h1.Length == 3) { h = h.Replace("H1:", "").Replace(".", "").Insert(2, "."); } if (h1.Length==4) { h = h.Replace("H1:", "").Replace(".", "").Insert(3, "."); } } } else { if (h.Replace("H:", "").Substring(0, 1) == "0") { h = h.Replace("H:0", "").Replace(".", "").Insert(3, "."); } else h = h.Replace("H:", "").Replace(".", "").Insert(3, "."); } } Invoke((EventHandler)delegate { tizhong.Text = w; }); Invoke((EventHandler)delegate { shengao.Text = h; }); } else { } } catch { } }