所用的软件:vs2019
1、创建一个新项目
打开vs2019,选择创建一个新项目,点击下一步:
搜索“窗体”,找到“Windows窗体应用(.NET Framework)”w项目,点击下一步:
更改项目名称和位置(可不做更改),最后点击创建:
最后生成这样一个界面:
然后修改窗口的名字,点击窗口,在右侧属性里找到“Text”,修改文字:
2、在窗口中添加控件
点击左侧的工具箱,并搜索添加button、label、comboBox、GroupBox、panle和serialPort这几个控件:
在添加的控件上点击右键,进入属性,找到“Text”属性,将button1~button3改成搜索串口、打开串口和关闭串口;将label1~label6改为串口号、波特率、校验位、数据位和停止位;
添加comboBox2中的元素,选中控件,右键,在属性中找到“Item”属性:
点击,并添加波特率元素:
相似的,然后在comboBox3~comboBox5中分别添加校验位、数据位和停止位的元素:
校验位:
数据位:
停止位:
接下来,双击搜索串口按钮,进入到Form1.cs进行编程:
在该事件下面编写下面程序,搜索并获取当前可用的串口:
string[] portname = SerialPort.GetPortNames();//定义一个字符串来获取串口
this.comboBox_port.Items.Clear();//清空comboBox1中的值
foreach (string port in portname)//遍历串口
{
var serialPort = new SerialPort();//把串口赋给定义的var变量
serialPort.PortName = port;
serialPort.Open();//打开串口
this.comboBox_port.Items.Add(port);//打开成功,则添加至下拉框
serialPort.Close();//关闭串口
}
同样的,双击打开串口:
if (serialPort1.IsOpen)//如果串口是打开的
{
try
{
serialPort1.Close();//先判断运行之前串口是否打开,若打开则要先关闭
}
catch
{
}
}
else
{
try
{
serialPort1.PortName = comboBox_port.Text;//选中串口
serialPort1.Open();//打开选中的串口
button_openport.Enabled = false;//此时打开串口按钮失效
comboBox_port.Enabled = false;//选择串口下拉框失效
comboBox_baud.Enabled = false;//选择波特率下拉框失效
comboBox_parity.Enabled = false;//选择校验位下拉框失效
comboBox_data.Enabled = false;//选择数据位下拉框失效
comboBox_stop.Enabled = false;//选择停止位下拉框失效
button_searchport.Enabled = false;//搜索串口按钮失效
button_closeport.Enabled = true;//关闭串口按钮可用
}
catch//若上面有错误操作,则返回下面的信息
{
MessageBox.Show("串口打开失败", "错误");
}
}
双击关闭串口按钮:
try
{
serialPort1.Close();//关闭串口
button_openport.Enabled = true;//可用
comboBox_port.Enabled = true;//可用
comboBox_baud.Enabled = true;//可用
comboBox_parity.Enabled = true;//可用
comboBox_data.Enabled = true;//可用
comboBox_stop.Enabled = true;//可用
button_searchport.Enabled = true;//可用
button_closeport.Enabled = false;//失效
}
catch (Exception err)//一般情况下关闭串口不会出错,加上以防万一
{
MessageBox.Show(err.Message);
}
接下来,双击窗体,配置默认参数:
接下来,添加下图中的控件,其中实时温度为label;发送、停止和修改温度为按钮,按照上面的方法添加就好;这里面多了一个显示实时温度的Text文本框和二选一的RadioButton按钮:
接下来就是重点了,双击发送按钮:
private void button_sendorder_Click(object sender, EventArgs e)//(button_sendorder就是添加的发送按钮)
{
this.sendCommand();//声明一个函数,用来执行发送按钮命令
}
private void sendCommand()//构造发送指令函数
{
if (this.serialPort1.IsOpen == true && (!radioButton2.Checked||!radioButton1.Checked ))//判断串口是否打开 与上 (获取测量实时温度
或 修改温度的设定值),若为真,则执行下面代码
{
try
{
//81 81 52 00 00 00 53 00(读指令:地址(基础值+仪表地址)+地址+固定格式+参数代号+默认值+默认值+CRC校验码(注:该读指令是根据自己所要控制的仪器的通讯协议而定,例如我的就是一个测量温度的仪表,该仪表的读指令就是上面的值)
string[] commandString = new string[] { "81", "81", "52", "00", "00", "00", "53", "00" };//将读指令或写指令传入字符串commandString中
byte[] commandByte = new byte[commandString.Length];//定义一个与commandString长度相同的字节数组
try
{
for (int i = 0; i < commandString.Length; i++)//以十六进制发送
{
commandByte[i] = Convert.ToByte(commandString[i], 16);//将字符串变成字节数组
}
}
catch (Exception er)//若上面有错误,则返回以下信息
{
MessageBox.Show(er.Message);//这个信息是系统自返回的
}
this.serialPort1.Write(commandByte, 0, commandByte.Length);//转换完成之后,发送给下位机指令
System.Threading.Thread.Sleep(this.delayTime);//线程睡眠
if (this.serialPort1.BytesToRead <= 10)//接收仪表返回的数据,并判断数据长度
{
byte[] byteReceive = new byte[this.serialPort1.BytesToRead];//定义字节数据接收数据
this.serialPort1.Read(byteReceive, 0, byteReceive.Length);//读取缓冲区中的数据
string text_R = "";//定义一个字符串
text_R = Convert.ToString((byteReceive[0] + byteReceive[1] * 256) * 0.1).ToString() ;//只读取测量温度(该仪表返回的前两个字节为温度字节,所以只用到了byteReceive[0] 和 byteReceive[1])
textBox_RT.Text = text_R.ToUpper();//显示并大写 实时温度的文本框的内容(textBox_RT就是添加的TextBox)
}
}
catch
{
//MessageBox.Show(er.Message);
}
}
}
至此,读取数据编码已完成。接下来把读取的数据以实时曲线的方式展现出来。
在这里,先在工具箱添加一个JYTEK工具;具体添加步骤可在B站视频查看:
SeeSharp基础篇 — 第一个 C# WinForm 程序_哔哩哔哩_bilibili
添加好上述工具之后,我们在窗体中添加listbox、EasyChartX、folderBrowserDialog这三个控件:
然后在,发送按钮指令下,添加以下代码用来在listbox中显示实时温度;在EasyChartX中绘制实时曲线:(注:这里要先添加
这个是B站简仪科技的一个GUI图像,跟着视频添加引用,这里不再赘述)
listBox1.Items.Add ( text_R.ToUpper());//在listBox中显示实时数据
listBox1.SelectedIndex = listBox1.Items.Count - 1;//使滚动条一直在最下方
foreach(var item in this.listBox1.Items)
{
temp.Add(item.ToString());//将listBox中的数组赋值给定义的列表数组temp(temp为自己定义的一个列表数组:List<string> temp = new List<string>();)
}
int len = listBox1.Items.Count;
string[] str = new string[len];
for (int i = 0; i < len; i++)
{
str[i] = listBox1.Items[i].ToString();
}
double[] doubleArray = Array.ConvertAll<string, double>(str, delegate (string s) { return double.Parse(s); });
easyChartX1.Plot(doubleArray);//EasyChartX绘制实时曲线
最后生成excel报表,来记录历史数据温度,在窗体中添加label1、label2并改为“写入温度数据”、”路径“;添加一个按钮button1、button2并改为“开始”、”浏览“;添加一个Textbox文本框用来显示保存路径:
双击开始按钮,添加如下代码:(注这里要先添加
这个是B站简仪科技的一个报表生成,跟着视频添加引用,这里不再赘述)
if(textBox_path.Text == string.Empty)
{
MessageBox.Show("请选择保存路径");
return;
}
excel = new ExcelReport();
//excel.Show();//显示创建的表格
excel.WriteTextToReport("A1", DateTime.Now.Date.ToLongDateString());
int len = listBox1.Items.Count;
string[] str = new string[len];
for (int i = 0;i<len;i++)
{
str[i] = listBox1.Items[i].ToString();
}
excel.WriteArrayToReport("A2", str);
excel.SaveAs(textBox_path.Text + @"\dataReport", ExcelSaveFormat.xlsx);
excel.Close();
然后,双击浏览按钮:
if(folderBrowserDialog1.ShowDialog() ==DialogResult.OK)
{
textBox_path.Text = folderBrowserDialog1.SelectedPath;//设置保存路径
}
添加好之后的界面如下图所示:
这里可根据自己的需要来添加定时器(是否需要实时采集温度数据)。
运行界面如下图所示:
最后附上完整程序代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Windows.Forms.DataVisualization.Charting;
using SeeSharpTools.JY.Report;
using SeeSharpTools.JY.ArrayUtility;
namespace 第一个优化的温度测量程序
{
public partial class Form1 : Form
{
int count;//定义一个整型count,用于定时器1
ExcelReport excel;
public Form1()
{
InitializeComponent();
button_closeport.Enabled = false;
button_sendorder.Enabled = false;
}
List<string> temp = new List<string>();
private int delayTime = 200;
private void Form1_Load(object sender, EventArgs e)
{
comboBox_port.Text = "COM1";
comboBox_baud.Text = "9600";
comboBox_parity.Text = "None";
comboBox_data.Text = "8";
comboBox_stop.Text = "Two";
timer1.Stop();//暂停计时
}
private void button_searchport_Click(object sender, EventArgs e)//搜索可用串口
{
string[] portname = SerialPort.GetPortNames();//定义一个字符串来获取串口
this.comboBox_port.Items.Clear();//清空comboBox1中的值
foreach (string port in portname)//遍历串口
{
var serialPort = new SerialPort();//把串口赋给定义的var变量
serialPort.PortName = port;
serialPort.Open();//打开串口
this.comboBox_port.Items.Add(port);//打开成功,则添加至下拉框
serialPort.Close();//关闭串口
}
}
private void button_openport_Click(object sender, EventArgs e)//打开所选择的串口
{
if (serialPort1.IsOpen)//如果串口是打开的
{
try
{
serialPort1.Close();//先判断运行之前串口是否打开,若打开则要先关闭
}
catch
{
}
}
else
{
try
{
serialPort1.PortName = comboBox_port.Text;//选中串口
serialPort1.Open();//打开选中的串口
button_openport.Enabled = false;//此时打开串口按钮失效
comboBox_port.Enabled = false;//选择串口下拉框失效
comboBox_baud.Enabled = false;//选择波特率下拉框失效
comboBox_parity.Enabled = false;//选择校验位下拉框失效
comboBox_data.Enabled = false;//选择数据位下拉框失效
comboBox_stop.Enabled = false;//选择停止位下拉框失效
button_searchport.Enabled = false;//搜索串口按钮失效
button_closeport.Enabled = true;//关闭串口按钮可用
}
catch//若上面有错误操作,则返回下面的信息
{
MessageBox.Show("串口打开失败", "错误");
}
}
}
private void button_closeport_Click(object sender, EventArgs e)//关闭所选择的串口
{
try
{
serialPort1.Close();
button_openport.Enabled = true;
comboBox_port.Enabled = true;
comboBox_baud.Enabled = true;
comboBox_parity.Enabled = true;
comboBox_data.Enabled = true;
comboBox_stop.Enabled = true;
button_searchport.Enabled = true;
button_closeport.Enabled = false;
}
catch (Exception err)//一般情况下关闭串口不会出错,加上以防万一
{
MessageBox.Show(err.Message);
}
}
private void timer1_Tick(object sender, EventArgs e)//定时器
{
button_sendorder_Click(button_sendorder, null);
if (serialPort1.IsOpen) //如果串口已经打开
{
count++;
}
}
private void button_sendorder_Click(object sender, EventArgs e)
{
this.sendCommand();
button_sendorder.Enabled = false;
timer1.Start();
timer2.Start();
}
private void sendCommand()//构造发送指令函数
{
if (this.serialPort1.IsOpen == true && (!radioButton2.Checked||!radioButton1.Checked ))
{
try
{
//81 81 52 00 00 00 53 00(读指令:地址(基础值+仪表地址)+地址+固定格式+参数代号+默认值+默认值+CRC校验码
string[] commandString = new string[] { "81", "81", "52", "00", "00", "00", "53", "00" };//将读指令或写指令传入字符串commandString中
byte[] commandByte = new byte[commandString.Length];//将字符串变成字节数组
try
{
for (int i = 0; i < commandString.Length; i++)//以十六进制发送
{
commandByte[i] = Convert.ToByte(commandString[i], 16);
}
}
catch (Exception er)
{
MessageBox.Show(er.Message);
}
this.serialPort1.Write(commandByte, 0, commandByte.Length);
System.Threading.Thread.Sleep(this.delayTime);
if (this.serialPort1.BytesToRead <= 10)
{
byte[] byteReceive = new byte[this.serialPort1.BytesToRead];//定义字节数据接收数据
this.serialPort1.Read(byteReceive, 0, byteReceive.Length);//读取缓冲区中的数据
string text_R = "";
text_R = Convert.ToString((byteReceive[0] + byteReceive[1] * 256) * 0.1).ToString() ;//只读取测量温度
textBox_RT.Text = text_R.ToUpper();
listBox1.Items.Add ( text_R.ToUpper());
listBox1.SelectedIndex = listBox1.Items.Count - 1;
foreach(var item in this.listBox1.Items)
{
temp.Add(item.ToString());
}
int len = listBox1.Items.Count;
string[] str = new string[len];
for (int i = 0; i < len; i++)
{
str[i] = listBox1.Items[i].ToString();
}
double[] doubleArray = Array.ConvertAll<string, double>(str, delegate (string s) { return double.Parse(s); });
easyChartX1.Plot(doubleArray);
}
}
catch
{
//MessageBox.Show(er.Message);
}
}
}
private void chart1_Click(object sender, EventArgs e)//双击chart事件来显示滑动条
{
}
private void timer2_Tick(object sender, EventArgs e)
{
//series = chart1.Series[0];
//foreach (var item in this.listBox1.Items)
//{
// temp.Add(item.ToString());
//}
//chart1.Series[0].Points.DataBindXY(DateTime.TryParseExact,temp);
}
private void button_modifytemperature_Click(object sender, EventArgs e)//声明修改参数函数
{
this.modifyTemperature();
button_sendorder.Enabled = true;
}
private void modifyTemperature()//构造修改参数函数
{
if (this.serialPort1.IsOpen == true && !radioButton1.Checked)
{
try
{
//81 81 43 00 00 00 44 00(写指令:地址(基础值+仪表地址)+地址+固定格式+参数代号+低字节(要改写参数值的低字节)+高字节(要改写参数值的高字节)+CRC校验码
double modifydata = Convert.ToInt32(textBox_modify.Text);
byte modifyhigh = 0xff, modifylow = 0xff, crchigh = 0xff, crclow = 0xff;
modifyhigh = (byte)((int)modifydata * 10 / 256);
modifylow = (byte)((int)modifydata * 10 % 256);
crchigh = (byte)((68 + modifydata * 10) / 256);
crclow = (byte)((68 + modifydata * 10) % 256);
//string[] commandString = new string[] { "81", "81", "43", "00", "modifylow", "modifyhigh", "crclow", "crchigh" };//将读指令或写指令传入字符串commandString中
//byte[] commandByte = new byte[commandString.Length];//将字符串变成字节数组
byte[] commandByte = new byte[] { 0x81, 0x81, 0x43, 0x00, modifylow, modifyhigh, crclow, crchigh };
this.serialPort1.Write(commandByte, 0, commandByte.Length);
serialPort1.DiscardOutBuffer();
System.Threading.Thread.Sleep(this.delayTime);
}
catch
{
//MessageBox.Show(er.Message);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
button_sendorder.Enabled = true;
timer1.Stop();
}
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
button_sendorder.Enabled = true;
}
private void radioButton2_CheckedChanged(object sender, EventArgs e)
{
}
private void button_writedata_Click(object sender, EventArgs e)
{
if(textBox_path.Text == string.Empty)
{
MessageBox.Show("请选择保存路径");
return;
}
excel = new ExcelReport();
//excel.Show();//显示创建的表格
excel.WriteTextToReport("A1", DateTime.Now.Date.ToLongDateString());
int len = listBox1.Items.Count;
string[] str = new string[len];
for (int i = 0;i<len;i++)
{
str[i] = listBox1.Items[i].ToString();
}
excel.WriteArrayToReport("A2", str);
excel.SaveAs(textBox_path.Text + @"\dataReport", ExcelSaveFormat.xlsx);
excel.Close();
}
private void button_browse_Click(object sender, EventArgs e)
{
if(folderBrowserDialog1.ShowDialog() ==DialogResult.OK)
{
textBox_path.Text = folderBrowserDialog1.SelectedPath;
}
}
}
}