串口处理c#

https://www.cnblogs.com/candylakers/archive/2011/12/30/2306930.html
一、属性设置-(不过我还没研究好怎么在程序中动态设置)
串行通讯起始位、数据、奇偶和停止位透解https://wenku.baidu.com/view/6e3f5f3b83c4bb4cf7ecd19f.html

comm1.CommName:=‘COM1’; //串口号
comm1.BaudRate:=1200; //波特率 1200 4800 9600 115200当然也可以有范围比方1201 1205
comm1.ByteSize:=_8; //数据位 5 6 7 8
comm1.Parity:=Even; //检验位 Even Odd None Space Mark
comm1.StopBits:=_1; //停止位 1 1.5 2
comm1.OnReceiveData:=Comm1ReceiveData; //这个是你以后写事件驱动时的名称,你可以改成你的名字
串口通讯会产sh
生多条数据连续接收或接收不完整的情况。可以用一个全局变量来累加接收结果,再判断这个变量是否包含完整数据并做相应处理。
接收就是在SerialPort类的rececivedata里面接收
设置每接受到1个字节时,触发oncomm事件。
以前搞BCB的时候,用的是 API写的,后来有一天发现,其实这个是网上经典的一个串口的 简单裁剪版本!
现在用delphi, 还搞串口,用的SPComm控件,很好用,也没有出过问题!
oncomm事件中写:
1、把接到到的数据转换为字符用个全局变量字符串存起来(如0A 0B 1C 这样子存起来)。
2、通过协议的结束标志字符判断此帧有没有结束,没结束就退出。结束了,就置位一个接受完成标志位。

发送数据函数:
1、发送数据.
2、是全局变量接受字符串=‘’ 、接受完成标志位=false.
3、延时3s(如果接受完成标志位置位就退出延时) 注:此处的延时相当于超时时间。//这里很关键
4、判断全局变量接受字符串等,然后处理接收到的数据。
MSComm.RThreshold :=self.OutLen;// RecvCount[add]; //设置接收多少字节开产生oncomm事件
MSComm.InBufferCount:=0;//清空数据
mscomm.output:=senddata;

1、每一次发送之前都有清空,并设置为接收到多少人字节时,为触发。
2、是PC和DSP的通讯
3、发送之后,都有设置延时时间。延时的时间足够发送并回收
4、缓冲区:返回的字节数,不会超过50个字节。所以缓冲区应该是足够的。

string data=""
int count=serialport.ByteToRead;
byte[] byteBar=new byte[count];
serialport.Read(byteBar,0,count)
for(int i=0;i<byteBar.length;i++)
{
data+=()byteBar[i];
}
我是接收多条数据包,每条数据包是以~开头\r结尾的,可是有时侯得到的
第一个data="~34532355";
第二个data=“345\r~23425235634\r”
有时候得到的是第一个data="~3";
第二个data=“4532355345\r~23425235634\r”

我想要得到的是
第一个data="~345323554532355345\r";
第二个data="~23425235634\r";
先把读到的数据放入stringbuilder,然后再从stringbuilder里读出来,这样可能更安全

以上接收端会出现类似于两段接收的情况但是中间间隔极短,所以最简单的是在一接收到数据之后进行读秒倒计时譬如在1.5s内也进行数据接收和数据拼接,1.5秒后在对数据进行拼接判断是不是符合要求的数据。

请不要说循环Read请给出代码,谢谢。用ReadLine(),因为下位机发送完数据之后没有写\r\n所以就不能用。
现在的情况是,接收到数据是不固定的长度,协议是起始是AA AA 结束是DE D0
接收到的数据有可能是AA AA 08 56 82 44 DE DO
也可能是AA AA 08 56 82 44 AA AA 55 63 1A 55 AA AA 55 99 33 77 DE D0要做的是将接到的数据处理解析。
1、串口接收的数据不是一次性接完的。比如第一次buf收到了AA AA 08 56 第二次buf才收到82 44 DE DO
怎么把他们装到一个新数组里面。
我用的ArragList LIST=new ArragList();为什么不行啊,因为我判断的是if(LIST[2]==0x69)调试提示object 和int不能。
2、解析时我想处理的AA AA 08 56 82 44 和AA AA 55 63 1A 55和 AA AA 66 99 33 77 DE D0分开处理。这个怎么做啊。
例如 if(buf[2]==0x08) {执行相应的事情}
if(buf[2]==0x55) {执行相应的事情}
求大神给个指导

解决方案

List <byte> list =new List<byte>() //必须定义为全局
int b=0
private void comm_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
          int a=comm.ByteToRead;
          byte []buf=new byte[a];
          comm.Read(buf,0,a);   
          for(i=0;i<a;i++)//用来将buf的数据拼接到list数组,以保证把完整的一帧数据加到一个数组里面
           {
                   list.Insert(b,buf[i]);
                   b++;
               }  
           if(list[list.account-1]==0xD0&&list[list.account-2]==0xDE)//判断数据是否收完;DE和D0为帧结束符在下面处理数据
           {
              对于第2个问题,利用LIST.Removerange(int ,length)//在指定索引处删除一定的长度
             if(list[2]==0x08{
              做相应处理;
               list.removerange(0,6);

                }
               if(list[2]==0x55{
              做相应处理;
               list.removerange(0,6);

                }
               if(list[2]==0x66{
              做相应处理;
      

                }
            list.Clear();//清空list用来下次接收
            b=0;
           }
           


}

接收到的信息(list中的内容),不一定只有一个命令(例如发消息的设备很多、发送速度远比上位机处理速度快),可能有多2个甚至多个。你的程序是假设每一次接收到“一个”命令,只执行一次命令就结束。

你的程序并不能保证正确,只是说你现在的测试环境“恰好”设备比较慢、而双绞线距离比较短、主机比较空闲,因此基本上都能恰好每当设备发来一条消息时能够满足 list[list.account-1]==0xD0&&list[list.account-2]==0xDE 这个条件。如果你能兼容“收到多条命令”的形式(甚至你能发挥想象力先来制造一个此类测试,然后再解决bug),程序将来用到实际的各种生产环境中才会比较可靠。

在你的程序中,既然执行 list.Clear(),那么前边其实就不需要执行 list.removerange(0,6)。 但是list.Cear()其实是不对的,因此最终还是应该保留 list.removerange 而删掉 list.Celar。

另外将来让程序变成一个教正规、可为中型应用而扩展的“设计升级”的建议:

  1. DataReceive中的程序,应该清晰地是处理这个Receive流程的。你应该把判断任务类型和调用处理程序的代码,从这段程序中分离出去,不应该在这里出现一堆的 if…else 判断。
  2. 将来还是要在子线程中执行命令解析和调用处理程序过程,而 Receive程序应该仅仅在从list中去掉一条条命令之后,立刻返回(不等命令执行就返回)。以免造成通讯延迟、甚至程序死锁。
    最近总结了串口(COM)读写操作的三种方式:

第1种方式是采用微软在.NET2.0推出了一个串口控件,SerialPort类,但必须是.NET2.0才可以
第2种方式是用API写串口通信,虽然难度高,但可以方便实现自己想要的各种功能
第3种方式是通过采用Visual Studio 6.0中原来的MSComm控件这是最简单的,最方便的方法,但需要注册

以下详细分析了每种方式的使用方式,并提供相应例题下载:

A.第1种方式:

.NET 2.0提供了对串口通信功能的支持,在命名空间System.IO.Ports下找到SerialPort类,通过创建一个新的SerialPort对象,我们就可以在.NET程序中控制串口通讯的全过程。

(1).属性介绍
进行串口通讯时,需要设置一些相关参数,可以通过设置SerialPort类的属性来进行。
SerialPort属性主要包括:
.PortName 串口名称,COM1, COM2等。
.BaudRate 波特率,也就是串口通讯的速度,进行串口通讯的双方其波特率需要相同,如果用PC连接其他非PC系统,一般地,波特率由非PC系统决定。
.Parity 奇偶校验。可以选取枚举Parity中的值
.DataBits 数据位
.StopBits 停止位,枚举StopBits中的值
.Handshake 握手方式,也就是数据流控制方式,枚举Handshake中的值

(2).打开与关闭串口
在创建一个SerialPort对象,设置串口属性后,可以通过Open()方法打开串口。数据读写完成后,可以通过Close()方法关闭串口。
根据经验,对于有些系统,在打开串口后,还需要将RtsEnable设置为True,这样才能读写数据,否则不能正常读写数据。

(3).读写行数据
ReadLine()方法是阻塞的,直至遇到一个换行符后返回。在读取数据时,如果一直没有遇到换行符,那么在等待ReadTimeout时间后,抛出一个TimeoutException。默认情况下,ReadTimeout为InfiniteTimeout。这样,ReadLine一直处于阻塞状态,直至有新一行数据到达。
WriteLine()方法也是阻塞的,如果另一方不能及时接收数据,就会引起TimeoutException异常。
由于ReadLine()和WriteLine()方法都是阻塞式的,在程序使用SerialPort 进行串口通讯时,一般应该把读写操作交由其他线程处理,避免因为阻塞而导致程序不响应。

(4).读写字节或字符数据
对于字节或字符数据,用Read()方法来读数据,该方法需要一个字节或字符数组作为参数来保存读取的数据,结果返回实际读取的字节或字符数。
写数据使用Write()方法,该方法可以将字节数组、字符数据或字符串发送给另一方。

(5).事件
SerialPort提供了DataReceived事件。当有数据进入时,该事件被触发。
该事件的触发由操作系统决定,当有数据到达时,该事件在辅助线程中被触发。辅助线程的优先级比较低,因此并不能确保每个字节的数据到达时,该事件都被触发。

串口通信之超时
  超时分串口读超时和串口写超时,主要是读超时,即ReadTimeout与Read方法之间的超时。下面谈谈他们之间的意义和实现。

Read方法是阻塞的,它一直在读串口接收缓冲区中的数据,如果接收缓冲区有数据,Read方法则返回一个或多个字节数据;如果Read方法在等待ReadTimeout毫秒时间内,串口接收区一直没有任何数据,则Read方法将甩ExceptionTimeout异常。注意,Read(outBuf, offset, count)阻塞读取的不是非等到count个字节数据,而是当前接收缓冲区大于等于1小于等于count个字节数据,即只要有数据Read方法就立刻返回。

由于Read方法的阻塞性,所以我们必须防止(如串口物理断开) Read永远不返回,而导致程序卡死。方法有:

1. 设置ReadTimeout属性为合理值,其默认值为-1,即Read永不可能因为ReadTimeout而超时返回。

2. 先判断serialPort.BytesToRead大于0,再调用Read方法,则Read肯定会返回。

代码段一:

      int nStartTime = Environment.TickCount;

      while(true)

      {

           int nNowTime = Environment.TickCount;

    if(nNowTime – nStartTime > 360)   //等待360ms

    {

          Console.WriteLine(“等待360ms后超时”);

      break;

    }

    if(serialPort.BytesToRead > 35)      //用户业务数据包长度

    {

          int nLen = serialPort.Read(outBuf, 0, 35);

          DealData(outBuf, nLen);     //验证合法包,然后处理业务

      break;

    }

    //时间消耗在循环过程中,可在这加一行 Thread.Sleep(20);

  }
代码段二:

       serialPort.ReadTimeout = 1000;     //等待1000ms  初始化

     

      //接收处理函数

      try

  {

    int nLen = serialPort.Read(outBuf, 0, serialPort. BytesToRead);  //如果接收区一直没数据,时间消耗在这,等1000ms后甩TimeoutException异常

    if(nLen > 0)

        {

             DealData(outBuf, nLen);     //进全局数据队列,然后分析队列里的合法数据包

    }

  }

  catch (TimeoutException ex)

  {

    Console.WriteLine("通信超时");

  }
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值