欢迎来到老马的领地~ 这是“压风式散热底座”发明者的个人网站:) 本人QQ:80524554,用户群1:562279766
C#来做串口通讯项目,目前这还是第一个,以前都用VB6做的,自认为没啥问题,结果还真就出了问题了
具体来说,就是SerialPort对象的Close方法调用时,有时会挂在Close上.
并且程序中打开串口后,有通讯的情况下,不主动关闭串口,而是直接关闭主窗体,在FormClosed事件中再调用Close方法,大概率会挂.
想了一下,这应该是与线程有关,一搜,发现老熟人【wuyazhe】的相关文章,里面也讲到这个问题了.
不过估计这兄弟没搞好格式,代码全挤一起了....我也懒得复制出来看了,大概理解了意思就行,关键点应该就在于关闭串口的时机上.
于是我把我封装的串口类加了两个标志变量,并在Close方法中做了处理,搞定!
相关代码:
简单来说,串口的DataReceived事件在工作时,不要调用Close方法.
而一旦需要Close,那就不要再调用Write方法往串口里写数据了.
于是利用mFunctionOK变量来判断是否正在进行DataReceived事件处理,利用mIsWork变量来决定当前事件处理完毕后是否还要继续发送下一条指令.
在Close之前,先让mIsWork = False,然后等待mFunctionOK = True即可:
至此,随便折腾也不会再挂起了
整个封装好的串口类:
【 点击此处下载cCommPort.cs 】
调用很简单:
除了上面示例中的字符串形式调用,还扩展了一套Byte[]数组方式的接口,大部分情况下足够了.
这个封装类其实就是VB6里曾经做过的串口封装类,只不过处理了一下委托调用而已,所以用着是一样的顺手
***************2022-08-22***************
修正个BUG,把变量写反了


具体来说,就是SerialPort对象的Close方法调用时,有时会挂在Close上.
并且程序中打开串口后,有通讯的情况下,不主动关闭串口,而是直接关闭主窗体,在FormClosed事件中再调用Close方法,大概率会挂.
想了一下,这应该是与线程有关,一搜,发现老熟人【wuyazhe】的相关文章,里面也讲到这个问题了.
不过估计这兄弟没搞好格式,代码全挤一起了....我也懒得复制出来看了,大概理解了意思就行,关键点应该就在于关闭串口的时机上.
于是我把我封装的串口类加了两个标志变量,并在Close方法中做了处理,搞定!
相关代码:
private void oComm_DataReceived(object sender, SerialDataReceivedEventArgs e)
{ //定时器超时法获取串口数据
//有连续数据到来时会连续重置定时器,直到数据停止发送超过一定时长。
mIsInFunction = true;
oFrm.Invoke(actionTimerRecvRestart); //委托调用下面的timerRecvRestart()
mIsInFunction = false;
}
private void timerRecvRestart()
{
oRecvTimer.Enabled = false; //重置定时器
oRecvTimer.Enabled = mIsWork;
}
{ //定时器超时法获取串口数据
//有连续数据到来时会连续重置定时器,直到数据停止发送超过一定时长。
mIsInFunction = true;
oFrm.Invoke(actionTimerRecvRestart); //委托调用下面的timerRecvRestart()
mIsInFunction = false;
}
private void timerRecvRestart()
{
oRecvTimer.Enabled = false; //重置定时器
oRecvTimer.Enabled = mIsWork;
}
简单来说,串口的DataReceived事件在工作时,不要调用Close方法.
而一旦需要Close,那就不要再调用Write方法往串口里写数据了.
于是利用mFunctionOK变量来判断是否正在进行DataReceived事件处理,利用mIsWork变量来决定当前事件处理完毕后是否还要继续发送下一条指令.
在Close之前,先让mIsWork = False,然后等待mFunctionOK = True即可:
public void Close()
{
try
{
mIsWork = false; //先设置这个标志,以防有新数据被写到串口
do
{
Application.DoEvents();
} while (mFunctionOK); //等到DataReceived执行完毕
oComm.Close();
}
catch
{
}
}
{
try
{
mIsWork = false; //先设置这个标志,以防有新数据被写到串口
do
{
Application.DoEvents();
} while (mFunctionOK); //等到DataReceived执行完毕
oComm.Close();
}
catch
{
}
}
至此,随便折腾也不会再挂起了

整个封装好的串口类:
【 点击此处下载cCommPort.cs 】
调用很简单:
cCommPort oComm; //定义对象
oComm = new cCommPort(this); //初始化,需要传入主窗体,以使委托能在窗体所在线程执行
oComm.eventDataRecv += oComm_eventDataRecv; //挂载事件处理函数
oComm.Open("COM1", 115200);
oComm.SendData("test");
private void oElectronicScale_eventDataRecv(string recvData)
{ //这个事件处理函数中就可以在上面传入的this窗体所在的线程中直接工作了
this.Text = recvData;
}
oComm = new cCommPort(this); //初始化,需要传入主窗体,以使委托能在窗体所在线程执行
oComm.eventDataRecv += oComm_eventDataRecv; //挂载事件处理函数
oComm.Open("COM1", 115200);
oComm.SendData("test");
private void oElectronicScale_eventDataRecv(string recvData)
{ //这个事件处理函数中就可以在上面传入的this窗体所在的线程中直接工作了
this.Text = recvData;
}
除了上面示例中的字符串形式调用,还扩展了一套Byte[]数组方式的接口,大部分情况下足够了.
这个封装类其实就是VB6里曾经做过的串口封装类,只不过处理了一下委托调用而已,所以用着是一样的顺手

***************2022-08-22***************
修正个BUG,把变量写反了

添加评论
GB2312 https://www.m5home.com/blog/trackback.php?id=139&encode=gb2312
UTF-8 https://www.m5home.com/blog/trackback.php?id=139&encode=utf-8