串口通信,问个简单的问题,立即给分

易2017 2017-09-13 10:03:47
c#串口接收事件是一帧数据触发一次,还是一个字节触发一次,能说详细点更好,另外,如果是一帧数据触发一次,我该如何判断这帧数据的字节数,目前提出两种方法:1、通过包头包尾判断 2、通过comPort.BytesToRead读出缓存区的字节数,处理完一帧数据清空缓存区。欢迎大家指正
...全文
343 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
yizhiluoye 2017-09-14
  • 打赏
  • 举报
回复
用事件+对列的方式,事件触发,将数据区的数据读取到队列中,然后进行数据校验,数据正确接收完成
  • 打赏
  • 举报
回复
引用 14 楼 qq_38588710 的回复:
这个问题我存在一个疑问,串口是源源不断的接收数据的,会不会存在触发了接收事件却因为上一个接收事件延迟阻塞了,导致这帧数据没被处理。我看csdn上,串口接收事件,备注了不保证每一个字节都被接收
就是因为这个,所以在一个普通的并且比较规范的接收数据的代码中,怎样都不应该写 DiscardInBuffer 这种东西的。怎么可能在以读取数据为目的代码中去“放弃缓冲区中未接收的数据”呢? 这类代码中写 DiscardInBuffer 是不对的。
  • 打赏
  • 举报
回复
比如说,你定义通讯协议信令格式,定义为每一个消息都是使用 \r 来结束,那么程序中应该用一个(比如说)List<byte> 集合来缓存收到的数据。你 Read(.....) 并且将数据加入集合之后,判断最后一个字节是不是 0x0d,如果是那么这个集合中的内容就包括了1条或者多条完整的消息了、可以进行解析处理消息内容,如果不是那么就不要去解析消息内容。 msdn 上的例子总是很简单很基本的语法展示。例如它在 DataReceived 事件的例子中,使用 ReadLine() 方法来阻塞式地去等待 NewLine 属性所标记的结束符号的到来,这就是一种比较原始的同步阻塞式的方法。跟上面的灵活、异步的处理方法,在“信令结束”协议这个本质上是一致的。
易2017 2017-09-13
  • 打赏
  • 举报
回复
引用 13 楼 sp1234 的回复:
[quote=引用 11 楼 qq_38588710 的回复:] 受教,如果我的数据帧长度很小,比如只有8个字节,还会存在这种情况么。因为上位机采集的是一些实时的测量数据,数据帧并不大。
不可说“会”还是“不会”,只能说概率小了而已。但是如果你正规地编写一个通讯协议处理程序,能处理粘包分包,你就根本不用纠结这些字节数是很小还是很大了啊。[/quote] 有道理
易2017 2017-09-13
  • 打赏
  • 举报
回复
引用 10 楼 sp1234 的回复:
不知道从什么时候起,我看到许多人在 csdn 上贴的 DataReceived 代码中都有调用
DiscardOutBuffer
这么一句。 原本来说,你使用 sr.Read(...) 就把缓冲区里边的内容读取走了,你用得着特意去“丢弃缓冲区中的数据”吗?写 DiscardOutBuffer 应该是一个 bug。虽然可能暂时不出错,以至于是多余的代码,但是这个没用的代码在特定情况下、特意设计的测试下一定出 bug。所以不应该写 DiscardOutBuffer 代码。
这个问题我存在一个疑问,串口是源源不断的接收数据的,会不会存在触发了接收事件却因为上一个接收事件延迟阻塞了,导致这帧数据没被处理。我看csdn上,串口接收事件,备注了不保证每一个字节都被接收
  • 打赏
  • 举报
回复
引用 11 楼 qq_38588710 的回复:
受教,如果我的数据帧长度很小,比如只有8个字节,还会存在这种情况么。因为上位机采集的是一些实时的测量数据,数据帧并不大。
不可说“会”还是“不会”,只能说概率小了而已。但是如果你正规地编写一个通讯协议处理程序,能处理粘包分包,你就根本不用纠结这些字节数是很小还是很大了啊。
  • 打赏
  • 举报
回复
DiscardOutBuffer --> DiscardInBuffer
易2017 2017-09-13
  • 打赏
  • 举报
回复
引用 9 楼 sp1234 的回复:
[quote=引用 3 楼 qq_38588710 的回复:] 启发,也就是说我的一帧数据就是一次写入接收缓冲区的字节数,那么,这个写入操作是原子性的么,比如说我写入这一帧数据的时候,会不会存在一种情况,当我写入一半,命令执行清空缓冲区,这时原来的一帧数据只存在一半。
你写入的3帧假设分别是500、1000、5500字节,那么在另一端读取时可能分为4帧,分别是 800字节、2200字节、2000字节、2000字节。也就是说,写一个正规的通讯程序自然就是要处理分包和粘包。而 msdn 上的一些例子是错误的,是故意简化的,只是用来示意个别语法,根本不能保证正确地处理业务通讯逻辑。[/quote] 受教,如果我的数据帧长度很小,比如只有8个字节,还会存在这种情况么。因为上位机采集的是一些实时的测量数据,数据帧并不大。
  • 打赏
  • 举报
回复
不知道从什么时候起,我看到许多人在 csdn 上贴的 DataReceived 代码中都有调用
DiscardOutBuffer
这么一句。 原本来说,你使用 sr.Read(...) 就把缓冲区里边的内容读取走了,你用得着特意去“丢弃缓冲区中的数据”吗?写 DiscardOutBuffer 应该是一个 bug。虽然可能暂时不出错,以至于是多余的代码,但是这个没用的代码在特定情况下、特意设计的测试下一定出 bug。所以不应该写 DiscardOutBuffer 代码。
  • 打赏
  • 举报
回复
引用 3 楼 qq_38588710 的回复:
启发,也就是说我的一帧数据就是一次写入接收缓冲区的字节数,那么,这个写入操作是原子性的么,比如说我写入这一帧数据的时候,会不会存在一种情况,当我写入一半,命令执行清空缓冲区,这时原来的一帧数据只存在一半。
你写入的3帧假设分别是500、1000、5500字节,那么在另一端读取时可能分为4帧,分别是 800字节、2200字节、2000字节、2000字节。也就是说,写一个正规的通讯程序自然就是要处理分包和粘包。而 msdn 上的一些例子是错误的,是故意简化的,只是用来示意个别语法,根本不能保证正确地处理业务通讯逻辑。
易2017 2017-09-13
  • 打赏
  • 举报
回复
引用 6 楼 qq_38588710 的回复:
[quote=引用 5 楼 yuhijk2055 的回复:] [quote=引用 3 楼 qq_38588710 的回复:] [quote=引用 1 楼 duanzi_peng 的回复:] 没有帧的概念,BytesToRead 已经说明是通过字节来发送接收数据。 接收事件是通过 write方法写入数据到缓冲区的时候触发的,只要有写入,就会触发。 接收的数据大小并不是一定是一个字节,也可能是多个字节,这个根据你写入的量来决定。 PS:个人观点,欢迎指正。
启发,也就是说我的一帧数据就是一次写入接收缓冲区的字节数,那么,这个写入操作是原子性的么,比如说我写入这一帧数据的时候,会不会存在一种情况,当我写入一半,命令执行清空缓冲区,这时原来的一帧数据只存在一半。[/quote] 会有这种情况产生,会有缓冲区数据写入一半或者没有写完我想的完整数据,接收事件就执行了,把数据取走,缓冲区被清空,所以我们在接收数据的时候,如果知道数据大小就定义一下ReceivedBytesThreshold的数值,或者在接收到一次时,就对数据做完整性分析,如果数据不完整,就把这次数据存起来,等下一次数据发过来做数据完整性分析,直到接收到完整数据为止。[/quote] 你这回答的应该不是同一个问题[/quote] 针对这个问题我写了个测试
  for (int i = 0; i < 8; i++)
                {
                    receviedBuf[i] = Convert.ToByte(comPort.ReadByte());//读取数据
                    comPort.DiscardInBuffer();
                }
操作:串口软件源源不断的发送8字节数据帧,测试demo采用断点调试 现象:receivedBuf中被8字节数据帧中的第一个字节填满,也就是说我不断发送11 22 33 44 55 66 77 88这帧数据,receivedBuf被赋值11 11 11 11 11 11 11 11 分析:写入的数据帧是一次性写入的,不存在同一数据帧分字节写入,ReadByte是同步读取读取的 问题:可能断点操作存在延时性,让数据帧有足够的时间全部写入
exception92 2017-09-13
  • 打赏
  • 举报
回复
引用 3 楼 qq_38588710 的回复:
[quote=引用 1 楼 duanzi_peng 的回复:] 没有帧的概念,BytesToRead 已经说明是通过字节来发送接收数据。 接收事件是通过 write方法写入数据到缓冲区的时候触发的,只要有写入,就会触发。 接收的数据大小并不是一定是一个字节,也可能是多个字节,这个根据你写入的量来决定。 PS:个人观点,欢迎指正。
启发,也就是说我的一帧数据就是一次写入接收缓冲区的字节数,那么,这个写入操作是原子性的么,比如说我写入这一帧数据的时候,会不会存在一种情况,当我写入一半,命令执行清空缓冲区,这时原来的一帧数据只存在一半。[/quote] SerialPort类是线程安全的,有自己的执行线程。它的读/写操作是不可中断的,当然这个无法检测到。只能说write/read方式可以理解成原子性,因为它们都提供了三个参数,分别为:byte[],字节起始位置,字节长度,确保每次的操作数据都是完整的byte[]。 至于写入的时候 清空缓冲区,这个是无法执行的,因为除非你手动调用DiscardInBuffer 方法才会清空接收的数据,但是这个时候数据肯定已经完全写入缓冲区了。
易2017 2017-09-13
  • 打赏
  • 举报
回复
引用 5 楼 yuhijk2055 的回复:
[quote=引用 3 楼 qq_38588710 的回复:] [quote=引用 1 楼 duanzi_peng 的回复:] 没有帧的概念,BytesToRead 已经说明是通过字节来发送接收数据。 接收事件是通过 write方法写入数据到缓冲区的时候触发的,只要有写入,就会触发。 接收的数据大小并不是一定是一个字节,也可能是多个字节,这个根据你写入的量来决定。 PS:个人观点,欢迎指正。
启发,也就是说我的一帧数据就是一次写入接收缓冲区的字节数,那么,这个写入操作是原子性的么,比如说我写入这一帧数据的时候,会不会存在一种情况,当我写入一半,命令执行清空缓冲区,这时原来的一帧数据只存在一半。[/quote] 会有这种情况产生,会有缓冲区数据写入一半或者没有写完我想的完整数据,接收事件就执行了,把数据取走,缓冲区被清空,所以我们在接收数据的时候,如果知道数据大小就定义一下ReceivedBytesThreshold的数值,或者在接收到一次时,就对数据做完整性分析,如果数据不完整,就把这次数据存起来,等下一次数据发过来做数据完整性分析,直到接收到完整数据为止。[/quote] 你这回答的应该不是同一个问题
无情时尚 2017-09-13
  • 打赏
  • 举报
回复
引用 3 楼 qq_38588710 的回复:
[quote=引用 1 楼 duanzi_peng 的回复:] 没有帧的概念,BytesToRead 已经说明是通过字节来发送接收数据。 接收事件是通过 write方法写入数据到缓冲区的时候触发的,只要有写入,就会触发。 接收的数据大小并不是一定是一个字节,也可能是多个字节,这个根据你写入的量来决定。 PS:个人观点,欢迎指正。
启发,也就是说我的一帧数据就是一次写入接收缓冲区的字节数,那么,这个写入操作是原子性的么,比如说我写入这一帧数据的时候,会不会存在一种情况,当我写入一半,命令执行清空缓冲区,这时原来的一帧数据只存在一半。[/quote] 会有这种情况产生,会有缓冲区数据写入一半或者没有写完我想的完整数据,接收事件就执行了,把数据取走,缓冲区被清空,所以我们在接收数据的时候,如果知道数据大小就定义一下ReceivedBytesThreshold的数值,或者在接收到一次时,就对数据做完整性分析,如果数据不完整,就把这次数据存起来,等下一次数据发过来做数据完整性分析,直到接收到完整数据为止。
xuzuning 2017-09-13
  • 打赏
  • 举报
回复
如果传输的数据足够长,那么所谓帧就是接收缓冲区的大小(当然可以是1个字节) 按照 RS232 通信协议,数据中是包含数据长度的,据此你就可以识别出有效数据
易2017 2017-09-13
  • 打赏
  • 举报
回复
引用 1 楼 duanzi_peng 的回复:
没有帧的概念,BytesToRead 已经说明是通过字节来发送接收数据。 接收事件是通过 write方法写入数据到缓冲区的时候触发的,只要有写入,就会触发。 接收的数据大小并不是一定是一个字节,也可能是多个字节,这个根据你写入的量来决定。 PS:个人观点,欢迎指正。
启发,也就是说我的一帧数据就是一次写入接收缓冲区的字节数,那么,这个写入操作是原子性的么,比如说我写入这一帧数据的时候,会不会存在一种情况,当我写入一半,命令执行清空缓冲区,这时原来的一帧数据只存在一半。
无情时尚 2017-09-13
  • 打赏
  • 举报
回复
引用 1 楼 duanzi_peng 的回复:
没有帧的概念,BytesToRead 已经说明是通过字节来发送接收数据。 接收事件是通过 write方法写入数据到缓冲区的时候触发的,只要有写入,就会触发。 接收的数据大小并不是一定是一个字节,也可能是多个字节,这个根据你写入的量来决定。 PS:个人观点,欢迎指正。
说得有理,接收时可以用ReceivedBytesThreshold的大小来决定在有多少数据的时候触发接收事件
exception92 2017-09-13
  • 打赏
  • 举报
回复
没有帧的概念,BytesToRead 已经说明是通过字节来发送接收数据。 接收事件是通过 write方法写入数据到缓冲区的时候触发的,只要有写入,就会触发。 接收的数据大小并不是一定是一个字节,也可能是多个字节,这个根据你写入的量来决定。 PS:个人观点,欢迎指正。
与硬件通信的程序基本上要用到串口,虽然qt5以后集成了串口通信类,但是个人觉得那个串口通信类有点问题,在linux上表现很好,windows上大数据会有怪怪的问题出现,而且只能在qt5以上的版本才能用,无奈大部的嵌入式linux上还停留在4.7.1到4.8.5左右的版本,所以本人一直喜欢用第三方的串口通信类做处理。 程序调试中经常需要串口调试,甚至还需要模拟设备数据回复,甚至串口转网络出去,特意将这些常用功能都做到一个串口调试助手中去。 基本功能: 1:支持16进制数据发送与接收。 2:支持windows下COM9以上的串口通信。 3:实时显示收发数据字节大小以及串口状态。 4:支持任意qt版本,亲测4.7.0 4.8.5 4.8.7 5.4.1 5.7.0 5.8.0。 5:支持串口转网络数据收发。 高级功能: 1:可自由管理需要发送的数据,每次只要从下拉框中选择数据即可,无需重新输入数据。 2:可模拟设备回复数据,需要在主界面开启模拟设备回复数据。当接收到设置好的指令时,立即回复设置的回复指令。例如指定收到0x16 0x00 0xFF 0x01需要回复0x16 0x00 0xFE 0x01,则只需要在SendData.txt中添加一条数据16 00 FF 01:16 00 FE 01即可。 3:可定时发送数据和保存数据到文本文件:,默认间隔5秒钟,可更改间隔时间。 4:在不断接收到大量数据时,可以暂停显示数据来查看具体数据,后台依然接收数据但不处理,无需关闭串口来查看已接收到的数据。 5:每次收到的数据都是完整的一条数据,而不是脱节的,做了延时处理。 6:一套源码随处编译,无需更改串口通信类,已在XP/WIN7/UBUNTU/ARMLINUX系统下成功编译并运行。

110,566

社区成员

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

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

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