请教下,WinForm C#串口如何接收不定长的数据?
搞个上位机,C#这个串口控件接收事件有时会将一包数据分成1次、2次等接收,不会每次都能完整接收完。 本帖最后由 墨非 于 2021-9-17 15:42 编辑简单粗暴一点,读完一包数据,小延时3~5ms 继续读,还有数据跟前一包拼一起算一帧。 配好超时,可以100ms一次,缓冲区大点,一次读上百字节,这样短包超时退出,长包收满退出。 怎么判定接收完成?数据有结束符吗? C#的事件不靠谱,我都是自己开个线程专门负责接受,事件速度太慢,通信协议定好后用状态机拼接,死循环判断有没有收到数据,有数据进状态机,没数据就sleep(1) 墨非 发表于 2021-9-17 15:34
简单粗暴一点,读完一包数据,小延时3~5ms 继续读,还有数据跟前一包拼一起算一帧。 ...
3~5ms估计不够,现在做的跟单片机差不多,接收做个超时判断,认为接收一包完成。数据时不定长的。 wye11083 发表于 2021-9-17 15:54
配好超时,可以100ms一次,缓冲区大点,一次读上百字节,这样短包超时退出,长包收满退出。 ...
{:lol:} 100ms会不会有点久 thepresent 发表于 2021-9-17 16:10
怎么判定接收完成?数据有结束符吗?
没有,不定长,没有协议,目前通过超时判断一包数据。 初音之恋 发表于 2021-9-17 16:27
C#的事件不靠谱,我都是自己开个线程专门负责接受,事件速度太慢,通信协议定好后用状态机拼接,死循环判断 ...
线程周期读取判断是否有数据吗,周期时间定了多少? 初音之恋 发表于 2021-9-17 16:27
C#的事件不靠谱,我都是自己开个线程专门负责接受,事件速度太慢,通信协议定好后用状态机拼接,死循环判断 ...
你是不会玩吧, 串口那点速度,不至于。 直接调用Win32 API的SetCommTimeouts函数,比单片机都灵活。 浮华一生 发表于 2021-9-17 19:59
你是不会玩吧, 串口那点速度,不至于。
速度快会丢事件,玩过你就知道了 jufr12315 发表于 2021-9-17 18:10
线程周期读取判断是否有数据吗,周期时间定了多少?
public void MonitorRun() //异步的方式,接受优先级高
{
mReadThread = new Thread(new ThreadStart(delegate
{
int count;
int i;
byte[] rebuf = new byte;
int step = 0;
MonitorData data = new MonitorData();
mControl.Run();
Thread.Sleep(1000);
byte xor = 0;
byte [] cmd = new byte;
byte length = 0;
while (true)
{
if (mNeedClose)
{
mReadThread = null;
return;
}
count = this.BytesToRead;
if (count > 0)
{
this.Read(rebuf, 0, count);
for (i = 0; i < count; i++)
{
if (mNeedClose)
{
mReadThread = null;
return;
}
switch (step)
{
case 0:
if (rebuf == mReHead1)
{
step = 1;
xor = rebuf;
}
break;
case 1:
if (rebuf == mReHead2)
{
xor ^= rebuf;
step = 2;
}
else if (rebuf != mReHead1)
{
step = 1;
}
break;
case 2:
data.Unit = rebuf;
xor ^= rebuf;
step = 3;
break;
case 3:
if (rebuf == 0xFF)
{
xor ^= rebuf;
step = 4;
}
else
{
step = 0;
}
break;
case 4:
cmd = rebuf;
xor ^= rebuf;
step = 5;
break;
case 5:
cmd = rebuf;
data.CMD = (ECMD)BitConverter.ToUInt16(cmd, 0);
xor ^= rebuf;
step = 6;
break;
case 6:
length = rebuf;
data.Data.Clear();
xor ^= rebuf;
if (length > 0)
step = 7;
else
step = 8;
break;
case 7:
data.Data.Add(rebuf);
xor ^= rebuf;
if (data.Data.Count >= length)
{
step = 8;
}
break;
case 8:
if (xor == rebuf)
{
mControl.SendData(this,data);
}
step = 0;
break;
}
}
}
else
{
Thread.Sleep(1);
}
}
}));
mReadThread.IsBackground = true;
mReadThread.Start();
}
protected void CommadBase(byte unit, byte cmd, ReData reData, params byte[] datas)//同步的方式,发送优先级高,不需要使用事件
{
for (int i = 0; i < mRetry; i++)
{
mutex.WaitOne();
//lock (mKEY)
//{
this.SendData(unit, cmd, datas);
//Thread.Sleep(mDelay);
this.ReadData(unit, cmd, reData, mDelay);
mutex.ReleaseMutex();
//}
if (reData.ACKType == reData.ACKType_Normal)
{
return;
}
}
}
C#事件的机制是短时间里无法连续触发两次,实时性要求高的就会伤不起 初音之恋 发表于 2021-9-18 08:31
public void MonitorRun() //异步的方式,接受优先级高
{
mReadThread = new Thread ...
protected void ReadData(byte unit, byte cmd, ReData data, int delay)
{
int length;
byte[] rebuf = new byte;
byte step = 0;
byte xor = 0;
int i = 0;
DateTime tick = DateTime.Now;
while (DateTime.Now.Subtract(tick).TotalMilliseconds <= delay)
{
length = this.BytesToRead;
if (length > 0)
{
this.Read(rebuf, 0, length);
for (i = 0; i < length; i++)
{
switch (step)
{
case 0://ReHead1
if (rebuf == mReHead1)
{
step = 1;
xor = rebuf;
}
break;
case 1://ReHead2
if (rebuf == mReHead1)
{
}
else if (rebuf == mReHead2)
{
step = 2;
xor ^= rebuf;
}
else
{
step = 0;
}
break;
case 2://Unit
if (rebuf == unit)
{
data.Unit = rebuf;
xor ^= rebuf;
step = 3;
}
else
{
step = 0;
}
break;
case 3://cmd
data.CMD = cmd;
xor ^= rebuf;
step = 4;
break;
case 4://warning
data.ACKType = rebuf;
xor ^= rebuf;
step = 5;
break;
case 5://length
data.Length = rebuf;
data.ReDatas.Clear();
xor ^= rebuf;
if (rebuf == 0)
{
step = 7;
}
else
{
step = 6;
}
break;
case 6://data
data.ReDatas.Add(rebuf);
xor ^= rebuf;
if (data.ReDatas.Count >= data.Length)
{
step = 7;
}
break;
case 7://xor
if (xor == rebuf)
{
if (data.CMD != cmd)
{
data.ACKType = data.ACKType_NoMate;
}
return;
}
step = 0;
break;
}
}
}
else
{
Thread.Sleep(1);
}
}
data.Init();
return;
} C# 在波特率很高、数据量密集的情况下,是多开一个线程靠谱 初音之恋 发表于 2021-9-18 08:23
速度快会丢事件,玩过你就知道了
我玩得多了,我们上位机串口都是 用事件啊, 你这是自己不会玩。 DataReceive 事件读取数据存到队列 另开线程 处理就行了。 微软要这点东西做不好,还混个啥。 你这代码的思想还停留在前后台,读了处理,处理了又才读,你这个不掉数据才怪。 浮华一生 发表于 2021-9-19 10:37
我玩得多了,我们上位机串口都是 用事件啊, 你这是自己不会玩。 DataReceive 事件读取数据存到队列 另开 ...
听你这么一说我想起来几年前研究过一次winform,现在都忘了。
大概是115200波特率,500hz的帧率,每帧十几个字节吧,解析数据并在vs自带的chart上描曲线,横轴最多显示1000个点,不卡顿,且不丢数据。 天下乌鸦一般黑 发表于 2021-9-19 11:11
听你这么一说我想起来几年前研究过一次winform,现在都忘了。
大概是115200波特率,500hz的帧率,每帧十 ...
串口数据量其实并不大,甚至可以说很小。 一般处理不过来都是程序问题。 就类似单片机里面,中断接收了数据,如果就在中断里面处理数据,那大概率是要丢数据的。所以一般都是忌讳在中断里面直接处理数据。c# 的数据接收事件就类似单片机的数据中断,肯定不能在里面读了就处理啊。 浮华一生 发表于 2021-9-19 11:37
串口数据量其实并不大,甚至可以说很小。 一般处理不过来都是程序问题。 就类似单片机里面,中断接收了数 ...
是的。图像显示,数据处理,数据接收,这仨要分开处理,要是有阻塞就完了。。。 浮华一生 发表于 2021-9-19 11:37
串口数据量其实并不大,甚至可以说很小。 一般处理不过来都是程序问题。 就类似单片机里面,中断接收了数 ...
C#事件比单片机中断不要慢的太多,说实话数据采集这种对实时性没要求的能有什么问题,和波特率一点关系都没有,关键是接受之后的通讯指令的高速响应,需要在有限时间内收到后立刻相应下位机,数据是变长的,来回交互你还靠事件sleep 100毫秒这种来吗,接受到的数据也不一定完成的,可能粘包或者分包 初音之恋 发表于 2021-9-19 20:57
C#事件比单片机中断不要慢的太多,说实话数据采集这种对实时性没要求的能有什么问题,和波特率一点关系都 ...
考虑发送不管是不是开一个线程的问题,485这种半双工天生发送和接受就在同一个线上就不存在这个问题,全双工的肯定发送和接受分开处理 浮华一生 发表于 2021-9-19 11:37
串口数据量其实并不大,甚至可以说很小。 一般处理不过来都是程序问题。 就类似单片机里面,中断接收了数 ...
反正我这程序可以应对几十个模拟终端高速响应,丢不丢帧不是我考虑的问题 初音之恋 发表于 2021-9-19 20:57
C#事件比单片机中断不要慢的太多,说实话数据采集这种对实时性没要求的能有什么问题,和波特率一点关系都 ...
呵呵, 接受不了建议就算了。 你只是把C#当C来写而已。 “来回交互你还靠事件sleep 100毫秒这种来吗” 这句话 我就感觉你连单片机的操作系统用得可能都不多。 你一直以前后台的思维在写程序。 你都用操作系统了,为什么要 sleep 呢? 信号量是不能用还是咋的,烫手么。你要说信号量,队列这些的操作影响了你的“实时响应”,我只能说,嗯,你是对的。 如果连这种操作都影响到你的实时性了,只能说选windows不是明智之举, 因为windows不是实时系统,最高优先级的线程也会被调度。 浮华一生 发表于 2021-9-19 23:35
呵呵, 接受不了建议就算了。 你只是把C#当C来写而已。 “来回交互你还靠事件sleep 100毫秒这种来吗” 这 ...
学习一下呗,这个串口接受怎么个信号量法,提升一下也好的,信号量我只知道用在多线程同步 初音之恋 发表于 2021-9-20 07:42
学习一下呗,这个串口接受怎么个信号量法,提升一下也好的,信号量我只知道用在多线程同步 ...
说不来, 咱们不在一个频道上 {:titter:} .既然你的方法可以满足,那就用它就行。 使用: 事件+队列, 接收数据处理线程
要是能发个例程给我等低手参考该多好啊
页:
[1]