对串口通讯中的COMMTIMEOUTS参数一直很疑惑。到底是怎么起作用的呢?
typedef struct _COMMTIMEOUTS { DWORD ReadIntervalTimeout; DWORD ReadTotalTimeoutMultiplier; DWORD ReadTotalTimeoutConstant; DWORD WriteTotalTimeoutMultiplier; DWORD WriteTotalTimeoutConstant; } COMMTIMEOUTS, *LPCOMMTIMEOUTS;
曾经写一个简单的串口通讯程序,其中的过程是发60个字节数据到设备,然后过一段时间后大概是1到2秒的时间,会返回两次各60字节的数据,结果因为COMMTIMEOUTS的作用不清楚,试了好久。现总结一下。
一、针对同步通讯专门试了一下其效用。
COMMTIMEOUTS CommTimeOuts;
1 WriteFile(m_hComm, cBufSend, nBufLen , &dwNumBytesWritten, 0);
写操作时间有两种设置模式:
如下设置,写操作时,不考虑时间限制,直到写完成才返回
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
如下设置,即a,b中至少有一个大于0,此时以 b+ a*(发送字节数 nBufLen ) 为时间限制,超时则返回
CommTimeOuts.WriteTotalTimeoutMultiplier = a;
CommTimeOuts.WriteTotalTimeoutConstant = b;
实际试验中,可能由于我的写的数据量太少,所以发送很快,两种模式下都很快返回了,看不出什么区别。而且串口的另一端没有设备连接时也可以写数据,所以写操作设置可以随意一些。
2 ReadFile(m_hComm, cBufSend, nBufLen , &dwNumBytesReaded, 0);
CommTimeOuts.ReadIntervalTimeout 与 (CommTimeOuts.ReadTotalTimeoutMultiplier 、CommTimeOuts.ReadTotalTimeoutConstant)
分两组共同起影响。
2.1 CommTimeOuts.ReadIntervalTimeout 管理单个字符的读时限。
CommTimeOuts.ReadIntervalTimeout=0; 表示对单个字符的读不加时限。
CommTimeOuts.ReadIntervalTimeout=a; (a>0) 表示 从读操作(即ReadFile)启动开始每a毫秒内能读到下一个字符(包括第一个字符),则OK,否则返回。
2.2 CommTimeOuts.ReadTotalTimeoutMultiplier 和 CommTimeOuts.ReadTotalTimeoutConstant 管理总的读数时限
如下设置时表示不加限制
CommTimeOuts.ReadTotalTimeoutMultiplier=0;
CommTimeOuts.ReadTotalTimeoutConstant=0;
如下设置时, b,c中至少有一个大于0,c+ b*(读取字节数nBufLen ) 为总时限,超过则返回
CommTimeOuts.ReadTotalTimeoutMultiplier=b;
CommTimeOuts.ReadTotalTimeoutConstant=c;
2.3 MSDN中提到如下的时限设置,无论是否读到数据,读操作立即返回,读缓冲区的已有内容需读取返回
CommTimeOuts.ReadIntervalTimeout= MAXDWORD ;
CommTimeOuts.ReadTotalTimeoutMultiplier=0;
CommTimeOuts.ReadTotalTimeoutConstant=0;
2.4 注意:如下设置时,很可能阻塞,建议不使用
CommTimeOuts.ReadIntervalTimeout=0;
CommTimeOuts.ReadTotalTimeoutMultiplier=0;
CommTimeOuts.ReadTotalTimeoutConstant=0;
3 在以上都清楚的情况下,进行如下设置就很快成功了
CommTimeOuts.ReadIntervalTimeout=0; //对单个字符不设限制,无限等待
CommTimeOuts.ReadTotalTimeoutMultiplier=0;
CommTimeOuts.ReadTotalTimeoutConstant=5000; //限制总的时限不超过5秒, 主要考虑到设备返回数据没那么快, 且防止永久阻塞
CommTimeOuts.WriteTotalTimeoutMultiplier = 0; //以下两个设置在使用没发现太大效果
CommTimeOuts.WriteTotalTimeoutConstant = 1000;
二、串口异步通讯时,时限设置的影响
在异步通讯下,时限设置也是有影响的。
试验中,曾串口异步通讯中如下设置
CommTimeOuts.ReadIntervalTimeout = 100;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 250;
读串口内容时,如下
if( ReadFile(m_hComm, (LPVOID)buf, bufLen, &dwReadLen, &m_hOLP) )
{
return true;
}
else
{
if(GetLastError()==ERROR_IO_PENDING)
{
if(WaitForSingleObject(m_hWRevent,5000)==WAIT_OBJECT_0)
return true; //此处应该是
}
}
上面编写的目的是希望 WaitForSingleObject(m_hWRevent,5000) 中 在读到 bufLen 个数据后m_hWRevent 才变为激活状态,才返回,但实际是很快返回,而 buf 中的内容仍然是初始状态。
由于我当时并没有意识到是时限的问题,我就换了个函数,如下
if(ReadFile(m_hComm, (LPVOID)buf, bufLen, &dwReadLen,&m_hOLP))
{
return true;
}
else
{
if(GetLastError()==ERROR_IO_PENDING)
{
if(GetOverlappedResult(m_hComm,&m_hOLP,&dwReadLen,TRUE)
&& dwReadLen==bufLen)
{
return true;
}
return true;
}
}
因为我看到MSDN说 GetOverlappedResult 函数在第四个参数设置为TRUE后,会产生阻塞效果,直接异步操作完成,但实际运行时,仍然是GetOverlappedResult很快就返回了,且 buf 中并未读取到所需的值。
最后没办法,在不太了解的情况下,我尝试如下,终于可以得到正确结果了
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
三、 综上所述,我得出的结果是,COMMTIMEOUTS 在同步或异步通讯时都是起效果的,只是产生效果的地方不一样。
在同步通讯中,对ReadFile和WriteFile的何时完成返回产生作用。
在异步通讯中,Read和WriteFile是立即返回的,当返回值时为TRUE时,当然就不提了,但当返回FALSE且 GetLastError()==ERROR_IO_PENDING 时,就对异步操作相关事件的状态起影响。
我认为假设同步通讯时,读写根据时限设置应该返回了,相应的在异步通讯状态下,读写的事件就应该变为激活状态了。
当事件激活后 WaitForSingleObject(m_hWRevent,5000)==WAIT_OBJECT_0 此时就为真了
而 GetOverlappedResult(m_hComm,&m_hOLP,&dwReadLen,TRUE) 的第四个参数为TRUE时,会等待,但当事件状态为激活后,也就会返回。