问题描述
我现在通过以硬件发送数据波特率115200数据一秒一发。上位机接收数据用的是C#winform使用DataReceived这个自带的方法接收数据。前台用了一个RichTextBox控件显示。我设置了一个变量i只要一进DataReceived方法自加一次,值赋给RichTextBox。现在的显示就应该是RichTextBox显示的数一秒加一次1,2,3,4.....一直加下去。但是现在的显示是1,3,,6,9,12。请大神指点。voidSp_DataReceived(objectsender,SerialDataReceivedEventArgse){if(!com.Sp.IsOpen){MessageBox.Show("ComError");}intid=Thread.CurrentThread.ManagedThreadId;if(rtb_Test.InvokeRequired){i=i+1;this.Invoke(a,i);}else{rtb_Test.Text=Builder.ToString();}}privatedelegatevoidSetValues_Thead(inti);voidSetValue(intk){rtb_Test.Text=(i).ToString();}
解决方案
解决方案二:
其中a就是那个委托的对象。我断点调试了下i的数据没问题一直自加(1,2,3,4,5,6,7,8,9,1o.........)但是就是显示的不对
解决方案三:
你写如下代码:for(inti=0;i<10000000;i++){this.Invoke(a,i);}我不信你能看清每一个变化2个数据间隔时间太短,一次跳2个数,那不是太正常了
解决方案四:
解决方案五:
要么你别用rtb_Test.Text=(i).ToString();而改用rtb_Test.Text+=(i).ToString()+"";再看结果,看到底是没变,还是变的太快没看见
解决方案六:
串口是一秒一发一秒更新一次还快吗?使用+=确实能看到!
解决方案七:
引用2楼Z65443344的回复:
你写如下代码:for(inti=0;i<10000000;i++){this.Invoke(a,i);}我不信你能看清每一个变化2个数据间隔时间太短,一次跳2个数,那不是太正常了
你这个循环肯定是太快了!串口设置的才一秒一发,不应该啊?
解决方案八:
引用6楼nileige123的回复:
Quote: 引用2楼Z65443344的回复:
你写如下代码:for(inti=0;i<10000000;i++){this.Invoke(a,i);}我不信你能看清每一个变化2个数据间隔时间太短,一次跳2个数,那不是太正常了你这个循环肯定是太快了!串口设置的才一秒一发,不应该啊?
现在改成2秒一发效果依旧!
解决方案九:
这个问题类似多线程共享变量使用冲突的问题。为证明问题。可以在i=i+i下面使用console.write(i.tostring);不要设置断点调试,输出i的值如果确实是这个问题。你可以用lock来解决。
解决方案十:
接收几次跟发送几次没有任何关系你只发送一次,但是可能事件会执行2次,因为你使用的异步接收,不能确定回调函数到底什么时候执行比如设备给你发送10个字节,有可能接收到第1个字节它就执行了一次,也有可能先接收了9个再执行,也有可能一次性接收到,这都没准串口通信本身就不是像TCP和UDP那样有"包"的概念,所以程序也不可能自动给你判断每一次的数据是否完整,数据的完整性需要你自己做好协议去验证
解决方案十一:
TCP和UDP在互联网中应用时也会发生"粘包","分包"的现象何况你是串口,如果你发送的太快,很可能一次接收到设备的2次返回值,或者设备一次返回的数据你需要多次才能接收到所以你应该先将接收到的数据放到缓冲区里,比如就是一个List<byte>,每次接收完判断一下长度,如果不足先不处理,等待下一次接收完再次判断
解决方案十二:
引用5楼nileige123的回复:
串口是一秒一发一秒更新一次还快吗?使用+=确实能看到!
串口DataReceived事件的触发不是你一秒钟发送一次就只触发一次的,还跟接收的字节数有关,你虽然是一秒一发,但是你一次发送多少字节的内容呢,从显示的内容看,一次发送的内容触发了3次DataReceived事件,就是1秒钟实际上触发了三次。
解决方案十三:
引用9楼Z65443344的回复:
接收几次跟发送几次没有任何关系你只发送一次,但是可能事件会执行2次,因为你使用的异步接收,不能确定回调函数到底什么时候执行比如设备给你发送10个字节,有可能接收到第1个字节它就执行了一次,也有可能先接收了9个再执行,也有可能一次性接收到,这都没准串口通信本身就不是像TCP和UDP那样有"包"的概念,所以程序也不可能自动给你判断每一次的数据是否完整,数据的完整性需要你自己做好协议去验证
看了大家的解答问题找了!果然是一秒发一次数据,但是可能是分2或者3次接收。eg:我现在测试是2秒发16字节,接收到的是字节大部分是分开的如:16;106;655;313;412;412;313;313;412;313;511;412;313;5115。很少有一次能接16个字节的。怎么能同步接收呢?就是一次数据发完了再接收!在硬件上的中断就是一次收一个字节有个计时器超过一定时间没数据就算一包。那我怎么处理啊因为数据长度不定不能按长度处理。我的思路是开个定时器或者线程500ms处理一次数据。处理完把数组清了。因为是1秒1发500ms足够接收完数据。但是我感觉这种方法不好有没有什么更好的方法。
解决方案十四:
引用12楼nileige123的回复:
Quote: 引用9楼Z65443344的回复:
接收几次跟发送几次没有任何关系你只发送一次,但是可能事件会执行2次,因为你使用的异步接收,不能确定回调函数到底什么时候执行比如设备给你发送10个字节,有可能接收到第1个字节它就执行了一次,也有可能先接收了9个再执行,也有可能一次性接收到,这都没准串口通信本身就不是像TCP和UDP那样有"包"的概念,所以程序也不可能自动给你判断每一次的数据是否完整,数据的完整性需要你自己做好协议去验证看了大家的解答问题找了!果然是一秒发一次数据,但是可能是分2或者3次接收。eg:我现在测试是2秒发16字节,接收到的是字节大部分是分开的如:16;106;655;313;412;412;313;313;412;313;511;412;313;5115。很少有一次能接16个字节的。怎么能同步接收呢?就是一次数据发完了再接收!在硬件上的中断就是一次收一个字节有个计时器超过一定时间没数据就算一包。那我怎么处理啊因为数据长度不定不能按长度处理。我的思路是开个定时器或者线程500ms处理一次数据。处理完把数组清了。因为是1秒1发500ms足够接收完数据。但是我感觉这种方法不好有没有什么更好的方法。
首先你要确定下位机(硬件)与上位机记性收发数据时,并不严格按照逻辑意义的“包”进行即:1.一个包可能分多个帧发出或接收。2.一个帧里面有可能含有多个包的数据比如,逻辑的包的数据是第一个包3A000c03053F第二包3A000f0406cc那么接收到的帧有可能是3A00;0c03053f3A;000f0406cc针对上述情况,需要定义通讯协议,按照具体的场景可以定义不同复杂度的协议简单的情况,直接协商一个包结束字节(该字节不会在数据中出现)一般的情况下,建议使用类似状态机协议,比如定义一包的数据包头是啥,第二个字节是数据长度,后面跟上数据,最后加上校验码更复杂的不建议重复造轮子,可以借鉴RFID、TCP等等的数据协议
解决方案十五:
引用12楼nileige123的回复:
Quote: 引用9楼Z65443344的回复:
接收几次跟发送几次没有任何关系你只发送一次,但是可能事件会执行2次,因为你使用的异步接收,不能确定回调函数到底什么时候执行比如设备给你发送10个字节,有可能接收到第1个字节它就执行了一次,也有可能先接收了9个再执行,也有可能一次性接收到,这都没准串口通信本身就不是像TCP和UDP那样有"包"的概念,所以程序也不可能自动给你判断每一次的数据是否完整,数据的完整性需要你自己做好协议去验证看了大家的解答问题找了!果然是一秒发一次数据,但是可能是分2或者3次接收。eg:我现在测试是2秒发16字节,接收到的是字节大部分是分开的如:16;106;655;313;412;412;313;313;412;313;511;412;313;5115。很少有一次能接16个字节的。怎么能同步接收呢?就是一次数据发完了再接收!在硬件上的中断就是一次收一个字节有个计时器超过一定时间没数据就算一包。那我怎么处理啊因为数据长度不定不能按长度处理。我的思路是开个定时器或者线程500ms处理一次数据。处理完把数组清了。因为是1秒1发500ms足够接收完数据。但是我感觉这种方法不好有没有什么更好的方法。
如果你非要这样,就给每个串口开个线程,用while(true)同步接收,否则你现在是异步接收,系统接收到数据就会自动调用回调函数
解决方案:
根据不同的数据协议可以选择不同的处理方式;比如:1一个基础方法一直监听串口接收到的数据,并写入到临时存储数组中2存入临时数组中出发一个检查现有数据的方法,该方法分析现有数据,按照协议,把符合完整“包”定义的从临时存储数组中取出,然后交给后续的方法处理;如果没有符合完整“包”定义的,继续等待,等待下次收到数据继续处理一次
解决方案:
其中API方式就是同步接收的当然你也可以用SerialPort同步接收
解决方案:
引用15楼yanbuodiao的回复:
根据不同的数据协议可以选择不同的处理方式;比如:1一个基础方法一直监听串口接收到的数据,并写入到临时存储数组中2存入临时数组中出发一个检查现有数据的方法,该方法分析现有数据,按照协议,把符合完整“包”定义的从临时存储数组中取出,然后交给后续的方法处理;如果没有符合完整“包”定义的,继续等待,等待下次收到数据继续处理一次
有协议,协议格式大概是:包头+数据长度+数据+校验都有。如果我按从包头数长度解析的话,如果是中间的有一帧丢数据了,那么解析的时候这“包”原有的数据就不够了,我只能从下一“包”数据里面取。结果校验不通过,我只能把这包数据和下一包数据都丢掉。会多丢一包数据。
解决方案:
是不是大家接收的时候都这样啊,今天用监视软件测试了一下别人的程序,为什么别人的是偶尔出现呢我这个总是这样测试的java写的!
解决方案:
引用17楼nileige123的回复:
Quote: 引用15楼yanbuodiao的回复:
根据不同的数据协议可以选择不同的处理方式;比如:1一个基础方法一直监听串口接收到的数据,并写入到临时存储数组中2存入临时数组中出发一个检查现有数据的方法,该方法分析现有数据,按照协议,把符合完整“包”定义的从临时存储数组中取出,然后交给后续的方法处理;如果没有符合完整“包”定义的,继续等待,等待下次收到数据继续处理一次有协议,协议格式大概是:包头+数据长度+数据+校验都有。如果我按从包头数长度解析的话,如果是中间的有一帧丢数据了,那么解析的时候这“包”原有的数据就不够了,我只能从下一“包”数据里面取。结果校验不通过,我只能把这包数据和下一包数据都丢掉。会多丢一包数据。
首先:你需要跟下位机的同事协商,为什么会丢数据;这是关键,关于上下位机的通讯,需要大家配合,必须有一个可靠度的问题其次:你提到的某个包的数据有问题的时候,直接丢弃这个包,最好有日志,也是为你跟下位机配合的时候拿出证据你直接丢弃有问题的包,不影响后面包的处理
解决方案:
多线程中不能胡乱共享外部变量。大致来说,流程应该这样if(rtb_Test.InvokeRequired){intj;lock(lockFlagObject){i=i+1;j=i;}this.Invoke(a,j);}else{rtb_Test.Text=Builder.ToString();}
其中最关键的,是要传送互不共享的j变量,而不是i。
解决方案:
当然你说的“winform使用DataReceived”设置为仅仅在主线程触发的,那么不用考虑它是在子线程中运行问题。但是这样,就不可能出现“1,3,,6,9,12”的问题,而且你为什么要写this.Invoke而不直接调用a方法呢?到底,你得DataReceived是在主线程触发的,还是在不同的子线程触发的,这需要你自己回答这个问题。
解决方案:
引用21楼sp1234的回复:
当然你说的“winform使用DataReceived”设置为仅仅在主线程触发的,那么不用考虑它是在子线程中运行问题。但是这样,就不可能出现“1,3,,6,9,12”的问题,而且你为什么要写this.Invoke而不直接调用a方法呢?到底,你得DataReceived是在主线程触发的,还是在不同的子线程触发的,这需要你自己回答这个问题。
仔细看回复,问题解决了。跨线程调用需要invoke。
解决方案:
先MARK,我也碰到这个问题,我是serialPort.DataReceived+=newSerialDataReceivedEventHandler(ReceiveData)就是这个样子,如果接到的是'010203'那么就基本会先接到'01'然后再'0203',我觉得问题就在这个触发上面。