搜索
bottom↓
回复: 26

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

[复制链接]

出20入4汤圆

发表于 2021-9-17 15:21:33 | 显示全部楼层 |阅读模式
搞个上位机,C#这个串口控件接收事件有时会将一包数据分成1次、2次等接收,不会每次都能完整接收完。

出0入25汤圆

发表于 2021-9-17 15:34:18 | 显示全部楼层
本帖最后由 墨非 于 2021-9-17 15:42 编辑

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

出0入133汤圆

发表于 2021-9-17 15:54:37 来自手机 | 显示全部楼层
配好超时,可以100ms一次,缓冲区大点,一次读上百字节,这样短包超时退出,长包收满退出。

出0入4汤圆

发表于 2021-9-17 16:10:36 | 显示全部楼层
怎么判定接收完成?数据有结束符吗?

出0入16汤圆

发表于 2021-9-17 16:27:53 | 显示全部楼层
C#的事件不靠谱,我都是自己开个线程专门负责接受,事件速度太慢,通信协议定好后用状态机拼接,死循环判断有没有收到数据,有数据进状态机,没数据就sleep(1)

出20入4汤圆

 楼主| 发表于 2021-9-17 18:08:45 | 显示全部楼层
墨非 发表于 2021-9-17 15:34
简单粗暴一点,读完一包数据,小延时3~5ms 继续读,还有数据跟前一包拼一起算一帧。 ...

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

出20入4汤圆

 楼主| 发表于 2021-9-17 18:09:01 | 显示全部楼层
wye11083 发表于 2021-9-17 15:54
配好超时,可以100ms一次,缓冲区大点,一次读上百字节,这样短包超时退出,长包收满退出。 ...

100ms会不会有点久

出20入4汤圆

 楼主| 发表于 2021-9-17 18:09:36 | 显示全部楼层
thepresent 发表于 2021-9-17 16:10
怎么判定接收完成?数据有结束符吗?

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

出20入4汤圆

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

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

出0入0汤圆

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

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

出870入263汤圆

发表于 2021-9-18 07:30:44 | 显示全部楼层
直接调用Win32 API的SetCommTimeouts函数,比单片机都灵活。

出0入16汤圆

发表于 2021-9-18 08:23:26 | 显示全部楼层
浮华一生 发表于 2021-9-17 19:59
你是不会玩吧, 串口那点速度,不至于。

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

出0入16汤圆

发表于 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[200];
                  int step = 0;
                  MonitorData data = new MonitorData();
                  mControl.Run();
                  Thread.Sleep(1000);
                  byte xor = 0;
                  byte [] cmd = new byte[2];
                  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[0] = rebuf;
                                      xor ^= rebuf;
                                      step = 5;
                                      break;
                                  case 5:
                                      cmd[1] = 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#事件的机制是短时间里无法连续触发两次,实时性要求高的就会伤不起

出0入16汤圆

发表于 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[200];
            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;
        }

出0入0汤圆

发表于 2021-9-18 09:08:21 | 显示全部楼层
C# 在波特率很高、数据量密集的情况下,是多开一个线程靠谱

出0入0汤圆

发表于 2021-9-19 10:37:53 | 显示全部楼层
初音之恋 发表于 2021-9-18 08:23
速度快会丢事件,玩过你就知道了

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

出20入44汤圆

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

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

出0入0汤圆

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

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

出20入44汤圆

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

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

出0入16汤圆

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

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

出0入16汤圆

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

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

出0入16汤圆

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

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

出0入0汤圆

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

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

出0入16汤圆

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

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

出0入0汤圆

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

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

出0入17汤圆

发表于 2021-10-6 21:37:39 | 显示全部楼层
使用: 事件+队列, 接收数据处理线程

出0入0汤圆

发表于 2021-11-3 21:25:49 | 显示全部楼层
要是能发个例程给我等低手参考该多好啊
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子论坛 ( 公安交互式论坛备案:44190002001997 粤ICP备09047143号 )

GMT+8, 2022-6-26 01:13

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表