jufr12315 发表于 2021-9-17 15:21:33

请教下,WinForm C#串口如何接收不定长的数据?

搞个上位机,C#这个串口控件接收事件有时会将一包数据分成1次、2次等接收,不会每次都能完整接收完。

墨非 发表于 2021-9-17 15:34:18

本帖最后由 墨非 于 2021-9-17 15:42 编辑

简单粗暴一点,读完一包数据,小延时3~5ms 继续读,还有数据跟前一包拼一起算一帧。

wye11083 发表于 2021-9-17 15:54:37

配好超时,可以100ms一次,缓冲区大点,一次读上百字节,这样短包超时退出,长包收满退出。

thepresent 发表于 2021-9-17 16:10:36

怎么判定接收完成?数据有结束符吗?

初音之恋 发表于 2021-9-17 16:27:53

C#的事件不靠谱,我都是自己开个线程专门负责接受,事件速度太慢,通信协议定好后用状态机拼接,死循环判断有没有收到数据,有数据进状态机,没数据就sleep(1)

jufr12315 发表于 2021-9-17 18:08:45

墨非 发表于 2021-9-17 15:34
简单粗暴一点,读完一包数据,小延时3~5ms 继续读,还有数据跟前一包拼一起算一帧。 ...

3~5ms估计不够,现在做的跟单片机差不多,接收做个超时判断,认为接收一包完成。数据时不定长的。

jufr12315 发表于 2021-9-17 18:09:01

wye11083 发表于 2021-9-17 15:54
配好超时,可以100ms一次,缓冲区大点,一次读上百字节,这样短包超时退出,长包收满退出。 ...

{:lol:} 100ms会不会有点久

jufr12315 发表于 2021-9-17 18:09:36

thepresent 发表于 2021-9-17 16:10
怎么判定接收完成?数据有结束符吗?

没有,不定长,没有协议,目前通过超时判断一包数据。

jufr12315 发表于 2021-9-17 18:10:06

初音之恋 发表于 2021-9-17 16:27
C#的事件不靠谱,我都是自己开个线程专门负责接受,事件速度太慢,通信协议定好后用状态机拼接,死循环判断 ...

线程周期读取判断是否有数据吗,周期时间定了多少?

浮华一生 发表于 2021-9-17 19:59:22

初音之恋 发表于 2021-9-17 16:27
C#的事件不靠谱,我都是自己开个线程专门负责接受,事件速度太慢,通信协议定好后用状态机拼接,死循环判断 ...

你是不会玩吧, 串口那点速度,不至于。

armstrong 发表于 2021-9-18 07:30:44

直接调用Win32 API的SetCommTimeouts函数,比单片机都灵活。

初音之恋 发表于 2021-9-18 08:23:26

浮华一生 发表于 2021-9-17 19:59
你是不会玩吧, 串口那点速度,不至于。

速度快会丢事件,玩过你就知道了

初音之恋 发表于 2021-9-18 08:31:38

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:37:40

初音之恋 发表于 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;
      }

lonely9391 发表于 2021-9-18 09:08:21

C# 在波特率很高、数据量密集的情况下,是多开一个线程靠谱

浮华一生 发表于 2021-9-19 10:37:53

初音之恋 发表于 2021-9-18 08:23
速度快会丢事件,玩过你就知道了

我玩得多了,我们上位机串口都是 用事件啊, 你这是自己不会玩。 DataReceive 事件读取数据存到队列 另开线程 处理就行了。 微软要这点东西做不好,还混个啥。 你这代码的思想还停留在前后台,读了处理,处理了又才读,你这个不掉数据才怪。

天下乌鸦一般黑 发表于 2021-9-19 11:11:55

浮华一生 发表于 2021-9-19 10:37
我玩得多了,我们上位机串口都是 用事件啊, 你这是自己不会玩。 DataReceive 事件读取数据存到队列 另开 ...

听你这么一说我想起来几年前研究过一次winform,现在都忘了。
大概是115200波特率,500hz的帧率,每帧十几个字节吧,解析数据并在vs自带的chart上描曲线,横轴最多显示1000个点,不卡顿,且不丢数据。

浮华一生 发表于 2021-9-19 11:37:31

天下乌鸦一般黑 发表于 2021-9-19 11:11
听你这么一说我想起来几年前研究过一次winform,现在都忘了。
大概是115200波特率,500hz的帧率,每帧十 ...

串口数据量其实并不大,甚至可以说很小。 一般处理不过来都是程序问题。 就类似单片机里面,中断接收了数据,如果就在中断里面处理数据,那大概率是要丢数据的。所以一般都是忌讳在中断里面直接处理数据。c# 的数据接收事件就类似单片机的数据中断,肯定不能在里面读了就处理啊。

天下乌鸦一般黑 发表于 2021-9-19 11:45:08

浮华一生 发表于 2021-9-19 11:37
串口数据量其实并不大,甚至可以说很小。 一般处理不过来都是程序问题。 就类似单片机里面,中断接收了数 ...

是的。图像显示,数据处理,数据接收,这仨要分开处理,要是有阻塞就完了。。。

初音之恋 发表于 2021-9-19 20:57:40

浮华一生 发表于 2021-9-19 11:37
串口数据量其实并不大,甚至可以说很小。 一般处理不过来都是程序问题。 就类似单片机里面,中断接收了数 ...

C#事件比单片机中断不要慢的太多,说实话数据采集这种对实时性没要求的能有什么问题,和波特率一点关系都没有,关键是接受之后的通讯指令的高速响应,需要在有限时间内收到后立刻相应下位机,数据是变长的,来回交互你还靠事件sleep 100毫秒这种来吗,接受到的数据也不一定完成的,可能粘包或者分包

初音之恋 发表于 2021-9-19 21:17:23

初音之恋 发表于 2021-9-19 20:57
C#事件比单片机中断不要慢的太多,说实话数据采集这种对实时性没要求的能有什么问题,和波特率一点关系都 ...

考虑发送不管是不是开一个线程的问题,485这种半双工天生发送和接受就在同一个线上就不存在这个问题,全双工的肯定发送和接受分开处理

初音之恋 发表于 2021-9-19 21:21:40

浮华一生 发表于 2021-9-19 11:37
串口数据量其实并不大,甚至可以说很小。 一般处理不过来都是程序问题。 就类似单片机里面,中断接收了数 ...

反正我这程序可以应对几十个模拟终端高速响应,丢不丢帧不是我考虑的问题

浮华一生 发表于 2021-9-19 23:35:09

初音之恋 发表于 2021-9-19 20:57
C#事件比单片机中断不要慢的太多,说实话数据采集这种对实时性没要求的能有什么问题,和波特率一点关系都 ...

呵呵, 接受不了建议就算了。 你只是把C#当C来写而已。 “来回交互你还靠事件sleep 100毫秒这种来吗” 这句话 我就感觉你连单片机的操作系统用得可能都不多。 你一直以前后台的思维在写程序。 你都用操作系统了,为什么要 sleep 呢? 信号量是不能用还是咋的,烫手么。你要说信号量,队列这些的操作影响了你的“实时响应”,我只能说,嗯,你是对的。   如果连这种操作都影响到你的实时性了,只能说选windows不是明智之举, 因为windows不是实时系统,最高优先级的线程也会被调度。

初音之恋 发表于 2021-9-20 07:42:18

浮华一生 发表于 2021-9-19 23:35
呵呵, 接受不了建议就算了。 你只是把C#当C来写而已。 “来回交互你还靠事件sleep 100毫秒这种来吗” 这 ...

学习一下呗,这个串口接受怎么个信号量法,提升一下也好的,信号量我只知道用在多线程同步

浮华一生 发表于 2021-9-20 08:16:11

初音之恋 发表于 2021-9-20 07:42
学习一下呗,这个串口接受怎么个信号量法,提升一下也好的,信号量我只知道用在多线程同步 ...

说不来, 咱们不在一个频道上 {:titter:} .既然你的方法可以满足,那就用它就行。

foxpro2005 发表于 2021-10-6 21:37:39

使用: 事件+队列, 接收数据处理线程

wiisir 发表于 2021-11-3 21:25:49

要是能发个例程给我等低手参考该多好啊
页: [1]
查看完整版本: 请教下,WinForm C#串口如何接收不定长的数据?