现象:
环境:上位机软件,Csharp编写,SerialPort类实现串口异步通讯;
下位机,STM32F1系列单片机。
串口设置: 波特率9600, 8bit数据位,1bit停止位, 无校验。
当上位机与下位机串口通讯时,通过观察收发,发现有时候接收的数据并不是一个完整的数据帧。
例如: 发 10 11 12 13 14
收到可能为 10 11 12
然后再收到 13 14.
但是通过串口助手分别与上位机和下位机通讯,同样的串口设备却不存在这个问题。基本可排除下位机问题了,可能是SerialPort类实现的串口接收问题。
上网查询,结果很多都存在这个问题。
解决
网上给出的办法基本有两个:
1, 在SerialPort的异步串口接收函数内,申请一个大的缓存,当存满指定长度的缓存时再触发。或者自己实现组包。
2, 在异步串口接收函数开始,对本异步线程延迟一段时间,等待所有数据接收完。 这个方法,也有效,不过延迟的时间需根据接收数据最大长度来定,这对短的数据接收就有些不可取了。
我采用的算是第一种方法吧。
首先申请了一个私有字段 List comRecvBuffer = new List();
然后在接收函数内
private void sp1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
while (ComDevice.BytesToRead > 0)
{
comRecvBuffer.Add((byte)ComDevice.ReadByte());
if (comRecvBuffer.Count >= 1024)//缓存溢出
{
comRecvBuffer.Clear();
//清缓存
ComDevice.DiscardInBuffer();//清输入缓冲区
return;
}
}
RecvDataAnalyze(null, comRecvBuffer.ToArray());
}
在RecvDataAnalyze 这个函数内判断接收到的是否为完整数据帧,因为是MODBUS协议,我先判断了地址,然后计算CRC跟后两位是否相同来判断是否接收了完整帧。是的话就显示然后处理。 若不是的话需要清缓存,如下:
comRecvBuffer.Clear();
//清缓存
ComDevice.DiscardInBuffer();//清输入缓冲区