欢迎来到老马的领地~ 这是“压风式散热底座”发明者的个人网站:) 本人QQ:80524554,用户群1:562279766
C#来做串口通讯项目,目前这还是第一个,以前都用VB6做的,自认为没啥问题,结果还真就出了问题了smilie55.pngsmilie55.png

具体来说,就是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;
    }

简单来说,串口的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
      {
      }
    }

至此,随便折腾也不会再挂起了cool.gif

整个封装好的串口类:

 点击此处下载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;
}

除了上面示例中的字符串形式调用,还扩展了一套Byte[]数组方式的接口,大部分情况下足够了.

这个封装类其实就是VB6里曾经做过的串口封装类,只不过处理了一下委托调用而已,所以用着是一样的顺手smilie66.png

***************2022-08-22***************

修正个BUG,把变量写反了smilie55.png
添加评论

昵称 *

E-mail