串口接收,一般我们都是开启新的线程,用新线程来处理接收数据的事务,以下代码是容易产生分包的代码:(示例代码为C#)
void Received_dat()
{
while (serialPort.IsOpen)
{
Thread.Sleep(50); //延时50毫秒
try
{
int n = serialPort.BytesToRead; // 获取串口已缓存到的字节数
byte[] buf = new byte[n]; //定义字节型数组存放串口读取到的数据
if (n != 0) //排除没有接收到数据的情况
{
serialPort.Read(buf, 0, n); //读串口,把数据存放到字节型数组
Application.Current.Dispatcher.Invoke(delegate{
TB_AESPassword.Text = UTF8Encoding.UTF8.GetString(buf);}); 更新主线程UI
}
}
catch (Exception)
{
}
}
}
分析原因:以上代码放进新线程中运行,会在新线程中无限循环,当串口接收的数据还不完整的时候,比如接收了50字节(共计100字节),这个时循环程序执行到了serialPort.Read(buf, 0, n); 这句,就把前50个字节给读出来了,剩下50字节,自然就会在下一个循环中接收到,这就产生了串口数据分包。 基于以上分析,我们可以利用两次读取BytesToRead属性值来判断接收到的数据是完整的,还是数据正在进行中。。。。。。 第一次读BytesToRead完后,延时一小断时间,然后再读一次,如果数据相等,说明数据已接收完,如果数据不相等,那么说明接收还在进行中,这样就可以避免串口数据分包的情况发生。
实现代码如下:
void Received_dat()
{
while (serialPort.IsOpen)
{
Thread.Sleep(50); //延时50毫秒,防止CPU占用过多
try
{
int n = serialPort.BytesToRead; // 获取串口已缓存到的字节数
Thred.Sleep(1); //延时1毫秒
int n1 = serialPort.BytesToRead; //再次获取串口已缓存到的字节数
byte[] buf = new byte[n]; //定义字节型数组存放串口读取到的数据
if(n==n1) //如果第一次和第二次读取的字节数相等,那么认为数据接收完整。
{
if (n != 0) //排除没有接收到数据的情况
{
serialPort.Read(buf, 0, n); //读串口,把数据存放到字节型数组
Application.Current.Dispatcher.Invoke(delegate{
TB_AESPassword.Text = UTF8Encoding.UTF8.GetString(buf);}); 更新主线程UI
}
}
}
catch (Exception)
{
}
}
}
经实际调试,以上代码很好的解决了分包问题,达到了预期的效果。