求助!! 关于C# 串口通信关闭串口时卡住的问题

Arron_0021 2019-01-07 10:38:07
如题,最近要做一个扫描二维码的系统,下位机拍二维码发送给上位机,显示图像然后识别。现在图像是能够正常显示并识别了,但是遇到一个问题——拍完第一个二维码再拍第二个二维码,图像不能正确显示。

一开始打算新建一个按钮的事件,把存放接收数据的数组ReceivedData[i]都归零,但是因为数组是串口函数里的局部变量,所以操作失败了。
然后只能识别完一次图像后关闭串口,再打开。然而关闭串口的时候遇到了卡住的状况, 上网查找相关资料以后,把Invoke改成了BeginInvoke,虽然改完以后不会卡住但是报错,显示“端口被关闭”。请问串口部分的程序应该怎样改才能使关闭串口的时候不出错?



附上程序:
 private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
System.Threading.Thread.Sleep(9000);//延时9000ms等待接收完数据
//this.Invoke就是跨线程访问ui的方法,也是本文的范例
BeginInvoke(new EventHandler(delegate
{
Byte[] ReceivedData = new byte[sp.BytesToRead]; //创建接收字节数组
sp.Read(ReceivedData, 0, ReceivedData.Length); //读取所接收到的数据
string RecvDataText = null;
if (false == isHex)
{
for (int i = 0; i < ReceivedData.Length; i++)
{
RecvDataText += ReceivedData[i];
}
//byte类型转成string类型
RecvDataText = System.Text.Encoding.Default.GetString(ReceivedData);
tbxRecvData.Text += RecvDataText;//更新接收框数据
tbxRecvLength.Text = tbxRecvData.TextLength.ToString();//更新接收框
}
else
{
for (int i = 0; i < ReceivedData.Length; i++)
{

//byte转image部分程序


RecvDataText += (ReceivedData[i].ToString("X2") + " ");

barCodeImg.Image = OvImage;
tbxRecvData.Text += RecvDataText;//更新接收框数据
tbxRecvLength.Text = (tbxRecvData.TextLength / 3).ToString();//更新接收框数据长度

}

sp.DiscardInBuffer(); //丢弃接收缓冲区数据

}));
}






...全文
1967 28 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jedius 2020-11-25
  • 打赏
  • 举报
回复
你那样收有时会有断片的问题。 注意DataReceive事件是另外的一个线程,跟主线程没有关系,所有application.doevent根本有必要也没有用。下面是官方文档原话。 “The DataReceived event is raised on a secondary thread when data is received from the SerialPort object. ” 连接: https://docs.microsoft.com/en-us/dotnet/api/system.io.ports.serialport.datareceived?view=dotnet-plat-ext-5.0 我这有个设备发的包中间会有大概15-18ms的间隔,如果不等待,收到的包就是断的。 用我这个方法,不会有问题。 因为我这个设备自己每200ms会发一大串数据包,设备是这样你改不了,只有改代码。 这里的sleep值不能太大,要不然会收到下一组数据,要看自己的设备去调节。 这里也是参考了HSLcommunication里面的方法,只不过我用的list.
  //Init variables
            string s = ""; //info
            List<byte> ResultData = new List<byte>(); //save received message

            //Read multiple times until meet empty gap
            while (port1.BytesToRead>0)
            {              
                byte[] buffer = new byte[port1.BytesToRead]; //define a buffer
                port1.Read(buffer, 0, port1.BytesToRead); //read data to buffer
                ResultData.AddRange(buffer); //Add buffer to result

                //Auto break when size too long
                if (ResultData.Count>1024)
                {
                    break;
                }

                Thread.Sleep(20);  //Wait to see if more data is on the way
                //limitimer gap is about 15-18 ms, to receive all , use 20 ms, is the safest way.
            }

            //Get final result
            byte[] data = ResultData.ToArray();
Jedius 2020-11-25
  • 打赏
  • 举报
回复
你那样收有时会有断片的问题。 注意DataReceive事件是另外的一个线程,跟主线程没有关系,所有application.doevent根本有必要也没有用。下面是官方文档原话。 “The DataReceived event is raised on a secondary thread when data is received from the SerialPort object. ” 连接: https://docs.microsoft.com/en-us/dotnet/api/system.io.ports.serialport.datareceived?view=dotnet-plat-ext-5.0 我这有个设备发的包中间会有大概15-18ms的间隔,如果不等待,收到的包就是断的。 用我这个方法,不会有问题。 因为我这个设备自己每200ms会发一大串数据包,设备是这样你改不了,只有改代码。 这里的sleep值不能太大,要不然会收到下一组数据,要看自己的设备去调节。 这里也是参考了HSLcommunication里面的方法,只不过我用的list. //Init variables string s = ""; //info List<byte> ResultData = new List<byte>(); //save received message //Read multiple times until meet empty gap while (port1.BytesToRead>0) { byte[] buffer = new byte[port1.BytesToRead]; //define a buffer port1.Read(buffer, 0, port1.BytesToRead); //read data to buffer ResultData.AddRange(buffer); //Add buffer to result //Auto break when size too long if (ResultData.Count>1024) { break; } Thread.Sleep(20); //Wait to see if more data is on the way //limitimer gap is about 15-18 ms, to receive all , use 20 ms, is the safest way. } //Get final result byte[] data = ResultData.ToArray();
wanghui0380 2020-11-25
  • 打赏
  • 举报
回复
同样,严格来说楼主所有问题。不在你们讨论的sleep,invoke上 楼主所有的问题就在他没有逻辑,程序完全靠着巧合和经验参数。 1.逻辑1,关闭了,dispose了。为啥还要操作。你开也好,关也罢。我依赖这个开关量,这是基础逻辑。并不是你们纠结的invoke,什么另外开线程 2.逻辑2,我们和串口通讯,依赖串口通讯协议封包,不依赖sleep。 你触发一次,2次,还是while了几回,sleep多少ms,都不是逻辑,那是巧合 3.逻辑3,事件或消息通知是你的逻辑,而不是一个什么DataReceive[] 的数组。你收了,解析到。通知我去做,我就去做。不是我弄个按钮,说“你做”,我都没收到或者没收全,我做啥做。
wanghui0380 2020-11-25
  • 打赏
  • 举报
回复
为啥我知道他昨天晚上通宵了,因为我的人已经跟我投诉了。每天都是没完没了的通宵一台一台调试。他一个人折腾,所有人都的陪着他折腾。 因为没有任何办法,他不跟你玩技术,只一门心思折腾经验参数,所以所有上层的都得把自己得程序写成“经验参数”的,所以所有人都得陪着他一台一台调整“经验参数”,没办法,那不靠逻辑,不靠技术,就是拼体力,拼运气。运气好,这一批设备一个参数够了,运气不好,这一批设备每台都单独折腾
wanghui0380 2020-11-25
  • 打赏
  • 举报
回复
??1年了,怎么有挖出来了。 而且过了1年了,还在讨论sleep?? 感情sp还是白讲,如果这1年你还在sleep中打转。 我们的建议是,可以考虑放弃自己写,先在github找两个成品使用一下,感受一下别人写的东西。然后在回头看看自己写的。 否则一门心思折腾sleep,那还真是?? 话说你自己说,啊,我这设备300ms,那他那设备10ms怎么办把。 对,我见过一个玩plc的就这么玩的,他从来不跟你讨论技术,他口里只有“经验参数”,ok,我得告诉你,他一年有230天都在外面出差,在现场调整“经验参数”,昨天晚上还在通宵,现场一台设备,一台设备的测试磨合他的“经验参数”
Arron_0021 2019-05-09
  • 打赏
  • 举报
回复
引用 23 楼 U闲程序猿 的回复:
可以用try catch忽略掉
请问是在哪个地方用try catch呢 因为串口事件里面的数组是局部变量 我担心外面没有成功把数据取出来
U闲程序猿 2019-05-09
  • 打赏
  • 举报
回复
可以用try catch忽略掉
Arron_0021 2019-05-09
  • 打赏
  • 举报
回复
问一个后续的问题 之前按照上面说的把处理数据算法分离开的思路把程序修改了一下 sp_DataReceived只接收数据 然后打算先新建一个按钮事件 在里面处理数据还原图像 但是运行的时候出现了这个问题 串口部分的数据好像没有成功传递出去 在外面处理的时候数组的内容是null 请问应该怎样解决呢
  • 打赏
  • 举报
回复
引用 18 楼 Legolas001 的回复:
[quote=引用 15 楼 loveljy_19901114 的回复:]串口关闭再打开这个操作最好不要有吧,还是要一直开的比较好

void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{

//接收数据
string str = "";
do
{
int count = serialPort.BytesToRead;
if (count <= 0)
break;
byte[] readBuffer = new byte[count];

Application.DoEvents();
serialPort.Read(readBuffer, 0, count);
str += System.Text.Encoding.Default.GetString(readBuffer);

} while (serialPort.BytesToRead > 0);
listBox1.Items.Add(str);
}
网上看的一段代码用来保证数据接收完成,不要用Thread.Sleep();
好的 学到了[/quote] 千万别学这个,这也就仅仅比sleep好了一点点,还是同步处理,请使用异步处理
SoulRed 2019-02-12
  • 打赏
  • 举报
回复
我猜BeginInvoke是异步的。所以你要在他前面加上await 或者判断已经连接上了后
良朋 2019-02-12
  • 打赏
  • 举报
回复
图像传送你用串口,UPH跟的上吗?可以在下位机直接解码再传送结果,或者用网口(千兆网卡)通讯。
Arron_0021 2019-02-11
  • 打赏
  • 举报
回复
引用 15 楼 loveljy_19901114 的回复:
串口关闭再打开这个操作最好不要有吧,还是要一直开的比较好

void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{

//接收数据
string str = "";
do
{
int count = serialPort.BytesToRead;
if (count <= 0)
break;
byte[] readBuffer = new byte[count];

Application.DoEvents();
serialPort.Read(readBuffer, 0, count);
str += System.Text.Encoding.Default.GetString(readBuffer);

} while (serialPort.BytesToRead > 0);
listBox1.Items.Add(str);
}
网上看的一段代码用来保证数据接收完成,不要用Thread.Sleep();
好的 学到了👌
Arron_0021 2019-02-11
  • 打赏
  • 举报
回复
好的 我后来自己解决的时候就是加了一个信号灯flag 不过原来的逻辑还有问题 我再改改
zmidl 2019-02-04
  • 打赏
  • 举报
回复
从楼主的描述就知道问题了,不用看你代码了。
串口读取过程中强行关闭串口,你试想一下 地铁还在上下客的过程中直接启动开车是啥场景。你需要在关闭串口前前停止串口的IO 操作。然后再关闭串口,所以你的异步请求做的就是等待IO 操作完成再关闭。但是你关闭了串口,串口还在进行IO操作 当然就异常了,对一个已经关闭的串口如何做IO操作呢?
解决方案如下:
1.每次读取串口前判定是否有请求关闭的Flag
2.当点击关闭串口的按钮时置这个Flag
3.读取过程中如果有Flag变换那么DoEvent()等待当前IO操作做完下一个循环前会通过Flag退出IO操作 随后关闭串口
loveljy_19901114 2019-02-01
  • 打赏
  • 举报
回复
串口关闭再打开这个操作最好不要有吧,还是要一直开的比较好

void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{

//接收数据
string str = "";
do
{
int count = serialPort.BytesToRead;
if (count <= 0)
break;
byte[] readBuffer = new byte[count];

Application.DoEvents();
serialPort.Read(readBuffer, 0, count);
str += System.Text.Encoding.Default.GetString(readBuffer);

} while (serialPort.BytesToRead > 0);
listBox1.Items.Add(str);
}
网上看的一段代码用来保证数据接收完成,不要用Thread.Sleep();
ilikeff8 2019-02-01
  • 打赏
  • 举报
回复
为什么要用sleep,后面调用异步本身不就是未来提高接收效率么,而且DiscardInBuffer要小心用,不要把在处理逻辑时最新发来的有效数据给扔了
Arron_0021 2019-01-31
  • 打赏
  • 举报
回复
引用 10 楼 以专业开发人员为伍 的回复:
如果你看到一些博客,如果事件/异步通讯程序里边写了 Sleep,哪怕是10毫秒,直接扔掉这类垃圾代码。否则学了危害无穷。
好的 谢谢大神
Arron_0021 2019-01-31
  • 打赏
  • 举报
回复
引用 9 楼 以专业开发人员为伍 的回复:
在异步/事件通讯程序中写 Sleep 代码本身就是错误的。其它暂时都不用考虑,先把基本的通讯机制搞清楚,先把 Sleep 删除掉。有数据发来就接收,接受逻辑要处理粘包分包,这是起码的编程技术。你怎么知道要阻塞9000而不是900也不是8000也不是100也不是9900?这个卡死9秒钟的动做完全是胡乱猜测的,而且是无病也要卡死的做法。
这个9秒的时间打算为了收到完整的数据所以加了一个延迟……后来看了之后感觉这样逻辑也不太对 但是之前自己也不太会改…发帖求助之后看了大神们的建议收获很多
  • 打赏
  • 举报
回复
引用 3 楼 likelinsiyuan 的回复:
不要在BeginInvoke中去读取串口接收的数据。
主要还是因为 lz 一上来就写 Sleep(9000),然后把自己搞得背道而驰了。
  • 打赏
  • 举报
回复
如果你看到一些博客,如果事件/异步通讯程序里边写了 Sleep,哪怕是10毫秒,直接扔掉这类垃圾代码。否则学了危害无穷。
加载更多回复(8)

111,093

社区成员

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

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

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