上位机开发常用到串口通信来控制设备,串口通信的主要参数:COM口,波特率(9600),停止位(One),数据位(8),校验位(None),括号中的是常用值,具体意思我也不太懂,会用能实现功能就行哈哈;
开发工具:VS2012; 开发前需要添加以下服务引用:
....... using System.IO.Ports;
窗体搭建,因为没有串口设备供调试 ,所以我用虚拟串口即两个串口互相连接来实现通信,窗体分为上下两部分,对应两个串口区,上图为虚拟串口设置,下图为Winform样式:
首先创建一个baseCom类:
1.声明变量,构造函数:
public delegate void delegateOnShowMsg(comType type,string msg); public delegate void delegateOnOpenCom(comType type, bool onOff); public event delegateOnShowMsg OnShow; public event delegateOnOpenCom OnOpen; comType type; string baudRate = string.Empty; string parity = string.Empty; string stopBits = string.Empty; string dataBits = string.Empty; string portName = string.Empty; public System.IO.Ports.SerialPort comPort = new System.IO.Ports.SerialPort(); public baseCom(string portName, string baudRate, string stopBits, string dataBits, string parity,comType type) { this.portName = portName; this.baudRate = baudRate; this.stopBits = stopBits; this.dataBits = dataBits; this.parity = parity; this.type = type; }
2.打开串口和关闭串口函数:
public void OpenPort() { try { if (comPort.IsOpen) comPort.Close(); comPort.BaudRate = int.Parse(baudRate); comPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), stopBits); comPort.DataBits = int.Parse(dataBits); comPort.Parity = (Parity)Enum.Parse(typeof(Parity), parity); comPort.PortName = portName; comPort.Handshake = Handshake.None; comPort.Open(); if (comPort.IsOpen) { showOnoff(true); comPort.Handshake = Handshake.None; comPort.DataReceived += comPort_DataReceived; } showMsg(portName + " Open Success"); } catch (Exception ex) { showMsg("Unable to Open " + portName); showMsg(ex.Message); showOnoff(false); } } public void ClosePort() { if (comPort.IsOpen) { comPort.Close(); comPort.Dispose(); showOnoff(false); showMsg(portName + " Close Success"); } }
3.接受数据和发送数据函数:
public void comPort_DataReceived(object sender,SerialDataReceivedEventArgs e) { try { int n = comPort.BytesToRead; if (n == 0) return; byte[] buffer = new byte[n]; comPort.Read(buffer, 0, n); string rcv = Encoding.Default.GetString(buffer, 0, buffer.Length); showMsg("RECV:" + rcv); } catch (Exception ex) { ClosePort(); showMsg("Com Closed:" + ex.Message); } } public void comPort_SendData(string msg) { if (msg == "") { showMsg("CAN Not EMPTY"); return; } if (comPort == null) { showMsg("First Open Port"); return; } if (!comPort.IsOpen) { showMsg("COM Not Open"); return; } byte[] data = Encoding.Default.GetBytes(msg); comPort.Write(data, 0, data.Length); showMsg("SEND:" + msg); }
4.显示信息,显示状态函数:
public void showMsg(string msg) { if (OnShow != null) OnShow(type, msg); } public void showOnoff(bool flag) { if (OnOpen != null) OnOpen(type, flag); } public enum comType { _up, _down }
OK,baseCOM类已定义完成,现在编写窗体各控件的功能函数:
1.初始化控件,主要是下拉菜单的Item的添加,初始值,初始状态:
public string[] baudRates = { "2400", "4800", "9600", "19200", "39400", "43000", "56000" }; public string[] paritys = { "None", "ODD", "Even" }; public string[] dataBits = { "5", "6", "7", "8" }; public string[] stopBits = { "None", "One", "Two", "OnePointFive" } ; public void InitControls() { comboBox_com.Items.Clear(); comboBox_baudRate.Items.Clear(); comboBox_dataBits.Items.Clear(); comboBox_parity.Items.Clear(); comboBox_StopBit.Items.Clear(); comboBox_com2.Items.Clear(); foreach (var v in baudRates) comboBox_baudRate.Items.Add(v); foreach (var v in stopBits) comboBox_StopBit.Items.Add(v); foreach (var v in paritys) comboBox_parity.Items.Add(v); foreach (var v in dataBits) comboBox_dataBits.Items.Add(v); foreach (var v in System.IO.Ports.SerialPort.GetPortNames()) { comboBox_com.Items.Add(v); comboBox_com2.Items.Add(v); } comboBox_com.SelectedIndex = 0; comboBox_baudRate.SelectedIndex = 2; comboBox_dataBits.SelectedIndex = 3; comboBox_parity.SelectedIndex = 0; comboBox_StopBit.SelectedIndex = 1; comboBox_com2.SelectedIndex = 0; }
2.声明两个COM类,comUp和comDown:
public delegate void delegateOnOff(comType type,bool onoff);//定义两个委托 public delegate void delegateShow(comType type,string msg);//为了防止报错“从不是创建控件的线程控制它” baseCom comUp; // 定义上串口COM baseCom comDown; //定义下串口COM Image imgOn, imgOff; //指示灯 Thread th; //线程用来实现自动发送
3.编写打开(关闭)串口按钮点击事件(打开comUp和打开comDown代码一样,只贴出comUp):
private void button_OpenCom_Click(object sender, EventArgs e) { string cmd = ((Button)sender).Text.Split(' ')[0]; if (comUp != null) comUp.ClosePort(); if (cmd == "Close") return; string s1 = comboBox_com.SelectedItem.ToString(); string s2 = comboBox_baudRate.SelectedItem.ToString(); string s3 = comboBox_StopBit.SelectedItem.ToString(); string s4 = comboBox_dataBits.SelectedItem.ToString(); string s5 = comboBox_parity.SelectedItem.ToString(); comUp = new baseCom(s1, s2, s3, s4, s5,comType._up); comUp.OnOpen += SignalState; comUp.OnShow += ShowMsg; comUp.OpenPort(); }
4.编写发送数据按钮点击事件:
private void button_Send_Click(object sender, EventArgs e) { if (comUp != null) comUp.comPort_SendData(textBox_ComSend.Text); }
5.编写数据接受发送显示到文本框及状态指示灯变化函数:
public void SignalState(comType type,bool onoff) { if (this.InvokeRequired) { delegateOnOff d = new delegateOnOff(SignalState); this.Invoke(d,new object[]{type,onoff}); } else { if (type == comType._up) { pictureBox_ComSignal.Image = onoff ? imgOn : imgOff; button_OpenCom.Text = onoff ? "Close COM" : "Open COM"; } else { pictureBox_Com2.Image = onoff ? imgOn : imgOff; button_OpenCom2.Text = onoff ? "Close COM" : "Open COM"; } } } public void ShowMsg(comType type, string msg) { if (this.InvokeRequired) { delegateShow d = new delegateShow(ShowMsg); this.Invoke(d, new object[] { type, msg }); } else { string date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff") + " "; if (type == comType._up) { textBox_serialPort.Text += (date + msg + " "); } else { textBox_serialPort2.Text += (date + msg + " "); } } }
6.Monitor函数,即线程函数,用来实现数据自动发送(没有进行准确定时,大概设了个间隔):
public void Monitor() { double i =0; while (i++<10000000000) { if (i == 1000000000) { i = 0; if (checkBox_AutoSend.Checked) { if (comUp != null) comUp.comPort_SendData(textBox_ComSend.Text); } if (checkBox_AutoSend2.Checked) { if (comDown != null) comDown.comPort_SendData(textBox_comSend2.Text); } } } }
至此,串口通信功能已基本实现,可在此框架上进行实际应用开发,编码新手,欢迎大家批评指正!看下效果图(COM1和COM11虚拟连接):