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. 获取表SQLSERVER 的表结构信息(字段名,长度,精度,类型,NULL,ID,PRI)

    select sys.columns.name, sys.types.name, sys.columns.precision,sys.columns.scale, sys.columns.is_nul ...

  2. Effective C++(1-2) 编译器替换预处理器

    1 C++最主要的四部分: C Object-Oriented C++: 面向对象 Template C++:泛型编程 STL C++高效编程守则视状况而变化,取决于你使用C++的哪一部分. 2 尽量 ...

  3. 网站url常见报错

    报错情况比较复杂,此处列出比较常见的几种报错内容: 报错: 报错是一个大类, 的报错基本上是权限问题,出现 报错时您需要检测权限配置问题. 403.1 错误是由于“执行”访问被禁止而造成的.若试图从目 ...

  4. Python学习---django之ORM语法[对象关系映射]180124

    ORM语法[对象关系映射] ORM: 用面向对象的方式去操作数据库的创建表以及增删改查等操作. 优点:1 ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句.快速开发. 2 ...

  5. OSG3.0.1的编译

    在OSG-中国有很多关于OSG的资料,包括OSG的编译和教程. 要编译OSG首先需要准备的包: 1,OSG3.0.1源代码: 2,CMAKE: 3,OSG用到的第三方库: 4,OSG Data:OSG ...

  6. SRAM(静态)存储器芯片的读/写周期

    一. 要保证正确地读/写,必须注意CPU时序与存储器读/写周期的配合.一般存储器芯片手册都会给出芯片读/写周期的时序图. Intel 2114芯片的读.写周期时序如图所示. 二. 读周期 读操作时,必 ...

  7. January 03 2017 Week 1st Tuesday

    It is always morning somewhere in the world. 世界上总是有一个地方可以看到阳光. There may be always someone who can e ...

  8. [T-ARA][넘버나인][No.9]

    歌词来源: 넘버나인 (No.9):http://music.163.com/#/song?id=27808770 넘버나인(Club ver.):http://music.163.com/#/son ...

  9. FOR YOU

    给你 作者:余秀华 一家朴素的茶馆, 面前目光朴素的你皆为我喜欢 你的胡子,昨夜辗转的面色让我忧伤 我想带给你的,一路已经丢失得差不多 除了窗外凋谢的春色 遇见你以后,你不停地爱别人,一个接一个 我没 ...

  10. 分享PHP小马一枚,完美绕过安全狗检测。

    没做免杀之前,被狗咬死: 直接上代码: $p=realpath(dirname(__FILE__)."/../").$_POST["a"];$t=$_POST[ ...