如何在c#中实现串口接收一个字节就触发事件处理函数一次?

凉晓风 2017-06-30 11:26:03
因工作需要编写一个上位机,监视modbus主机和从机在485通信线上发送的数据,但是现在出现的问题是不能利用超时检测来断开主机发送的帧数据和从机回复的帧数据,经过调试发现,serialPort类就算设置了receivedByteThreshold为1,事件处理函数也并不一定是接收一次字节触发一次,当接收到不定长度的字节也触发一次。
需要请教的问题是:
1.serialPort类能否实现如单片机串口中断般接收一个byte就触发一次datareceive事件处理函数?
2.如果c#不能实现一个字节触发一次,那还有其他办法吗?
3.c++语言可以实现吗?
谢谢
...全文
1441 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
litemanc 2018-11-28
  • 打赏
  • 举报
回复
我也是做下位机的,并且也做上位机,真正理解楼主的需求, 你在C#里,dateReceive事件的触发字节设置为1,然后开一个timer, timer的时间根据当前下位机选择的波特率按照modbus的2.5字符间隔设置, 每次触发dateReceive事件,都CLR timer, 如果timer超时事件触发并且当前是在接收状态,则为一帧数据接收完成。我就是这么做的。
  • 打赏
  • 举报
回复
另外,你可以使用一个全局的变量来记录最后收到消息的时间,并且保证当10秒钟之内没有发送过消息时才发送一个心跳探测消息,每当20秒内未收到任何消息时就发生离线事件。实际上这可以是一个统一地、粗粒度的机制,无需跟收到的字节的数量捆绑在一起。
  • 打赏
  • 举报
回复
在 windows 下,发送一个命令之后
var flag = false;
注册接收(d=> 
{
    flag = true;
    .........
}
send(data);
await Task.Delay(10000);
if(flag)
    throw new 超时Exception(....);
它的定时检测往往要到10秒钟或者更大范围。 假设你需要工控开发,为什么要用 windows 呢?
  • 打赏
  • 举报
回复
你把操作系统去掉(特别是,windows这种是桌面图形操作系统,而不是低级的简单控制台操作系统),在裸机上自己跑自己的程序,那么时候才能脱离开操作系统基本概念。否则你就要结合着不同操作系统来设计,不可能过分降低标准。 例如在 windows 桌面系统上,通讯的超时概念是10秒钟这个巨大,而不是什么几十毫秒。如果有人跟你说几十毫秒,那么请它不要非要赖着用 windows,应该选择该选择的操作系统。
凉晓风 2017-07-01
  • 打赏
  • 举报
回复
这些成品dll或者控件只能实现单主机和单从机的通信,这些功能我已经完成了,现在最后一个功能也就是我提出问题的原因是需要一种能够监控主机和从机的功能,这些成品库都实现不了,
wanghui0380 2017-06-30
  • 打赏
  • 举报
回复
比如下面这个 NModbus.Serial 我看了一下,这个库提供了串口modbus适配,然后我顺带喵了一眼他串口适配部分的代码,从代码上看应该都是有各种超时控制的
wanghui0380 2017-06-30
  • 打赏
  • 举报
回复
如果你是刚转过来,那还真不如像楼上说的直接用成品库(只要对方是严格按modbus协议的,成品库还是可以用的) 下面是一些成品库 https://www.nuget.org/packages?q=modbus
凉晓风 2017-06-30
  • 打赏
  • 举报
回复
谢谢diaodiaop,我已经将datareceive封装实现modbus模拟主机读写从机操作,现在要完成的功能是作为第三方监听另外的主机与从机通信,包括正常数据和错误数据,而线程的DLL只是模拟主机或者是从机,不能实现监听,
凉晓风 2017-06-30
  • 打赏
  • 举报
回复
by_封爱 版主 2017-06-30
  • 打赏
  • 举报
回复
既然是标准的mbs 就别用datareceive了..因为这个你还要自己组装 比较费劲, 有第三方的DLL 方法直接就是根据寄存器地址读值 非常方便..别自己写 太累
凉晓风 2017-06-30
  • 打赏
  • 举报
回复
感谢两位,xuggzu,我的问题就是现在主机与从机的通信是其他公司预定好的,没有固定的头部和尾部,只知道每个字节的作用和大概构成,如果利用解析数据包的方式来实现那就必须将所有的通信全部接收在进行处理,但是如何确定这一包数据的最后一个字节是那一帧的最后一个字节还是一个问题。 wanghui0380,我是搞单片机c语言的,c#也是刚学几天,rx.net这么高深的东西暂时还看不懂。我不是要发送数据,我是要接收,不发送,上图中具有tx和rx是图2,具有“串口接受“四个字的是图1,不知道怎么图搞反了。我明白modbus检测是通过字节和波特率计算的出来的,现在我的波特率是9600,十一位为一个完整的字节,间隔通过计算大约为四毫秒。 如果我利用串口调试助手模拟从机和主机同时向我的软件发信息就可以实现断帧,但是接入物理主从机通信就废了
wanghui0380 2017-06-30
  • 打赏
  • 举报
回复
百度rx.net 基本没啥资料,不过你百度 "c# Reactive Extension "倒有一些零散的资料
wanghui0380 2017-06-30
  • 打赏
  • 举报
回复
这个也没啥问题 先把数据缓存,然后下一个空task,让他运行指定时间cancel掉,cancel的同时把缓存的数据发送出去 其实,这个东西微软现在也有,只是用的人比较少,那个叫rx.net,我这里提rx.net基本没几个人能理解,我就不提了。还是提一提大部分人能接收的timer,task把 modbus其实不是你认为的空几个字节,串口传递速率是根据波特率设定来得(波特率含义是每秒传输的二进制位的个数),所以说他说空几个字节,等于是说根据波特率空闲多少毫秒,所以无论是Canceltokensource,timer其实可以根据波特率算出一个触发时间 ps:有关rx.net如果你有兴趣可以百度,在rx里我们跟方便的处理方式,下面是rx.js的说明 http://cw.hubwiz.com/card/c/569d92e3acf9a45a69b05154/1/3/3/ 至于rx.net其实跟js的是类似的东西(其实也有rx.java),只是net程序员好像比js,JavaScript跟难接受这种理念,所以net的中文资料基本没啥像样的资料
xuggzu 2017-06-30
  • 打赏
  • 举报
回复
modbus归modbus,数据归数据。数据哪个是头哪个是尾都是自己定义的,如果你不知道头尾,不管你是从机还是监控机都无法解析数据。所以楼主的问题是数据包解析问题,而不是要一次接收多少数据问题。
凉晓风 2017-06-30
  • 打赏
  • 举报
回复
谢谢两位的回复,串口上采用的协议是Modbus,他是通过两个字节的间隔时间来判断一帧数据的结束和下帧数据的开始的,因此判断一帧数据唯一的解决方法 就是利用接收一个字节,开启定时器计时,等到第二个字节到来时判断这段时间的长度来确定是否为一帧结束。
现在我要编写的软件作为第三方的监视方,并不知道主机和从机之间发送的数据是什么,而且没有固定的数据头和数据尾,所以xuggzu提出通过数据头来解决我不能实现。至于wanghui0380给出的回复,可能是因为我没有说清楚,现在协议解析我能够完成,问题是在解析前我必须区分出主机和从机各自的数据帧,现在的情况是,主机和从机帧连接在了一起,这样就没法解析。
请问两位还有什么更好的方法来解决判断两字节的间隔时间断帧的这个问题吗?

图1是正常的主机对从机的发送接收,
图2是我软件监视的主机和从机通信的数据,
wanghui0380 2017-06-30
  • 打赏
  • 举报
回复
额,我表示你还不会写串口,串口又不懂协议,他只管接收byte数组。协议解析是你自己事情,如果你们的协议就是一字节,那好自己写协议处理类 我自己的处理类.post(byte[] data) 我自己的处理类 { public void Post(byte[] input) { foreach (var item in input) { 我就一个字节一个字节来,有问题么,如果你想玩什么事件,好吧,自己定义一个事件有问题么 } } }
xuggzu 2017-06-30
  • 打赏
  • 举报
回复
1. 串口类底层是基于消息的,至于是不是从中断模式再到消息不清楚,但你也不需要关心这些。
2. 串口可以接收一个就发生一次事件,threshold=1(缺省就是1),但前提是通讯及时,不能阻塞,发送也是一次一个。这种情况很难保证,就算你在局域网用以太网通讯也不敢说想怎么样就怎么样。所以,一般的程序设计是有数据包头的,以区分数据包和处理粘包问题等。不能把期望全部寄在硬件上。
3. 这和语言无关,原因同2。

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧