当前位置:编程学习 > C#/ASP.NET >>

关于串口操作中想拿到返回数据的问题

用的是SerialPort类,以前的老方法是这样的:
SerialPort serPort = new SerialPort("COM1",9600);//创建串口
serPort.Open();//打开串口
serPort.Write(CMD_Byte, 0, CMD_Byte.Length);发送数据
Thread.Sleep(30);//硬等待(等待硬件返回数据)
byte[] buffer = new byte[serPort.BytesToRead];//读取接收缓冲区字节数
serPort.Read(buffer, 0, bytes);//读取缓冲区的数据到数组

//然后可以比较发送指令和接收指令是否匹配,如果匹配说明该功能实现,并且返回成功

现在为了节省时间,不想通过硬等待的方式了,想通过DataReceived事件接收串口返回的字节,但是我在接收到数据后怎么和发送指令比较并且返回成功或失败呢(发送操作和接收到的数据不在一个方法中)

如下:
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data"></param>
        public int Fun_SendData(byte[] data)
        {
            try
            {
                if (!sp.IsOpen)
                {
                    sp.Open();
                }
                sp.DiscardOutBuffer();//清空发送缓冲区数据
                sp.DiscardInBuffer();//清空接收缓冲区数据
                sp.Write(data, 0, data.Count());

                //这里该怎么写代码?
                if(***){return 1;}
                else{return -1;}
            }
            catch (Exception Ex)
            {

            }
        }


        /// <summary>
        /// 接收串口返回的数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] buff = new byte[sp.BytesToRead];
            sp.Read(buff, 0, buff.Length);
            Console.WriteLine("收到数据:" + ByteArrayToHexString(buff));
            sp.Close();

        } --------------------编程问答-------------------- DataReceived中处理接收到的数据是不行的,如果没有设置触发事件所需字节数,基本上每写入一个字节触发一次,并且不能保证接收数据的完整性,如果要设置固定字节数触发事件,也不可取,因为接收到的数据长度一般不能预知。
应该在DataReceived事件触发后,标志为不处理此事件,然后进入一个循环读取的线程,循环读取完毕后,再标志为处理DataReceived事件。
读取到的数据与写入命令的对应关系,可以通过写入时设置一个状态,表示某种命令,循环读取的线程读取后检查该状态,再进行成功或失败的处理。 --------------------编程问答--------------------
引用楼主 weistar 的回复:
现在为了节省时间,不想通过硬等待的方式了


把这个解释一下。含义不同,解决方案就不同。基本上,设计都是针对你能提出的带有具体数量参数的问题,而不是你说怎样时髦就怎样设计。具体解释一下你的“节省时间”是个什么概念,最好举出数字例子来说明一下。 --------------------编程问答-------------------- 把这个解释一下。含义不同,解决方案就不同。基本上,设计都是针对你能提出的带有具体数量参数的问题,而不是你说怎样时髦就怎样设计。具体解释一下你的“节省时间”是个什么概念,最好举出数字例子来说明一下。 --------------------编程问答-------------------- 如果你说30毫秒时间太长,那么你可以改为Sleep(0)啊?!

所以我想你大概不会这样给我解释“为了节省时间”这个目标的含义吧?!

在没有搞明白真正的目的之前,我们不做多余的设计动作。 --------------------编程问答-------------------- 还好,我的协议最少是一次是16个字节,所以我设置的触发事件字节数为16。
你的意思是在线程中写while(true)?我做实验了,CPU一下就达到50%,所以我觉得用死循环来监测状态改变,太消耗资源了,不知道用异步调用能不能解决这问题,但是异步调用看了半天头晕。
QQ:136486881 --------------------编程问答-------------------- 另外,假如你们的应用中不存在读取时的超时设置,那不需要DataReceived事件来处理,写入后直接循环读取到数据结束位置就行了,或者干脆一直有一个线程循环读取都行。 --------------------编程问答-------------------- 如果循环中ReadLine或者ReadTo,这两个都是阻塞方法,CPU消耗应该是0才对,如果是50%,说明读取完毕数据后还在循环,没有阻塞,你需要设置读取到结束位置。 --------------------编程问答-------------------- 哦,这个Sleep(30),是必须要有的,如果小于30ms,我就收到不硬件的返回数据了,说白了就是等待硬件返回数据的时间,但是如果用Sleep(30),说明每条指令至少耗时30ms,那有可能硬件10ms就返回数据了,我还死等30ms,不就是浪费时间吗?
现在指标要求从开始监测信号到发射出干预信号,一共不超过1s,所以分秒必争,望高人指点。 --------------------编程问答-------------------- 另外我还要跟你说一下比较明显的设计问题。(但是这其实属于看上去比较浅显易懂——实则许多人都做不到的问题)

就是你所说的:“接收到数据后怎么和发送指令比较并且返回成功或失败”,这个思路本身就是一个顺序操作思路,而不是异步操作思路。当你的思路以及语言逻辑都是顺序的,那么其实就很难写出高效并发的程序。即使勉强使用异步编程方式,你也十有八九会弄一个“阻塞”信号量来堵死自己的线程。原因就在于这个语言逻辑表达出来的思路:你偏要让程序等待返回成功或者失败的比较结果。

而真正的异步操作,是一种“一刀两断”式的编程思路,在当前程序并不应该承担负责判断发送成功或者失败的职责,而仅仅是向事件注册一个回调。这才是异步编程思路。 --------------------编程问答-------------------- 程序是需要设置超时的,一条指令如果超过50ms没有收到返回数据,则重发,如果重发3次后仍然没有返回,则认为本次操作失败,50ms和3次都是属性,可以灵活设置。 --------------------编程问答--------------------
引用 9 楼 sp1234 的回复:
另外我还要跟你说一下比较明显的设计问题。(但是这其实属于看上去比较浅显易懂——实则许多人都做不到的问题)

就是你所说的:“接收到数据后怎么和发送指令比较并且返回成功或失败”,这个思路本身就是一个顺序操作思路,而不是异步操作思路。当你的思路以及语言逻辑都是顺序的,那么其实就很难写出高效并发的程序。即使勉强使用异步编程方式,你也十有八九会弄一个“阻塞”信号量来堵死自己的线程。原因就在于这个语言逻……


谢谢,明白,如果用异步编程思路,应该怎么设计超时需要重发3次的功能?
--------------------编程问答--------------------
引用 8 楼 weistar 的回复:
哦,这个Sleep(30),是必须要有的,如果小于30ms,我就收到不硬件的返回数据了,说白了就是等待硬件返回数据的时间,但是如果用Sleep(30),说明每条指令至少耗时30ms,那有可能硬件10ms就返回数据了,我还死等30ms,不就是浪费时间吗?
现在指标要求从开始监测信号到发射出干预信号,一共不超过1s,所以分秒必争,望高人指点。


如果删除Sleep(30)这条语句,那么之后的两条语句执行之后变量buffer中得到什么结果? --------------------编程问答-------------------- “等待硬件返回数据时间”这个说法是错误的。我打一个比方,比如你去大饭店请客吃大餐,你现在的策略是点了菜之后不是上两三个菜就开始招呼大家吃,而是要别人要死等1个小时之后才开始吃(因为你估计一个小时差不多可以上齐所有菜)。这样请客方法,谁陪你吃饭? --------------------编程问答-------------------- 读取数据,“serPort.Read”这类代码才是放在循环之中的。如果它读取了3个字节,我就保存这3个字节。如果接下来又读取2个字节,我就继续保存2个字节。如果接下来有读取10个字节,我就又继续保存10个字节。如果接下来有读取1个字节,这时我发现读取到一条消息的尾部了,于是取出保存的字节组成一个完整的16字节,循环才结束。

读取数据是循环的,Read方法阻塞在那里等待有数据到来就立刻返回。而不是在Read之前弄一个什么死循环。 --------------------编程问答-------------------- 如果删除Sleep(30)这条语句,变量buffer中是空值。
你的例子正好,我的30ms就是这里的1小时,我想问如果所有客人提前半个小时就都到了,我们就应该开饭了,而不是一定1小时后准时开饭啊。
“等待硬件返回数据时间”这个逻辑不对吗?我想要就是硬件的返回数据啊? --------------------编程问答-------------------- 刚才又测试了一下,Read和ReadByte也是阻塞的,我这里消耗是0,为什么你那里CPU会50%呢。 --------------------编程问答--------------------
引用 15 楼 weistar 的回复:
“等待硬件返回数据时间”这个逻辑不对吗?我想要就是硬件的返回数据啊?


是的,不对!

应该循环来读取数据,一旦读取的数据足够多了,就跳出循环。 --------------------编程问答-------------------- 这就相当于上一道菜就吃一道菜,等才上完了立刻也就吃完结帐走人。而不是等1个小时之后才开始吃。 --------------------编程问答-------------------- 谢谢lizhibin11和sp1234,这点我知道了,就是读取数据是循环读取的,读到尾字节时跳出循环,说明该条指令读取完毕,没错,这点是要优化的(用之前的方法都能把数据读完整,所以没在意这个问题,不好意思,一会一定要优化这块)。

你们是不是这个意思:
发送方法是void的,只管发送
接收方法(sp_DataReceived)收到完整的数据后,和发送数据对比,如果正确就告诉上层成功?
--------------------编程问答-------------------- 按照你们的应用,如果就是写入-读取-写入-读取这样具有规律性的话,不用DataReceived,直接在写入之后进入循环读取,读取完毕退出循环。 --------------------编程问答-------------------- DataReceived所用的情况一般是这样的,我举一个例子,比如短信猫,除了发送短信外,还会收到短信,什么时候会收到短信完全不知道,而且又有读取超时设置,就不得不用DataReceived了。 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 如果有这种情况:发送数据下去后,硬件需要2秒(我的意思是有一段时间)才能返回第一个字节,我循环读就什么都读不到,问题是c#中这句话serialport.ReadByte()也不返回异常,就卡在那里,导致没法处理,针对硬件返回有延迟的情况有什么好的建议没?谢谢 --------------------编程问答--------------------
引用 24 楼 weistar 的回复:
如果有这种情况:发送数据下去后,硬件需要2秒(我的意思是有一段时间)才能返回第一个字节,我循环读就什么都读不到,问题是c#中这句话serialport.ReadByte()也不返回异常,就卡在那里,导致没法处理,针对硬件返回有延迟的情况有什么好的建议没?谢谢

返回去看一楼第二三段。
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,