您的位置:首页 > 编程语言 > C#

C#串口通信时丢失数据的一种解决方法

2010-07-29 11:26 323 查看
刘凯 周云耀
武汉理工大学信息工程学院 武汉市(430070)
E-mail: lk.6@163.com
摘要:C # 串行类( SerialPort )是.NET Framework version 2.0中一个新增的类,该类将串口操作了封装,从而为串口通信提供了简便方法,而且具有功能强大、通信快速、实时性好等特点。但在实际串口通信的应用中,在串口高波特率大信息量的数据通信时,会出现丢失数据的问题。此时如果只是增加串口类的缓存容量是不能根本解决问题的。本文就是从此实际中遇到的问题出发,分析了问题形成的原因,并结合实际开发的经验,给出了一种解决问题的方法。
关键词:C# 串行类 丢失数据 队列
中图分类号:TP311
1.
前言
C # 串行类( SerialPort ) 是Visual Studio 2005 中一个新增的类,它为应用程序提供了通过串口收发数据的简便方法。C # 串行类具有功能强大、通信快速、实时性好等特点[1]。该类用于控制串行端口文件资源,提供同步 I/O 和事件驱动的 I/O、对管脚和中断状态的访问以及对串行驱动程序属性的访问。SerialPort 类支持:ASCIIEncoding、UTF8Encoding等几乎所有编码格式[2]。
2.
C#串口通信的一般实现方法及潜存的问题
串口有两种读取方式,一种是同步的,另外一种就是基于事件的异步读取方式。因为同步接收会阻塞线程所以在大数据量通信时常采用异步方法读取。
在通信之前要对串口对象做一个初始化,这包括设置PortName(串口号)、BaudRate(波特率)、Parity(奇偶校验)、DataBits(数据位数)等,在此就不赘述了。
由于SerialPort类的封装,屏蔽了很多通信细节。所以异步串口通信的实现逻辑很简单。我们只须要为SerialProt类的DataReceived事件绑定一个处理函数,然后就可以在该函数中实现对串口数据的读取。假设该函数名为GetStrFromPort,其实现示例如下。
Public void GetStrFromPort( object sender, SerialDataReceivedEventArgs e )
{
string str = null;

str = serialprot.ReadTo( "/n/r" ); //读到该帧数据结束处
serialport.DiscardInBuffer(); //清除缓存中的内容
DealWithProtStr( str ); //处理接收到的数据
}

- 1 -
大多串口程序都会采用这种思路来实现串口通信。即先接收数据,然后处理数据,并在完成数据处理后,再次等待接收新数据。但这种实现方法在串口高速率大信息量通信时,会出现丢失数据的情况。
数据丢失的原因在于数据接收与数据处理同在一个线程中,如果数据处理时间较长,来不及接收的数据只能暂存于缓存中。一旦缓存满了,新到的数据就会冲刷掉未来的及接收的数据,从而造成数据的丢失。此时如果只单单增加缓存的容量是不能根本解决问题的。这也是我们在实际应用中遇到的问题。
3.
丢失数据问题的解决办法
有鉴于此,我们解决问题的思路就是将数据接收与数据处理分离开来,使数据接收得到最快的时间响应。通过在实际中的反复实验,我们最终采用了多线程加数据池来解决此问题。
设计思路如下:数据接收与数据处理分别在两个线程中进行,数据接收线程负责数据接收并将接收的数据存入数据池中;数据处理线程负责从数据池中读取数据和处理数据。程序设计思路如图1-1所示。
由图1-1可以看出,两个线程有可能会同时访问数据池。因此为使数据接收得到最快的时间响应,最好不要选用像数组这样数据结构。因为此类数据结构在多线程中操作时必须频繁地“加锁”和“解锁”,在一定程度上会降低程序的性能。所以我们选用队列Queen作为数据池的数据结构。
队列在顺序存储方面非常有用。数据对象在队列的一端插入,另一端移除。而且当两个线程同时访问队列,如果一个线程只负责数据存入,另一个线程只负责操作数据读取时,不会出现多线程的资源争用的问题,所以不必使用“加锁”和“解锁”操作,从而提高了程序的运行效率。

图1-1 多线程加数据池模式的串口通信设计示意图
http://www.paper.edu.cn
下面是程序的伪代码。
数据接收线程:
ReceiveThread() { string str = ReceiveFormPort(); //从串口读取数据 queue. Enqueue( str ); //将数据存入队列 }
数据处理线程
DealData()
{
while( true ) //循环检测队列
{
if( queue.Count >= 1 ) //队列中有数据
{
string data = queue.Dequeue(); //将数据出队
DealWithReceiveData( data ); //处理数据
}
}
}
通过这种多线程加数据池的方法,我们就可以将数据接与数据处理独立开来,从而使串口接收事件得到最快的速度响应。从而达到我们消除丢失数据的问题。
4.
结束语
通过利用多线程和数据池,我们解决了C#串口类,在高波特率通信时丢失数据的问题。该设计模式是非常强健的。它在处理高速串口通信时能够最大程度的保证通信的正确性。

参考文献:
[1] Schildt H. 《C # 完全手册》 [M]. 北京:电子工业出版,2005
[2] Microsoft. MSDN 2005(Microsoft Developer Network)
[3] Robison S, Harvey B.《 Profession C # 》 [M]. America: Wrox Press Inc, 2005.
[4] 李渊博. 《基于WindowsCE.NET的串口通信及应用》.中国科技论文在线,2005
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: