求串口通讯达人解答,遇到了一个很蹊跷的问题

leafmao 2012-12-14 12:03:54
先上代码

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{//串口的波特率为9600
List<byte> received=new List<byte>() ;
int loopCount=0;//检测共分几次读取串口
while (serialPort1.BytesToRead > 0)
{
loopCount++;
int len = serialPort1.BytesToRead;
byte[] temp = new byte[len];
serialPort1.Read(temp, 0, len);
received.AddRange(temp);
Thread.Sleep(10);//延迟10ms,等待串口的数据缓冲好
}
//其余处理代码
}

问题是:经过调试发现,同样的波特率,但是却发现接收的不稳定,
第一组数据从下位机发送了21个字节过来,有时是1次就读取了(通过查看loopCount的值发现), 有时是2次读取串口。当1次性读取串口时,第一次serialPort1.BytesToRead=21;分为两次读取时,第一次serialPort1.BytesToRead=16,第二次循环时,serialPort1.BytesToRead=5,很奇怪这是为什么呢

还有种情况是:第一次读取了16个字节,在Thread.Sleep(10)后,serialPort1.BytesToRead=0,于是数据就接收不完整了,此时就会跳出while (serialPort1.BytesToRead > 0)循环,我很奇怪呀,这10ms的时间里,用示波器可以看到下位机是有数据传送过来的,为什么这些数据没有进入serialPort1的缓冲区呢?
一样的代码,一样的波特率,为什么有时候进入触发事件时,缓冲区读到的字节数是16,有时候会超过16?为什么有时候Thread.Sleep(10);剩下的数据会存入缓冲区,有时候却不会?

不知道描述清楚了没,求达人解答呀
...全文
1719 68 打赏 收藏 转发到动态 举报
写回复
用AI写文章
68 条回复
切换为时间正序
请发表友善的回复…
发表回复
Fox1536 2013-06-24
  • 打赏
  • 举报
回复
引用 67 楼 leafmao 的回复:
自己给自己的帖子一个回答吧,是关于在sleep(10)的期间内,数据未进入缓冲区的问题。 用很多有代表性的数据进行了多次测试,猜测如下:串口硬件在收到8个字节的数据时,会将这些数据存到SerialPort的输入缓存区,不足8个字节时,会等待足够8个字节,但在一定时间内还不足8个字节,还是会将这些数据存至SerialPort的输入缓存区,这个时间为10ms~15ms。 总结起来就是:串口每接收到8个字节便会送到SerialPort的输入缓存区,不足8个字节,每10ms~15ms将接受到的数据存至SerialPort的输入缓存区。(这也就不难理解,为什么在用触发事件读取串口时,当数据多于8个字节时,会多次进入接收事件,每8个字节触发一次) 以上是根据自己进行多次试验得出的结论,不敢保证百分百正确。 关于串口硬件和软件衔接方面的资料太少了,没找到相关的,只能自己用数据试验。
怎么建立一个缓冲区啊?新手
leafmao 2013-02-26
  • 打赏
  • 举报
回复
自己给自己的帖子一个回答吧,是关于在sleep(10)的期间内,数据未进入缓冲区的问题。 用很多有代表性的数据进行了多次测试,猜测如下:串口硬件在收到8个字节的数据时,会将这些数据存到SerialPort的输入缓存区,不足8个字节时,会等待足够8个字节,但在一定时间内还不足8个字节,还是会将这些数据存至SerialPort的输入缓存区,这个时间为10ms~15ms。 总结起来就是:串口每接收到8个字节便会送到SerialPort的输入缓存区,不足8个字节,每10ms~15ms将接受到的数据存至SerialPort的输入缓存区。(这也就不难理解,为什么在用触发事件读取串口时,当数据多于8个字节时,会多次进入接收事件,每8个字节触发一次) 以上是根据自己进行多次试验得出的结论,不敢保证百分百正确。 关于串口硬件和软件衔接方面的资料太少了,没找到相关的,只能自己用数据试验。
知擎物联 2012-12-19
  • 打赏
  • 举报
回复
引用 65 楼 leafmao 的回复:
引用 25 楼 yeqi3000 的回复:引用 23 楼 leafmao 的回复:引用 22 楼 yeqi3000 的回复:你要早点弄懂我之前发的一个帖子里的异步接收原理不至于此。我觉得是你没弄懂串口接收的机制吧,要不你来给我解释下,为什么多次试验的时候,读取串口缓冲区的字节数是不一样的,注意哦,从下位机发送过来的数据在多次试验里都一样的,不同的只是PC读取的串口缓冲区的……
既然你测试过,那就行。我的实现方式是不在DataReceived做while,怕的就是扰乱了serialPort的工作机制。 我也说过了,我的基类是实战所用,百分百可靠通讯。至于你用不用,看不看,则随便了。我当初写出来也是让想用的人多一种思路。
leafmao 2012-12-19
  • 打赏
  • 举报
回复
引用 25 楼 yeqi3000 的回复:
引用 23 楼 leafmao 的回复:引用 22 楼 yeqi3000 的回复:你要早点弄懂我之前发的一个帖子里的异步接收原理不至于此。我觉得是你没弄懂串口接收的机制吧,要不你来给我解释下,为什么多次试验的时候,读取串口缓冲区的字节数是不一样的,注意哦,从下位机发送过来的数据在多次试验里都一样的,不同的只是PC读取的串口缓冲区的字节不一样。 最后嘛,虽然你是写了那……
今天经试验发现,一个DataReceived事件里的代码没跑完时,是不会再次进入该事件的,必须等跑完才行
leafmao 2012-12-19
  • 打赏
  • 举报
回复
引用 62 楼 jiangzhanchang 的回复:
楼主太不厚道了,写了半天,一分都不给。
这。。。 因为你说的我都懂,对我遇到的问题木有帮助哎 不过很感谢你的回答 sorry啦,就只有100分,就只给了对我的问题有帮助的回复
本拉灯 2012-12-18
  • 打赏
  • 举报
回复
引用 62 楼 jiangzhanchang 的回复:
楼主太不厚道了,写了半天,一分都不给。
你光打字人家看不懂呀,直接给Code来的快些
Eric_Jiang 2012-12-18
  • 打赏
  • 举报
回复
楼主太不厚道了,写了半天,一分都不给。
  • 打赏
  • 举报
回复
串口接收是挺麻烦的
leafmao 2012-12-17
  • 打赏
  • 举报
回复
引用 56 楼 qldsrx 的回复:
影响串口接收不稳定的因素其实不重要,重要的是如何保证接收的完整,只要串口数据肯定不丢失,那么分几次接收不是问题,你需要定义每次接收的信息的结束标志,如果未检测到结束标志,则继续等待后续的数据,拼接完整再处理,一般结束标志就是换行符。
换行符的ASCII码是0x0A,数据里也很有可能出现0x0A的。 额,可能我就是那种喜欢打破砂锅问到底的吧,一定要弄明白 谢谢你的建议
salecn 2012-12-17
  • 打赏
  • 举报
回复
来学习一下,顺便帮顶!
qldsrx 2012-12-17
  • 打赏
  • 举报
回复
影响串口接收不稳定的因素其实不重要,重要的是如何保证接收的完整,只要串口数据肯定不丢失,那么分几次接收不是问题,你需要定义每次接收的信息的结束标志,如果未检测到结束标志,则继续等待后续的数据,拼接完整再处理,一般结束标志就是换行符。
leafmao 2012-12-17
  • 打赏
  • 举报
回复
引用 42 楼 vpjian 的回复:
串口编程时有任何的延时行为都有可能导至接收丢数据。 因此我的策略是使用异步或者开一个线程去读数据(这就确保了不会有延时)。读到数据后再使异步调用处理函数(这样就可以无视处理时间了)。自从使用了这个方案后从来没有发生过丢数据的情况(受干扰除外)。 附代码 C# code?1234567891011121314151617181920212223serialPo……
请问下这句的详解:CallOnReceiveDataInvoke(this, arg); //使用异步方式引发接收事件 怎么用异步方式引发?
leafmao 2012-12-17
  • 打赏
  • 举报
回复
引用 50 楼 wyd1520 的回复:
引用 43 楼 leafmao 的回复:引用 36 楼 wyd1520 的回复:引用 35 楼 leafmao 的回复:引用 34 楼 wyd1520 的回复:引用 32 楼 leafmao 的回复:引用 30 楼 wyd1520 的回复:serialPort1_DataReceived 用这个必须要有协义头才行 如 协议号 长度 内容 这三个 ……
好想法,之前我都不晓得List是个啥东西,现在才发现很好用啊,可以任意的Add和Remove。 你思路和想法很好,但是0x55也有可能在数据部分出现,不一定是结束字节,我要是想拿过来用的话还要再改改。我的想法是: 1、找到AA,AA之前的字节移除 2、根据AA之后的两个字节判断数据长度,与queue的长度比较,queue的长度不够就继续接收 3、判断结束字节是不是55,比如开头为AA 00 00,那就判断AA之后的第20个字节是不是55,如果是就判断校验和是否正确,不是55就寻找下一个AA 但是如果在数据的传输中,有字节被遗漏或者受干扰出错了,那样的话就会一直在等待正确的数据了,此时串口已经没有数据发送过来了,但程序还是一直在等串口发送正确的数据过来。不知道对于这种情况你是怎么处理的?
leafmao 2012-12-17
  • 打赏
  • 举报
回复
引用 60 楼 wyd1520 的回复:
引用 54 楼 leafmao 的回复:引用 50 楼 wyd1520 的回复: 引用 43 楼 leafmao 的回复:引用 36 楼 wyd1520 的回复:引用 35 楼 leafmao 的回复:引用 34 楼 wyd1520 的回复:引用 32 楼 leafmao 的回复:引用 30 楼 wyd1520 的回复:serialPort1_DataReceived ……
你的例子让我好好的学习了面向对象思想,我再根据自己的需求做些修改,谢谢!! 也谢谢所有回答的筒子。
本拉灯 2012-12-17
  • 打赏
  • 举报
回复
引用 54 楼 leafmao 的回复:
引用 50 楼 wyd1520 的回复: 引用 43 楼 leafmao 的回复:引用 36 楼 wyd1520 的回复:引用 35 楼 leafmao 的回复:引用 34 楼 wyd1520 的回复:引用 32 楼 leafmao 的回复:引用 30 楼 wyd1520 的回复:serialPort1_DataReceived 用这个必须要有协义头才行 如 协议号 长度 内容 ……
如果内容中有55 那就把Find()中的改一下就行了 int length= GetLength(); if (m_buffer.Count <length) { return false; } if(m_Buffer[length]!=0x55) { m_buffer.RemoveRange(0, length); return false; } 这样就可以了。接下来自己要怎么判断还是怎么判断 你所说的 但是如果在数据的传输中,有字节被遗漏或者受干扰出错了,那样的话就会一直在等待正确的数据了,此时串口已经没有数据发送过来了,但程序还是一直在等串口发送正确的数据过来。不知道对于这种情况你是怎么处理的? 字节出错问题 如果数据没有接到收完整的包,他是要一直等待的,这是正常的。你要是防止等待过长,那就加个超时功能,在向单片机发送数据后,如果超过你指定的时间内,没收到回应。那先将缓冲区的数据清空,重新发一个指令给单片机就是了。
leafmao 2012-12-16
  • 打赏
  • 举报
回复
引用 42 楼 vpjian 的回复:
串口编程时有任何的延时行为都有可能导至接收丢数据。 因此我的策略是使用异步或者开一个线程去读数据(这就确保了不会有延时)。读到数据后再使异步调用处理函数(这样就可以无视处理时间了)。自从使用了这个方案后从来没有发生过丢数据的情况(受干扰除外)。 附代码 C# code?1234567891011121314151617181920212223serialPo……
谢谢,周一我去试试你的方法,现在不在办公室。 你的程序里有的我都没见过,还要先去查下MSDN资料,伤不起。
leafmao 2012-12-16
  • 打赏
  • 举报
回复
引用 36 楼 wyd1520 的回复:
引用 35 楼 leafmao 的回复:引用 34 楼 wyd1520 的回复:引用 32 楼 leafmao 的回复:引用 30 楼 wyd1520 的回复:serialPort1_DataReceived 用这个必须要有协义头才行 如 协议号 长度 内容 这三个 要是不明白百度一下,Socket粘包原理,跟这个一样的。 这个事件要这么……
又环形,实在不懂环形
vpjian 2012-12-16
  • 打赏
  • 举报
回复
串口编程时有任何的延时行为都有可能导至接收丢数据。 因此我的策略是使用异步或者开一个线程去读数据(这就确保了不会有延时)。读到数据后再使异步调用处理函数(这样就可以无视处理时间了)。自从使用了这个方案后从来没有发生过丢数据的情况(受干扰除外)。 附代码


serialPort = new System.IO.Ports.SerialPort();
serialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(serialPort_DataReceived);
..........
serialPort.Open();




        void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.AboveNormal;//调高优先级,保证线程不会被其它线程阻塞。
            if (e.EventType == System.IO.Ports.SerialData.Chars)
            {
                while (serialPort.BytesToRead > 0)
                {
                    BuffEventArgs arg = new BuffEventArgs();
                    arg.Connection = this;
                    arg.Data = new byte[512];
                    arg.Length = serialPort.Read(arg.Data, 0, arg.Data.Length);
                    CallOnReceiveDataInvoke(this, arg); //使用异步方式引发接收事件                  
                }
            }
        }



leafmao 2012-12-16
  • 打赏
  • 举报
回复
引用 37 楼 sp1234 的回复:
loopCount不管是1,还是直到21,都是正常的。你不能根据这个就说人家.net“不稳定”,这都是正常的。 不然还是抛开低层次系统自己去拿c“开发”操作系统驱动吧。
我是小白不懂呀,求大神指导。 就是搞不懂为什么同样的情况,为什么有时候是这样,有时候是那样,能说下引起这种现象的原因吗。为什么有时候触发DataReceived事件的时候,串口已经有21个字节进入缓冲区(当然可能会更多),有时却只有16个字节,甚至有时是8个字节?说下你的见解嘛,我比较笨,懂的又不多,想不出有什么原因可能导致这么个结果。
leafmao 2012-12-16
  • 打赏
  • 举报
回复
引用 39 楼 truelove12 的回复:
和yeqi3000聊过,他还是很有经验的。 回答你的两个问题: 1、在使用断点调试时和不用断点调试时,你看到的结果是不一样的。 如果在断点的情况下,21个字节,可能第一次只能接收到5个byte,第二次可以收到16个字节。。 如果是非断点的情况下,多半这21个字节可以一次性收完,因为数据量很小。所以,千万不要让断点调试误导你。 2、这里没有必要使用thread……
感谢你们的热心回答,但是我真的要泪奔了我真正想问的问题似乎一直被大家忽略了。 进行多次调试的时候,我没有用断点,是通过查看loopCount知道共读取了几次串口的。有时是1次,有时是2次,所以才感觉不稳定,因此我真正想问的是,为什么会出现这种现象呢。 另外,接收的数据不是固定长度的,只是第一组数据是21个字节,后面的是十几个字节的,也有几个字节的。 用sleep是因为我希望在一次DataReceived事件里就把数据读取完毕,而不是进入多次DataReceived事件。 最后,缓冲区的是不会溢出的,因为PC端发送一个指令时,下位机只回答一次。第一次的时候,下位机只发了21个字节。 谢谢你的回答,但是那个,还是希望能看清我的问题一定是我没注意分段,没用红字标明,才会模糊问题的焦点
加载更多回复(47)

110,500

社区成员

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

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

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