欢迎来到老马的领地~ 这是“压风式散热底座”发明者的个人网站:) 本人QQ:80524554,用户群1:562279766
这几天用SerialPort类做485通讯,发现超时设置有点不太对劲,给了个5ms给ReadTimeout后,调用Read就马上返回了,感觉这个ReadTimeout并不是"两个字节间隔",而是"总时长"了
然后用AccessPort一看,果然,COMMTIMEOUTS结构中ReadTotalTimeoutConstant被设为了5,而需要的ReadIntervalTimeout却是-1
于是搜索了一下设置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类【已打开的串口的句柄】的方法.
经组合,成功了!
于是就有了以下代码:
这样处理后,就可以在Write数据后,调用Read来阻塞式获取数据,并在接收数据超时后自动返回,当然还要做一下错误处理,大约如下:
在AccessPort中查看Open串口时的参数,发现ReadIntervalTimeout 已经是目标值15了,实际测试效果也OK,大功告成


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

于是搜索了一下设置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类【已打开的串口的句柄】的方法.
经组合,成功了!

于是就有了以下代码:
/// <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;
}
/// 打开串口。
/// </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;
}
}
{
_recvCount = oComm.Read(mRecvBuff, 0, mRecvBuff.Length);
}
catch (Exception e)
{
if (e.HResult != -2146233083) //这个错误码是超时,除此之外都抛出
{
throw;
}
}
在AccessPort中查看Open串口时的参数,发现ReadIntervalTimeout 已经是目标值15了,实际测试效果也OK,大功告成



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