欢迎来到老马的领地~ 这是“压风式散热底座”发明者的个人网站:) 本人QQ:80524554,用户群1:562279766
这几天用SerialPort类做485通讯,发现超时设置有点不太对劲,给了个5ms给ReadTimeout后,调用Read就马上返回了,感觉这个ReadTimeout并不是"两个字节间隔",而是"总时长"了smilie4.png

然后用AccessPort一看,果然,COMMTIMEOUTS结构中ReadTotalTimeoutConstant被设为了5,而需要的ReadIntervalTimeout却是-1smilie64.png

于是搜索了一下设置ReadIntervalTimeout的方法,找到了以下两篇文章:

https://blog.51cto.com/yfsoft/323771

https://stackoverflow.com/questions/6414043/can-i-get-the-device-handle-for-a-serial-port-that-i-am-accessing-using-the-net

第一篇文章确认了SerialPort类确实只设置了ReadTotalTimeoutConstant.

第二篇文章找到了获取SerialPort类【已打开的串口的句柄】的方法.

经组合,成功了!lol.gif

于是就有了以下代码:

    /// <summary>
    /// 打开串口。
    /// </summary>
    /// <param name="commPort">串口号</param>
    /// <param name="BaudRate">波特率</param>
    /// <param name="parity">校验</param>
    /// <param name="DataBits">数据位长度</param>
    /// <param name="stopBits">停止位长度</param>
    /// <returns>是否成功</returns>
    public bool Open(string commPort, int BaudRate = 9600, Parity parity = Parity.None, int DataBits = 8, StopBits stopBits = StopBits.One)
    {
      bool bRet = false;
      COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS();

      if (oComm == null) return false;

      try
      {
        oComm.PortName = commPort;
        oComm.BaudRate = BaudRate;
        oComm.DataBits = DataBits;
        oComm.Parity = parity;
        oComm.StopBits = stopBits;
        oComm.ReadTimeout = 500;
        oComm.Open();

        object stream = typeof(SerialPort).GetField("internalSerialStream", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(oComm);
        var handle = (SafeFileHandle)stream.GetType().GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(stream);
        IntPtr phandle = handle.DangerousGetHandle();     //获取已打开的串口的句柄,以供API使用
        Debug.Print(phandle.ToString());

        GetCommTimeouts(phandle.ToInt32(), ref ctoCommPort);
        ctoCommPort.ReadIntervalTimeout = 15;    //要设置的是它,当接收两个字节间隔时间超过此值(单位ms)后,就超时返回
        ctoCommPort.ReadTotalTimeoutConstant = 500;
        ctoCommPort.ReadTotalTimeoutMultiplier = 500;
        ctoCommPort.WriteTotalTimeoutMultiplier = 0;
        ctoCommPort.WriteTotalTimeoutConstant = 0;
        SetCommTimeouts(phandle.ToInt32(), ref ctoCommPort);
        Debug.Print("SetCommTimeouts: " + GetLastError().ToString());

        bRet = true;
        mIsWorking = true;
      }
      catch
      {
        bRet = false;
        mIsWorking = false;
      }

      return bRet;
    }


这样处理后,就可以在Write数据后,调用Read来阻塞式获取数据,并在接收数据超时后自动返回,当然还要做一下错误处理,大约如下:

try
{
    _recvCount = oComm.Read(mRecvBuff, 0, mRecvBuff.Length);
}
catch (Exception e)
{
    if (e.HResult != -2146233083)    //这个错误码是超时,除此之外都抛出
    {
        throw;
     }
}


在AccessPort中查看Open串口时的参数,发现ReadIntervalTimeout 已经是目标值15了,实际测试效果也OK,大功告成lol.gifsmilie83.pngsmilie83.png
添加评论

昵称 *

E-mail