本着学习研究的态度,用c#语言实现简单的串口通讯工具。c#
串口通讯(Serial Communications)的概念很是简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通讯慢,可是串口能够在使用一根线发送数据的同时用另外一根线接收数据。它很简单而且可以实现远距离通讯。因为串口通讯是异步的,端口可以在一根线上发送数据同时在另外一根线上接收数据。其余线用于握手,但不是必须的。串口通讯最重要的参数是波特率、数据位、中止位和奇偶校验。对于两个进行通讯的端口,这些参数必须匹配。数组
波特率:这是一个衡量符号传输速率的参数。指的是信号被调制之后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每一个字符格式包含10位(1个起始位,1个中止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。通常调制速率大于波特率,好比曼彻斯特编码)。一般电话线的波特率为14400,28800和36600。波特率能够远远大于这些值,可是波特率和距离成反比。高波特率经常用于放置的很近的仪器间的通讯,典型的例子就是GPIB设备的通讯。框架
数据位:这是衡量通讯中实际数据位的参数。当计算机发送一个信息包,实际的数据每每不会是8位的,标准的值是六、7和8位。如何设置取决于你想传送的信息。好比,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。若是数据使用简单的文本(标准 ASCII码),那么每一个数据包使用7位数据。每一个包是指一个字节,包括开始/中止位,数据位和奇偶校验位。因为实际数据位取决于通讯协议的选取,术语"包"指任何通讯的状况。异步
中止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。因为数据是在传输线上定时的,而且每个设备有其本身的时钟,极可能在通讯中两台设备间出现了小小的不一样步。所以中止位不只仅是表示传输的结束,而且提供计算机校订时钟同步的机会。适用于中止位的位数越多,不一样时钟同步的容忍程度越大,可是数据传输率同时也越慢。ide
奇偶校验位:在串口通讯中一种简单的检错方式。有四种检错方式:偶、奇、高和低。固然没有校验位也是能够的。对于偶和奇校验的状况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,若是数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。若是是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备可以知道一个位的状态,有机会判断是否有噪声干扰了通讯或者是否传输和接收数据是否不一样步。工具
1 载波检测(DCD) 2 接受数据(RXD) 3 发出数据(TXD) 4 数据终端准备好(DTR) 学习
5 信号地线(SG) 6 数据准备好(DSR) 7 请求发送(RTS) 8 清除发送(CTS) 9 振铃指示(RI) this
System.IO.Port.SerialPort是.NET Framework提供的操做串行端口的类,里面提供了一些方法、属性和和事件供开发者调用操做串口。编码
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 = 115200, int dataBit = 8, int stopBit = 1, int timeout = 5000) { 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, 0, buffer.Length); string str = EncodingType.GetString(buffer); if (OnComReceiveDataHandler != null) { OnComReceiveDataHandler(str); } } #endregion } }
本界面主要功能是操做两个串口,一个发送数据另外一个接收数据。左侧设置两串口的一些参数,设置完成后点击"打开发送接收串口",如两串口成功打开,右侧即可操做发送和接受数据。spa
using System; using System.Windows.Forms; namespace PortControlDemo { public partial class FrmPortControl : Form { #region 字段/属性 int[] BaudRateArr = new int[] { 110, 300, 1200, 2400, 4800, 115200 }; int[] DataBitArr = new int[] { 6, 7, 8 }; int[] StopBitArr = new int[] { 1, 2, 3}; int[] TimeoutArr = new int[] { 500, 1000, 2000, 5000, 10000 }; 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