C#串口通信及数据表格存储
1.开发环境
系统:win10
开发工具:Visual Studio 2017
程序下载地址:https://download.csdn.net/download/wang0huan/11145500
分不够的可留言邮箱。
2.界面设计
串口通信的界面大致如此,在此基础上添加项目所需的调试指令与数据存储功能,界面排布方面可参考其他教程。
3.串口通信的实现
本文串口通信主要使用的技术点有:队列、多线程、List等,相比传统单线程方案更加稳定,适合大数据收发。
- 串口基础参数设置
#region 设置串口的属性SetPortProperty
private void SetPortProperty()//设置串口的属性
{
sp = new SerialPort
{
PortName = cbPortName.Text.Trim(), //设置串口名
//BaudRate = Convert.ToInt32(cbBaudRate.Text.Trim()) //设置串口波特率
BaudRate = int.Parse(cbBaudRate.Text.Trim())
};
float f = Convert.ToSingle(cbStop.Text.Trim());//设置停止位
if (f == )
{
sp.StopBits = StopBits.None;
}
else if (f == 1.5)
{
sp.StopBits = StopBits.OnePointFive;
}
else if (f == )
{
sp.StopBits = StopBits.One;
}
else if (f == )
{
sp.StopBits = StopBits.Two;
}
else
{
sp.StopBits = StopBits.One;
}
sp.DataBits = Convert.ToInt16(cbDataBits.Text.Trim());//设置数据位 string s = cbParity.Text.Trim();//设置奇偶校验位
if (s.CompareTo("无") == )
{
sp.Parity = Parity.None;
}
else if (s.CompareTo("奇校验") == )
{
sp.Parity = Parity.Odd;
}
else if (s.CompareTo("偶校验") == )
{
sp.Parity = Parity.Even;
}
else
{
sp.Parity = Parity.None;
}
sp.ReadTimeout = -; //设置超市读取时间
//sp.NewLine = "/r/n"; //根据实际情况吧,当使用ReadLine()时需要定义一下
//sp.RtsEnable = true; //启用RTS发送请求信号,根据实际情况吧 //定义串口DataReceived事件,当串口接受到数据后触发事件
sp.DataReceived += Sp_DataReceived; //添加事件注册
}
#endregion
下面重点说明串口的发送和接收的实现方法:
- 串口接收实现
串口接收事件Sp_DataReceived,串口组件的接收触发事件,接收数据并在接收文本框显示(便于调试),注意多线程访问UI资源要用invoke方式来同步,同时将接收的数据添加到SerialRevList中,同时加锁保护List。同时用一个独立线程来进行接收数据的处理,线程之间数据加锁同步。
#region 串口接受事件Sp_DataReceived
private void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)//串口接受事件
{
try
{
if (this.sp.BytesToRead > )
{
byte[] buffer = new byte[this.sp.BytesToRead];
this.sp.Read(buffer, , buffer.Length);
received_count += buffer.Length;//增加接收计数
Rev_builder.Clear();//清除字符串构造器的内容
//因为要访问ui资源,所以需要使用invoke方式同步ui,串口接收事件会自动创建线程,多线程访问控件需要使用invoke来委托
this.Invoke((EventHandler)(delegate
{
if (rbRcvHex.Checked == false)//接受数据字符串显示
{
//tbxRcvData.Text += sp.ReadLine(); //一直读取到输入缓冲区中的 NewLine 值,使用这个需要注意换行符
//直接按ASCII规则转换成字符串
Rev_builder.Append(Encoding.Default.GetString(buffer));
}
else//接受数据Hex显示
{
//依次的拼接出16进制字符串
foreach (byte b in buffer)
{
Rev_builder.Append(b.ToString("X2") + " ");
}
}
this.tbxRcvData.AppendText(Rev_builder.ToString());//接受数据显示在文本框
labelRcvCount.Text = "接收字节数:" + received_count.ToString();//修改接收计数
sp.DiscardInBuffer();//丢弃接受缓冲区数据
})); Monitor.Enter(this.SerialRevList); //数据保护
this.SerialRevList.AddRange(buffer); //交由接收处理线程处理
Monitor.Exit(this.SerialRevList);
}
}
catch (Exception ex)
{
throw ex;
}
}
#endregion
单独开一个线程来进行串口接收数据的处理,下面给出了常用帧处理给出的实例,充分利用了List集合带来的数据处理的便捷;
#region 串口接受缓存数据处理线程函数
/// <summary>
/// 串口接受缓存数据处理线程函数
/// </summary>
/// <param name="obj"></param>
private void SerialRev(object obj)
{
while (true)
{
try
{
this.SerialRevWaiter.WaitOne();
Monitor.Enter(this.SerialRevList); //AWGM.AWGHandle(SerialRevData);
//至少包含帧头(1字节)+长度(2字节)+数据 + 结束位(1字节)
if (this.SerialRevList.Count > 0) //接收缓存有数据
{
//AWG接收字符串协议处理方式
string str = Encoding.Default.GetString(SerialRevList.ToArray());
if (str.Substring(0, 1) == "*") //起始位
{
int len = Convert.ToInt32(str.Substring(1, 2));
if (SerialRevList.Count < len + 4)//未接收完整
{
break;
}
else
{
if (str.Substring(len + 3, 1) == "^")//结束位
{
//lineyValue1 = Convert.ToDouble(str.Substring(3, 5));
//lineyValue2 = Convert.ToDouble(str.Substring(8, 5)); //多线程访问控件使用invoke来委托
this.Invoke((EventHandler)(delegate
{
this.tbxRcvData.AppendText(str);
//tbxAWGMTem.Text = str.Substring(3, 5);
}));
} }
SerialRevList.RemoveRange(0, len + 4);//去掉处理一帧的数据
}
else
{
SerialRevList.RemoveAt(0);//帧头不正确时,记得清除
} //IODH接收16进制处理方式 }
Monitor.Exit(this.SerialRevList);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "串口接受线程处理错误!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Thread.Sleep(50);
}
} #endregion
- 串口发送
考虑到UI界面可能触发多个串口发送需求,此时也需要用到多线程来保证数据发生的安全;
串口发送采用队列的方式来存储发送数据,然后利用一个线程来一次发送队列中的数据;
串口发送实例:
private void btnTECPIDSet_Click(object sender, EventArgs e)
{
Monitor.Enter(this.SerialSendQueue);
this.SerialSendQueue.Enqueue("pidsettem" + string.Format("{0:0.000}", Convert.ToSingle(tbxTecKp.Text)) + string.Format("{0:0.000}",
Convert.ToSingle(tbxTecKi.Text)) + string.Format("{0:0.000}", Convert.ToSingle(tbxTecKd.Text)));
Monitor.Exit(this.SerialSendQueue);
}
串口发送线程:
#region 串口发送数据线程函数
/// <summary>
/// 这个线程函数负责发送串口命令
/// </summary>
/// <param name="obj"></param>
private void SerialSend(object obj)
{
while (true)
{
try
{
this.SerialSendWaiter.WaitOne();
Monitor.Enter(this.SerialSendQueue); //队列排他锁,实现同步访问
string buf = null;
if (this.SerialSendQueue.Count > ) //有命令
{
buf = this.SerialSendQueue.Dequeue(); //取出发送队列第一个命令
}
if (buf != null)
{
byte[] buffer = Encoding.Default.GetBytes(buf);
this.sp.DiscardInBuffer();
this.sp.Write(buffer, , buffer.Length); tbxSendData.AppendText(buf + "\r\n");
send_count += buf.Length;
labelSendCount.Text = "发送字节数:" + send_count.ToString();//更新发送计数
}
Monitor.Exit(this.SerialSendQueue); //释放锁
Thread.Sleep();
}
catch (Exception ex)
{
throw ex;
}
}
}
#endregion
4.表格数据存储EXCEL
常用测试中经常用到测试数据的自动化记录,这样由串口采集输出到excel中功能就变得非常实用;
下面介绍实现方法:结合的技术是dataGridView控件和SaveFileDialog;
由于电脑不一定都安装有office,直接使用office组件功能缺乏一定的通用性,因此本实例采用存储为csv格式表格数据文件,然后根据设置的格式转化为EXCEL文件即可。
代码参考如下:
#region 测试数据存储(DataGridView和DataTable的使用)
/// <summary>
/// 初始化DataTable,并将datatable绑定到DataGridView的数据源,新建列标题
/// </summary>
private void InitDatable()
{
//新建列
DataColumn col1 = new DataColumn("休眠", typeof(string));
DataColumn col2 = new DataColumn("门磁A-12V", typeof(string));
DataColumn col3 = new DataColumn("门磁A-5V", typeof(string));
DataColumn col4 = new DataColumn("门A-LED", typeof(string));
DataColumn col5 = new DataColumn("门磁B-12V", typeof(string));
DataColumn col6 = new DataColumn("门磁B-5V", typeof(string));
DataColumn col7 = new DataColumn("门B-LED", typeof(string));
//添加列
dt.Columns.Add(col1);
dt.Columns.Add(col2);
dt.Columns.Add(col3);
dt.Columns.Add(col4);
dt.Columns.Add(col5);
dt.Columns.Add(col6);
dt.Columns.Add(col7); this.dataGridView1.DataSource = dt.DefaultView;
} /// <summary>
/// 按键触发记录测试数据,即添加行数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTestRecord_Click(object sender, EventArgs e)
{
//dt.Rows.Clear();//清空数据
DataRow dr = dt.NewRow();//新增行
dr[] = "OK";
dr[] = "OK";
dr[] = "OK";
dr[] = "OK";
dr[] = "OK";
dr[] = "OK";
dr[] = "F";
this.dt.Rows.Add(dr);//增加行
} /// <summary>
/// 清除表格记录内容
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnCleanRecord_Click(object sender, EventArgs e)
{
dt.Rows.Clear();//清空数据
} /// <summary>
/// 测试记录导出
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnExport_Click(object sender, EventArgs e)
{
IODM_TEST.DataGridViewToExcel(dataGridView1);
}
#endregion
DataGridViewToExcel实现如下:
#region DateGridView导出到csv格式的Excel
/// <summary>
/// 常用方法,列之间加/,一行一行输出,此文件其实是csv文件,不过默认可以当成Excel打开。
/// </summary>
/// <remarks>
/// using System.IO;
/// </remarks>
/// <param name="dgv">表格数据控件</param>
public static void DataGridViewToExcel(DataGridView dgv)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.Filter = "Execl files (*.csv)|*.csv";
dlg.FilterIndex = ;
dlg.RestoreDirectory = true;
dlg.CreatePrompt = true;
dlg.Title = "保存为Excel文件"; if (dlg.ShowDialog() == DialogResult.OK)
{
Stream myStream;
myStream = dlg.OpenFile();
StreamWriter sw = new StreamWriter(myStream, System.Text.Encoding.GetEncoding(-));
string columnTitle = "";
try
{
//写入列标题
for (int i = ; i < dgv.ColumnCount; i++)
{
if (i > )
{
columnTitle += "/";
}
columnTitle += dgv.Columns[i].HeaderText;
}
sw.WriteLine(columnTitle); //写入列内容
for (int j = ; j < dgv.Rows.Count; j++)
{
string columnValue = "";
for (int k = ; k < dgv.Columns.Count; k++)
{
if (k > )
{
columnValue += "/";
}
if (dgv.Rows[j].Cells[k].Value == null)
columnValue += "";
else
columnValue += dgv.Rows[j].Cells[k].Value.ToString().Trim();
}
sw.WriteLine(columnValue);
}
sw.Close();
myStream.Close();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
finally
{
sw.Close();
myStream.Close();
}
}
}
#endregion
C#串口通信及数据表格存储的更多相关文章
- Qt串口通信接收数据不完整的解决方法
在使用串口接收数据时,当数据量大的时候会出现数据接收不完整的情况.因为串口数据获取函数readAll()由readyRead()信号触发,但readyRead()信号在串口读到起始标志时立即发送,并不 ...
- Qt串口通信接收数据不完整的解决方法(传输图片)
在使用串口接收数据时,当数据量大的时候会出现数据接收不完整的情况.因为串口数据获取函数readAll()由readyRead()信号触发,但readyRead()信号在串口读到起始标志时立即发送,并不 ...
- 在Linux中如何使用命令进行RS-232串口通信和数据包解析
文章首发于浩瀚先森博客 1. 获取串口号 在Linux系统中一切皆为文件,所以串口端口号也不例外,都是以设备文件的形式出现.也就是说我们可以用访问文本文件的命令来访问它们. a. 一般串口都是以/de ...
- C#串口通信发送数据
1 发送数据 需要2个串口 http://www.openedv.com/thread-228847-1-1.html 下载源文件 File_Protocol_Test.rar
- 张高兴的 .NET Core IoT 入门指南:(五)串口通信入门
在开始之前,首先要说明的是串口通信所用到的 SerialPort 类并不包含在 System.Device.Gpio NuGet 包中,而是在 System.IO.Ports NuGet 包中.之所以 ...
- Qt 串口通信 高速发送出错的解决方法总结
使用网上的qextserialport-1.2类,自行开发多线程串口通信.开发的过程中,出现两个问题: 问题1:我用信号槽跨线程调用串口类MyCom 发送和接收数据,中间运行的时候,会内存错误,Q ...
- 串口通信:接受数据(仿真task写法)
1.功能描述 设计一个串口数据接收模块.能够以设定的波特率(与发射端口速率匹配)接收数据,并输出保存到一个寄存器中. 2.过程描述 ①边沿检测器,识别出起始位时让接收使能端有效.这里需要排除边沿脉冲的 ...
- C#串口通信—向串口发送数据,同步接收返回数据
最近写C#串口通信程序,系统是B/S架构.SerialPort类有一个DataReceived事件,用来接收串口返回的数据,但这种方式在C/S架构下很好用,但B/S就不好处理了.所以写了一个同步模式接 ...
- Delphi 串口通信数据位长度对传输数据的影响 转
针对串口通信,关于设置数据位长度对通信的影响,如图: 在串口数据通信中,会看到串口参数设置.其中“数据位”设置,共有四档选项,分别是8.7.6.5.那么改变这个参数会对数据的传输有什么影响呢? 我 ...
随机推荐
- Effective C++(9) 构造函数调用virtual函数会发生什么
问题聚焦: 不要在构造函数和析构函数中调用virtual函数,因为这样的调用不会带来你预想的结果. 让我先来看一下在构造函数里调用一个virtual函数会发生什么结果 Demo class Trans ...
- 《C++ Primer Plus》读书笔记之四—分支语句和逻辑操作符
第六章 分支语句和逻辑操作符 1.&&的优先级低于关系操作符. 2.取值范围:取值范围的每一部分都使用AND操作符将两个完整的关系表达式组合起来: if(age>17&& ...
- namespace 相关
#include <iostream>using namespace std;namespace first{ int var = 5;}namespace second{ double ...
- PHPWAMP自启异常,服务器重启后Apache等服务不会自动重启的原因分析
在使用“PHPWAMP自动任务”时,不少学生遇到如下问题: “phpwamp绿色集成环境重启动电脑(服务器)后,不会自动启动网站服务” (如果是其他环境或是自己搭建时遇到此问题,也是可以用此法解决) ...
- Python 及其基础语法
重新开始玩 Python,打算就是学完实验楼的"Python3 简明教程",然后就可以玩点小项目,先前学了点 Python2 就不管它啦. 以上. 认识 Python Python ...
- Mac下安装Spark
1.Scala 官网下载scala安装包后解压,路径随意. 编辑/etc/bash_profile添加$SCALA_HOME并修改相应PATH 2.SSH无密码登陆 ssh-keygen -t rsa ...
- oracle imp 报12154错误解决办法
imp test/test@orcl file=/backup.dmp full=y --报错 imp test/test@127.0.0.1/orcl file=/backup.dmp full ...
- angularJs中的模块化操作
一.全局的写法 有可能会跟其他程序有冲突 <!DOCTYPE HTML> <html ng-app="myApp"> <head> <me ...
- maven的pom.xml文件报错问题
第一次用 Spring Starter Project 创建一个Spring应用时,POM 文件报错: Project build error: Non-resolvable parent POM f ...
- 2018-2019-2 网络对抗技术 20165322 Exp4 恶意代码分析
2018-2019-2 网络对抗技术 20165322 Exp4 恶意代码分析 目录 实验内容与步骤 系统运行监控 恶意软件分析 实验过程中遇到的问题 基础问题回答 实验总结与体会 实验内容与步骤 系 ...