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#串口通信及数据表格存储的更多相关文章

  1. Qt串口通信接收数据不完整的解决方法

    在使用串口接收数据时,当数据量大的时候会出现数据接收不完整的情况.因为串口数据获取函数readAll()由readyRead()信号触发,但readyRead()信号在串口读到起始标志时立即发送,并不 ...

  2. Qt串口通信接收数据不完整的解决方法(传输图片)

    在使用串口接收数据时,当数据量大的时候会出现数据接收不完整的情况.因为串口数据获取函数readAll()由readyRead()信号触发,但readyRead()信号在串口读到起始标志时立即发送,并不 ...

  3. 在Linux中如何使用命令进行RS-232串口通信和数据包解析

    文章首发于浩瀚先森博客 1. 获取串口号 在Linux系统中一切皆为文件,所以串口端口号也不例外,都是以设备文件的形式出现.也就是说我们可以用访问文本文件的命令来访问它们. a. 一般串口都是以/de ...

  4. C#串口通信发送数据

    1 发送数据 需要2个串口 http://www.openedv.com/thread-228847-1-1.html 下载源文件 File_Protocol_Test.rar

  5. 张高兴的 .NET Core IoT 入门指南:(五)串口通信入门

    在开始之前,首先要说明的是串口通信所用到的 SerialPort 类并不包含在 System.Device.Gpio NuGet 包中,而是在 System.IO.Ports NuGet 包中.之所以 ...

  6. Qt 串口通信 高速发送出错的解决方法总结

    使用网上的qextserialport-1.2类,自行开发多线程串口通信.开发的过程中,出现两个问题:   问题1:我用信号槽跨线程调用串口类MyCom 发送和接收数据,中间运行的时候,会内存错误,Q ...

  7. 串口通信:接受数据(仿真task写法)

    1.功能描述 设计一个串口数据接收模块.能够以设定的波特率(与发射端口速率匹配)接收数据,并输出保存到一个寄存器中. 2.过程描述 ①边沿检测器,识别出起始位时让接收使能端有效.这里需要排除边沿脉冲的 ...

  8. C#串口通信—向串口发送数据,同步接收返回数据

    最近写C#串口通信程序,系统是B/S架构.SerialPort类有一个DataReceived事件,用来接收串口返回的数据,但这种方式在C/S架构下很好用,但B/S就不好处理了.所以写了一个同步模式接 ...

  9. Delphi 串口通信数据位长度对传输数据的影响 转

      针对串口通信,关于设置数据位长度对通信的影响,如图: 在串口数据通信中,会看到串口参数设置.其中“数据位”设置,共有四档选项,分别是8.7.6.5.那么改变这个参数会对数据的传输有什么影响呢? 我 ...

随机推荐

  1. 第一个JavaScript代码

    既然我们的CSS就必须要要放再专门的style标签内  那么javascript也需要放在子级的标签内,那就是script标签内 在页面中,我们可以在body标签中放入<script type= ...

  2. 【MyBatis】 MyBatis入门

    1.MyBatis简介 MyBatis是这个框架现在的名字,而此框架最早的名字是IBatis,其名字的含义是“internet”.“abatis”两个单词的组合,是在2002年的时候开始的一个开源项目 ...

  3. LNMP-day3-php扩展缓存插件

     perl的编译问题 [root@localhost php5.6.33]# echo 'export LC_ALL=C' >> /etc/profile #设置环境变量,解决后面perl ...

  4. Web Service超限

    问题现状: {System.ServiceModel.CommunicationException: An error occurred while receiving the HTTP respon ...

  5. August 24th 2017 Week 34th Thursday

    If you have choices, choose the best. If you have no choice, do the best. 如果有选择,那就选择最好的:如果没有选择,那就努力做 ...

  6. 泛型举例:List<T>与DateTable相互转换

    一. DataTable转换到List<T> /// <summary> /// TableToList /// </summary> public class T ...

  7. Dictionary<Tkey.TValue>与SortedList

    一.概述 表示Key/Value集合,可以添加删除元素,允许按Key来访问元素.是Hashtable的泛型等效类. 它需要一个相等实现来确定键是否相等,可以使用实现了IEqualityComparer ...

  8. JS实现图片上传之前先预览

    <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat=&quo ...

  9. PhoneGap 白名单安全机制 navigator.app 加载外部页面返回以及退出介绍

    一. Phonegap 白名单安全机制 Phonegap应用的页面大多存在于本地,但有时需要加载外部的Web页面到应用内置的浏览器 视图中已完成特定的应用功能,出于安全性考虑,PhoneGap 设立了 ...

  10. IntelliJ IDEA Java项目中添加jar包

    1. 事先下载完成需要的javacsv.jar包. java项目在没有导入该jar包之前,显示如下图所示 2. 点击 File ->  Project Structure(快捷键 Ctrl + ...