一、引言
该简易串口助手借助OxyPlot库进行开发,具有波形显示功能。
二、正文
关于C#串口助手的开发代码网上较多,不再赘述。开始尝试使用GDI技术,但实现相对繁琐,后了解并使用OxyPlot。OxyPlot是.Net的跨平台绘图库,在NuGet程序包管理器添加OxyPlot的引用即可使用该控件。使用VSPD工具虚拟两串口并运行程序,使用串口调试工具发送数据,波形显示界面如图1所示。
图1 波形显示界面
三、程序代码
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Threading; 10 using System.Windows.Forms; 11 using System.IO.Ports; 12 using OxyPlot; 13 using OxyPlot.Annotations; 14 using OxyPlot.Axes; 15 using OxyPlot.Series; 16 using System.Collections; 17 18 namespace SerialPortCommunication 19 { 20 public partial class FormSampleSerialPortTool : Form 21 { 22 public FormSampleSerialPortTool() 23 { 24 InitializeComponent(); 25 } 26 SerialPort sp = new SerialPort(); 27 private void Form1_Load(object sender, EventArgs e) 28 { 29 foreach (string com in System.IO.Ports.SerialPort.GetPortNames()) 30 { 31 comboBoxPortName.Items.Add(com); 32 } 33 comboBoxPortName.SelectedIndex = 0; 34 comboBoxBaudRate.Items.Add("4800"); 35 comboBoxBaudRate.Items.Add("9600"); 36 comboBoxBaudRate.Items.Add("19200"); 37 comboBoxBaudRate.Items.Add("38400"); 38 comboBoxBaudRate.Items.Add("57600"); 39 comboBoxBaudRate.Items.Add("76800"); 40 comboBoxBaudRate.Items.Add("115200"); 41 comboBoxBaudRate.SelectedIndex = 1; 42 for (int i = 5; i < 9; i++) 43 { 44 comboBoxDataBits.Items.Add(i.ToString()); 45 } 46 comboBoxDataBits.SelectedIndex = 3; 47 for (int i = 0; i < 3; i++) 48 { 49 comboBoxStopBits.Items.Add(i.ToString()); 50 } 51 comboBoxStopBits.Items.Add("1.5"); 52 comboBoxStopBits.SelectedIndex = 1; 53 comboBoxParity.Items.Add("无"); 54 comboBoxParity.Items.Add("奇校验"); 55 comboBoxParity.Items.Add("偶校验"); 56 comboBoxParity.SelectedIndex = 0; 57 checkBoxSendHex.Checked = true; 58 checkBoxReciveHex.Checked = true; 59 sp.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived); 60 WaveDisplay(); 61 } 62 private void buttonOpenSerialPort_Click(object sender, EventArgs e) 63 { 64 if (sp.IsOpen==false) 65 { 66 try 67 { 68 sp.PortName = comboBoxPortName.Text; 69 sp.BaudRate = Convert.ToInt32(comboBoxBaudRate.Text); 70 sp.DataBits = Convert.ToInt32(comboBoxDataBits.Text); 71 switch (comboBoxStopBits.Text) 72 { 73 case "0": 74 sp.StopBits = StopBits.None; 75 break; 76 case "1": 77 sp.StopBits = StopBits.One; 78 break; 79 case "2": 80 sp.StopBits = StopBits.Two; 81 break; 82 case "1.5": 83 sp.StopBits = StopBits.OnePointFive; 84 break; 85 default: 86 break; 87 } 88 switch (comboBoxParity.Text) 89 { 90 case "无": 91 sp.Parity = Parity.None; 92 break; 93 case "奇校验": 94 sp.Parity = Parity.Odd; 95 break; 96 case "偶校验": 97 sp.Parity = Parity.Even; 98 break; 99 default: 100 break; 101 } 102 sp.Open(); 103 buttonOpenSerialPort.Text = "关闭串口"; 104 comboBoxPortName.Enabled = false; 105 comboBoxBaudRate.Enabled = false; 106 comboBoxDataBits.Enabled = false; 107 comboBoxStopBits.Enabled = false; 108 comboBoxParity.Enabled = false; 109 } 110 catch (Exception) 111 { 112 MessageBox.Show("串口打开失败!","错误提示"); 113 } 114 } 115 else 116 { 117 sp.Close(); 118 buttonOpenSerialPort.Text = "打开串口"; 119 comboBoxPortName.Enabled = true; 120 comboBoxBaudRate.Enabled = true; 121 comboBoxDataBits.Enabled = true; 122 comboBoxStopBits.Enabled = true; 123 comboBoxParity.Enabled = true; 124 } 125 } 126 private void buttonSendData_Click(object sender, EventArgs e) 127 { 128 try 129 { 130 string sendAreaText = textBoxDataSendArea.Text.Trim(); 131 byte[] sendData = null; 132 if (checkBoxSendHex.Checked==false) 133 { 134 sendData = Encoding.ASCII.GetBytes(sendAreaText); 135 } 136 else 137 { 138 if ((sendAreaText.Length) % 2==0) 139 { 140 sendData = new byte[(sendAreaText.Length) / 2]; 141 for (int i = 0; i < sendData.Length; i++) 142 { 143 sendData[i] = Convert.ToByte(sendAreaText.Substring(i * 2, 2).Replace(" ", ""), 16); 144 } 145 } 146 else 147 { 148 string sendAreaNewText = sendAreaText.Substring(0, sendAreaText.Length - 1) + "0" + sendAreaText.Substring(sendAreaText.Length-1,1); 149 sendData = new byte[(sendAreaNewText.Length) / 2]; 150 for (int i = 0; i < sendData.Length; i++) 151 { 152 sendData[i] = Convert.ToByte(sendAreaNewText.Substring(i * 2, 2).Replace(" ", ""), 16); 153 } 154 } 155 } 156 sp.Write(sendData,0,sendData.Length); 157 } 158 catch (Exception) 159 { 160 if (sp.IsOpen == false) 161 { 162 MessageBox.Show("请先打开串口!", "错误提示"); 163 } 164 else 165 { 166 MessageBox.Show("发送数据错误!", "错误提示"); 167 } 168 } 169 } 170 byte[] receiveData; 171 Queue qData = new Queue(); 172 Queue qTime = new Queue(); 173 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) 174 { 175 try 176 { 177 receiveData= new byte[sp.BytesToRead]; 178 sp.Read(receiveData, 0, receiveData.Length); 179 string receiveContent = null; 180 if (checkBoxReciveHex.Checked==false) 181 { 182 receiveContent += new ASCIIEncoding().GetString(receiveData); 183 } 184 else 185 { 186 for (int i = 0; i < receiveData.Length; i++) 187 { 188 qData.Enqueue(receiveData[i]); 189 qTime.Enqueue(DateTime.Now); 190 } 191 StringBuilder data = new StringBuilder(); 192 for (int i = 0; i < receiveData.Length; i++) 193 { 194 data.Append(receiveData[i].ToString("X2") + " "); 195 } 196 receiveContent += data.ToString().ToUpper(); 197 } 198 textBoxDataReceiveArea.Invoke((EventHandler)(delegate 199 { 200 textBoxDataReceiveArea.AppendText(receiveContent); 201 })); 202 } 203 catch (Exception) 204 { 205 MessageBox.Show("接收数据错误!", "错误提示"); 206 } 207 } 208 private void buttonClearData_Click(object sender, EventArgs e) 209 { 210 textBoxDataReceiveArea.Text = ""; 211 } 212 void WaveDisplay() 213 { 214 PlotModel myPlotModel = new PlotModel(); 215 myPlotModel.Title = "数据波形"; 216 DateTimeAxis xDateAxis = new DateTimeAxis() 217 { 218 Position = AxisPosition.Bottom, 219 StringFormat = "hh:mm:ss", 220 Minimum = DateTimeAxis.ToDouble(DateTime.Now), 221 Maximum = DateTimeAxis.ToDouble(DateTime.Now.AddMinutes(1)), 222 TitlePosition = 0, 223 IntervalLength = 60, 224 MinorIntervalType = DateTimeIntervalType.Seconds, 225 IntervalType = DateTimeIntervalType.Seconds, 226 MajorGridlineStyle = LineStyle.Solid, 227 MinorGridlineStyle = LineStyle.None, 228 }; 229 LinearAxis yValueAxis = new LinearAxis(); 230 yValueAxis.MajorGridlineStyle = LineStyle.Solid; 231 yValueAxis.MinorGridlineStyle = LineStyle.Dot; 232 yValueAxis.IntervalLength = 20; 233 yValueAxis.Angle = 60; 234 yValueAxis.IsZoomEnabled = false; 235 yValueAxis.IsPanEnabled = false; 236 yValueAxis.Maximum = 100; 237 yValueAxis.Minimum = -1; 238 LineSeries mySeries = new LineSeries(); 239 var myLine = new LineSeries(); 240 myLine.Color = OxyColors.Green; 241 myLine.StrokeThickness = 2; 242 myLine.MarkerSize = 3; 243 myLine.MarkerStroke = OxyColors.DarkGreen; 244 myLine.MarkerType = MarkerType.Diamond; 245 plotView1.Model = myPlotModel; 246 myPlotModel.Axes.Add(xDateAxis); 247 myPlotModel.Axes.Add(yValueAxis); 248 myPlotModel.Series.Add(myLine); 249 Task.Factory.StartNew(() => 250 { 251 var rand = new Random(); 252 bool bToMove = false; 253 while (true) 254 { 255 if (!bToMove) 256 { 257 TimeSpan timeSpan = DateTime.Now - DateTimeAxis.ToDateTime(xDateAxis.Minimum); 258 if (timeSpan.TotalSeconds >= (1)) 259 { 260 bToMove = true; 261 } 262 } 263 else 264 { 265 if (qTime.Count > 0) 266 { 267 DateTime t = (DateTime)qTime.Peek(); 268 xDateAxis.Minimum = DateTimeAxis.ToDouble(t.AddSeconds(-10)); 269 xDateAxis.Maximum = DateTimeAxis.ToDouble(t.AddSeconds(0)); 270 myLine.Points.Add(new DataPoint(DateTimeAxis.ToDouble((DateTime)qTime.Dequeue()), (byte)qData.Dequeue())); 271 } 272 if (myLine.Points.Count > 1000) 273 { 274 myLine.Points.RemoveAt(0); 275 } 276 myPlotModel.InvalidatePlot(true); 277 Thread.Sleep(10); 278 } 279 } 280 }); 281 } 282 } 283 }
四、参考资料
1、GitHub - oxyplot/oxyplot: A cross-platform plotting library for .NET
https://github.com/oxyplot/oxyplot
2、OxyPlot在WinForm中的应用_C#_芝麻麻雀-Asp.Net学习之路-CSDN博客
https://blog.csdn.net/weixin_42930928/article/details/81706540