C# 解决串口数据丢失问题
C # 串行类( SerialPort )是.NET Framework version 2.0 中一个新增的类,该类将串口操作了封装,从而为串口通信提供了简便方法。
但在实际串口通信的应用中,在串口高波特率大信息量的数据通信时,会出现丢失数据的问题。
通常我们使用SerialPort类接收串口发送过来的数据时,只需要写一个事件函数。
即DataReceived 事件绑定一个处理函数,然后就可以在该函 数中实现对串口数据的读取。 这个函数的内容可以根据我们的需求自行修改。就单纯从接收数据的角度出发, 假设该函数内容如下:
private void SerialPort_Rec(object sender, SerialDataReceivedEventArgs e)
{
string recString = string.Empty;
while (this.SerialPort.BytesToRead > 0)
{
recString += this.SerialPort.ReadExisting();
}
DealString(recString);
}
大多串口程序都会采用这种思路来实现串口通信。即先接收数据,然后处理数据,并在完成数据处理后,再次等待接收新数据。但这种实现方法在串口高速率大信息量通信时,会出现丢失数据的情况。
这里根据我自己的情况举一个例子:
我从下位机每次发送一帧数据给串口,我希望这一帧数据如下:
应答:$STA,A,27.0,1234,210914080000,X A为仪器ID,27.0为温度,1234为功耗1234mW,RTC时间,X为校验
但实际接收过程中往往只收到一半数据$STA,A,27.0,1234就直接执行了数据处理函数。
解决方法
采样多线程的方法来解决这个问题。
在主线程中专门接收数据,增加一个线程来处理数据。
最好不要选用像数组这样数据结构。因为此类数据结构在多线程中操作时必须频繁地“加锁”和“解锁”,在一定程度上会降低程序的性能。所以我们选用队列 Queen 作为数据池的数据结构。
使用列队的好处在这里充分体现了,数据先入先出,先收到的数据进行处理后就可以直接释放掉这部分内存,使得程序的运行效率大幅增加。
同时在结束处理的的时候,要对队列的长度进行判断,只有把整个队列处理结束才可以结束线程。
同时该方法也存很明显在劣势,如下:
当数据流入队的速度大于出列队(即接收数据的速度大于处理数据),举个简单例子,就相当于我们手上有一万块钱,每天要花20,每天只能赚10块钱,短时间内我们的钱是够用的,但时间一长,资源就会枯竭。
以上情况下,且需要长时间运行时,堆内存就有可能溢出。
这里贴出我的代码:
private void SerialPort_Rec(object sender, SerialDataReceivedEventArgs e)
{
while (this.SerialPort.BytesToRead > 0)
{
data_Queue.Enqueue((byte)SerialPort.ReadByte());
}
}
数据处理线程:
public void DealDataThread()
{
List<byte> databytes = new List<byte>();
while (!canStop)
{
if(data_Queue.Count>=24)
{
if (data_Queue.Dequeue()== 36)
{
if(data_Queue.Dequeue() == 36)
{
for(int i = 0; i < 20; i++)
{
databytes.Add(data_Queue.Dequeue());
}
fileWriter.WriteLine(System.Text.Encoding.Default.GetString(databytes.ToArray()));
databytes.Clear();
}
}
}
}
}
|