串口应该采用什么方法接收报文?

jshzp 2020-10-27 09:31:21
使用了SerialPort类接收串口通讯的报文。报文格式是0xF3 xx xx xx。。。xx xx 0xFD。其中报头是F3,报尾是FD。xx是数据,报文长度是不固定的。相邻两个报文之间间隔约500毫秒。考虑了下面几种判断报文的方法,但是都不成功:
1、采用最常见的方法,设置属性port.ReceivedBytesThreshold ,收到设定数量的字节数据后触发接收事件port.DataReceived,因为报文长度不固定,所以无法使用这个方法。
2、使用读取方法port.ReadTo(Encoding.ASCII.GetString(new byte[1] { 0xFD })),当读到报尾时则停止。调试发现,始终读不到数据,估计原因是ascii值0xFD对应的不是一个可打印字符。
3、感觉最合理的方法应该是:当串口持续一段时间例如100ms,没有收到数据,则读取缓冲区所有数据,并判断报头报尾。但是这种方法怎样写代码呢?SerialPort.ReadTimeout似乎并不合适。难道是要开一个100mS的timer吗?
4、因为一个报文里的相邻两个字节数据之间,时间间隔几乎等于零,所以每接收到一个字节就去读取并判断一次,这样的方法时间上来不及。

应该采用哪种方法呢?望高手指教,感谢!
...全文
7168 20 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
MauriceHng 2021-12-07
  • 打赏
  • 举报
回复

楼下的大神么,我是小白,谁能给来一段用JS写的串口监听不分包显示的代码,你们讨论的东西都听不懂[狗头]....

wanghui0380 2020-10-31
  • 打赏
  • 举报
回复
那些不是技术文章,那些人是自媒体人 目的么,当然是造神,故意说的神乎其神,拉你入会。然后么,嘿嘿,你知道的 至于你入会了怎么样,不怎么样。 人家的口号就3句话 1。我不是技术人,是自媒体人 2。想学技术,跟我混,你看我的文章,神不神,当然不白嫖 3。技术不行,你傻啊,都跟你说了,我是自媒体人,找我学技术,还是交点智商税把
wanghui0380 2020-10-31
  • 打赏
  • 举报
回复
引用 17 楼 ziqi0716 的回复:
[quote=引用 12 楼 wanghui0380 的回复:]不想说啥,如果你只愿意看博客园的,只愿意采用博客园的我们说啥都没有用 我们只能说在博客园之外,全世界的其他程序员采用的通用方式是 Transfer 层-------------------------基于 System.IO.Pipelines的 IDuplexPipe方式
引用
//IDuplexPipe is defined in System.IO.Pipelines, but they provide no default implementation!!! So something like the below occurs all over the place: SignalR, etc. Also, Input and Output are awful names, but I agree they're hard to name. // BACK // // output input // // --------------- // // writer reader // // || /\ // // || || // // || || // // || || // // \/ || // // reader writer // // --------------- // // input output // // FRONT //
编解码层----------------------------编解码和用什么底层无关,tcp,udp,串口都是一样,因为Transfer层已经把底层的东西给你完全抛开了,你只是站在 FRONT那端去玩。 所以你们前面讨论啥new byte[1024] 可以作废,那啥我需要一个队列也可以作废。 当然编解码层其实另一个类似所谓的asp.net core的另一个管道pipeline(博客园的叫middleware方式) 怎么去写一个Transfer层,这里有例子 https://github.com/rsocket/rsocket-net/blob/master/RSocket.Core/Transports/SocketTransport.cs 虽然例子是用socket client的,但是对于串口其实是一样一样的过程
老哥,又来黑博客园了?[/quote] 嘿嘿,别不服气。也别故意顶帖。就这帖子,看玩非博客园系列,在回头看看博客园系列你什么感觉 至于博客园的问题其实早就公开化了,10年前吉日那个年代就公开化了。 也就是你们这种才入行的才认为他们是站在你们那边的 ps:3天前,我就知道那个撒一线的span的帖子会封推,知道为啥不?因为他们一个星期前都在到处活动了,so,你觉着他的目的是啥 so,你觉着你这种被人卖了小白,还得给人点赞,冤不冤
ziqi0716 2020-10-31
  • 打赏
  • 举报
回复
引用 12 楼 wanghui0380 的回复:
不想说啥,如果你只愿意看博客园的,只愿意采用博客园的我们说啥都没有用 我们只能说在博客园之外,全世界的其他程序员采用的通用方式是 Transfer 层-------------------------基于 System.IO.Pipelines的 IDuplexPipe方式
引用
//IDuplexPipe is defined in System.IO.Pipelines, but they provide no default implementation!!! So something like the below occurs all over the place: SignalR, etc. Also, Input and Output are awful names, but I agree they're hard to name. // BACK // // output input // // --------------- // // writer reader // // || /\ // // || || // // || || // // || || // // \/ || // // reader writer // // --------------- // // input output // // FRONT //
编解码层----------------------------编解码和用什么底层无关,tcp,udp,串口都是一样,因为Transfer层已经把底层的东西给你完全抛开了,你只是站在 FRONT那端去玩。 所以你们前面讨论啥new byte[1024] 可以作废,那啥我需要一个队列也可以作废。 当然编解码层其实另一个类似所谓的asp.net core的另一个管道pipeline(博客园的叫middleware方式) 怎么去写一个Transfer层,这里有例子 https://github.com/rsocket/rsocket-net/blob/master/RSocket.Core/Transports/SocketTransport.cs 虽然例子是用socket client的,但是对于串口其实是一样一样的过程
老哥,又来黑博客园了?
  • 打赏
  • 举报
回复
[quote=引用 14 楼 jshzp 的回复:
我是在用单片机接收其它设备发来的串口数据时,遇到这个疑惑的。主要是因为不管哪个层,都需要单片机写代码去完成,而我之前的思路是用单线程在考虑,所以时间上遇到了疑惑。如楼上所述,按照两个线程考虑,接收干接收的活儿,解析干解析的活儿,就没问题了。[/quote]

.net 最近几年已经进化到微服务任务管理时代,在操作系统线程之外,.net 框架本身管理 Task。在进程中可以有上万、几十万个 Task,极轻量级的任务,并发多线程执行。例如
    var msg = 接收到缓冲区中的消息部分;
从接收缓冲区移除msg内容;
Task.Run(async () =>
{
var output = 处理消息内容(msg);
SendMessage(output);
});

使用 Task 比线程功能丰富、代码清晰,且“轻”百倍。
jshzp 2020-10-29
  • 打赏
  • 举报
回复
引用 12 楼 wanghui0380 的回复:
不想说啥,如果你只愿意看博客园的,只愿意采用博客园的我们说啥都没有用

我们只能说在博客园之外,全世界的其他程序员采用的通用方式是
Transfer 层-------------------------基于 System.IO.Pipelines的 IDuplexPipe方式

引用
//IDuplexPipe is defined in System.IO.Pipelines, but they provide no default implementation!!! So something like the below occurs all over the place: SignalR, etc. Also, Input and Output are awful names, but I agree they're hard to name.

// BACK //
// output input //
// --------------- //
// writer reader //
// || /\ //
// || || //
// || || //
// || || //
// \/ || //
// reader writer //
// --------------- //
// input output //
// FRONT //


编解码层----------------------------编解码和用什么底层无关,tcp,udp,串口都是一样,因为Transfer层已经把底层的东西给你完全抛开了,你只是站在 FRONT那端去玩。

所以你们前面讨论啥new byte[1024] 可以作废,那啥我需要一个队列也可以作废。

当然编解码层其实另一个类似所谓的asp.net core的另一个管道pipeline(博客园的叫middleware方式)

怎么去写一个Transfer层,这里有例子

https://github.com/rsocket/rsocket-net/blob/master/RSocket.Core/Transports/SocketTransport.cs

虽然例子是用socket client的,但是对于串口其实是一样一样的过程

感谢您分享的宝贵见解!
我是在用单片机接收其它设备发来的串口数据时,遇到这个疑惑的。主要是因为不管哪个层,都需要单片机写代码去完成,而我之前的思路是用单线程在考虑,所以时间上遇到了疑惑。如楼上所述,按照两个线程考虑,接收干接收的活儿,解析干解析的活儿,就没问题了。
生财 2020-10-29
  • 打赏
  • 举报
回复
参考数据流处理,或者TCP接收相关接收器代码
wanghui0380 2020-10-29
  • 打赏
  • 举报
回复
不想说啥,如果你只愿意看博客园的,只愿意采用博客园的我们说啥都没有用 我们只能说在博客园之外,全世界的其他程序员采用的通用方式是 Transfer 层-------------------------基于 System.IO.Pipelines的 IDuplexPipe方式
引用
//IDuplexPipe is defined in System.IO.Pipelines, but they provide no default implementation!!! So something like the below occurs all over the place: SignalR, etc. Also, Input and Output are awful names, but I agree they're hard to name. // BACK // // output input // // --------------- // // writer reader // // || /\ // // || || // // || || // // || || // // \/ || // // reader writer // // --------------- // // input output // // FRONT //
编解码层----------------------------编解码和用什么底层无关,tcp,udp,串口都是一样,因为Transfer层已经把底层的东西给你完全抛开了,你只是站在 FRONT那端去玩。 所以你们前面讨论啥new byte[1024] 可以作废,那啥我需要一个队列也可以作废。 当然编解码层其实另一个类似所谓的asp.net core的另一个管道pipeline(博客园的叫middleware方式) 怎么去写一个Transfer层,这里有例子 https://github.com/rsocket/rsocket-net/blob/master/RSocket.Core/Transports/SocketTransport.cs 虽然例子是用socket client的,但是对于串口其实是一样一样的过程
jshzp 2020-10-29
  • 打赏
  • 举报
回复
引用 10 楼 xian_wwq 的回复:
[quote=引用 3 楼 jshzp 的回复:][quote=引用 1 楼 晨易夕 的回复:]你这种就适合无限监听接收,将收到的数据放入一个队列中,然后异步从队列中提取片段解析。

每收到对方设备发来的报文后,我都要及时回复,所以不能接收很多数据后,再事后慢慢去解析。[/quote]

因为报文长度不固定,所以才要把收到的数据暂存,防止一次收到的数据包不完整,
不完整的数据进行解析,自然出错。
把数据放到队列中,接收线程的工作就结束了,不会因为后续解析导致接收超时异常。
用另外一个线程来从队列中读取解析。
队列操作注意加锁就没有问题。
因为相关操作都是内存操作,不存在很大的延迟。
[/quote]
我之前没注意1楼说的是异步解析。。。
你解释的很清楚,现在明白了1楼说的方法。非常感谢!
良朋 2020-10-29
  • 打赏
  • 举报
回复
有时候你只需要用readexsiting代替readline就可能解决所有的问题。
xian_wwq 2020-10-28
  • 打赏
  • 举报
回复
引用 3 楼 jshzp 的回复:
[quote=引用 1 楼 晨易夕 的回复:]你这种就适合无限监听接收,将收到的数据放入一个队列中,然后异步从队列中提取片段解析。

每收到对方设备发来的报文后,我都要及时回复,所以不能接收很多数据后,再事后慢慢去解析。[/quote]

因为报文长度不固定,所以才要把收到的数据暂存,防止一次收到的数据包不完整,
不完整的数据进行解析,自然出错。
把数据放到队列中,接收线程的工作就结束了,不会因为后续解析导致接收超时异常。
用另外一个线程来从队列中读取解析。
队列操作注意加锁就没有问题。
因为相关操作都是内存操作,不存在很大的延迟。
ziqi0716 2020-10-27
  • 打赏
  • 举报
回复
引用 6 楼 ziqi0716 的回复:
While(!Stop) { byte[] data=new byte[1024];//1024设置为数据最大长度,如果性能要求不高,用List<byte>,省事儿....,最好是数据头中包含下数据长度,便于接收时候申请数组长度 While(不是结束符){ 向data中加入读取的一个byte ; } 处理data; }
伪代码不是很严谨,大致思路
ziqi0716 2020-10-27
  • 打赏
  • 举报
回复
数据协议一般要包含包头,数据,包尾三部分的,包头一般处理数据长度等信息,包尾一般包含校验信息和结束符等内容,完整的数据协议才能较好的处理各种异常.
ziqi0716 2020-10-27
  • 打赏
  • 举报
回复
While(!Stop) { byte[] data=new byte[1024];//1024设置为数据最大长度,如果性能要求不高,用List<byte>,省事儿....,最好是数据头中包含下数据长度,便于接收时候申请数组长度 While(不是结束符){ 向data中加入读取的一个byte ; } 处理data; }
耗子哭死猫 2020-10-27
  • 打赏
  • 举报
回复
拿串口助手试一下,发送到返回数据多少毫秒, 2个数据包间隔 500毫秒足够了,除非返回的数据特别慢
glen30 2020-10-27
  • 打赏
  • 举报
回复
在收到设定数量的字节数据后触发接收事件中,先把数据读取出来保存到队列里面,然后在队列里面解析,解析到一个完整的帧则调用处理代码
jshzp 2020-10-27
  • 打赏
  • 举报
回复
引用 1 楼 晨易夕 的回复:
你这种就适合无限监听接收,将收到的数据放入一个队列中,然后异步从队列中提取片段解析。

每收到对方设备发来的报文后,我都要及时回复,所以不能接收很多数据后,再事后慢慢去解析。
jshzp 2020-10-27
  • 打赏
  • 举报
回复
引用 1 楼 晨易夕 的回复:
你这种就适合无限监听接收,将收到的数据放入一个队列中,然后异步从队列中提取片段解析。

感谢回复!
“无限监听接收,将收到的数据放入一个队列中”,代码应该怎样写,可以大致说一下吗,谢谢
晨易夕 2020-10-27
  • 打赏
  • 举报
回复
你这种就适合无限监听接收,将收到的数据放入一个队列中,然后异步从队列中提取片段解析。
six2me 2020-10-27
  • 打赏
  • 举报
回复
1.你接收数据是十六进制的数据不是ASCII。可以使用如下: comm.Read(buf, 0, n); foreach (byte b in buf)//将报文保存到字符串 { //依次的拼接出16进制字符串 builder.Append(b.ToString("X2")); } data.AddRange(buf);//或者报文保存到byte【】中, 2.开一个线程分析byte【】 从第一个字节分析 F3,没有删除 找到0xFD,开始分析报文,完成后删除 继续

111,092

社区成员

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

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

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