我正面临一个关于win32下串行通信的特殊问题。 我正在与设备通信只能接受尚未通信的帧。所以我必须找到一个有效的框架然后发送我的请求。
我开发了一个名为Serial的类,它处理串口(open,close,read,write)的基本操作,然后在循环读写函数内调用Thread。
线程循环
//Device is an object of class Serial
while( device->isOpen() && !terminate )
{
unsigned int readed = 0;
unsigned long error = ERROR_SUCCESS;
unsigned char* data = device->read( &readed, &error );
if( error==ERROR_SUCCESS )
{
//If data received, deliver to upper level
if( readed>0 )
{
QByteArray output( (const char*)data, (signed int)readed );
emit dataArrived( output, readed );
}
}
else
{
//unrelated stuff
}
//Here I manage the writting issue
//Only when nothing is received, and Upper layer wants to send a frame
//(Upper layer only will mark as something to send when it detects a valid frame)
if( readed==0 )
{
out_lock.lock();
//If something to send...
if( something_to_send > 0 )
{
if( device->write( output_buffer, output_size, &error ) )
{ //things...
}
}
}
}
线程基本上保持读取,并且当没有接收到任何内容时,查看是否有人发信号通知发送帧(这意味着刚刚接收到有效帧)。 发生这种情况时,它会通过串口写入帧。
这是我的问题。
在Serial :: read()函数内部:
我使用重叠的阅读方式:
::ClearCommError( handle, &dwErrors, &stat);
if( stat.cbInQue )
{
//If there's something to read, read it, please note the bytes to read parameter, here 1.
bool ok = ::ReadFile( handle, buffer_in, 1, &bytes_read, &ov_reader );
if( !ok )
{
DWORD _error = ::GetLastError();
if( _error == ERROR_IO_PENDING )
{
DWORD result = ::WaitForMultipleObjects( 2, waiters, FALSE,INFINITE );
switch( result )
{ //Eventshutdown
case WAIT_OBJECT_0: /*code omitted*/break;
case WAIT_OBJECT_0+1: ok = ::GetOverlappedResult( handle, &ov_reader, &bytes_read, true );
//check ok value omitted
break;
}
}
}
}
if( bytes_read>0 )
{
*size = bytes_read;
}
这里开始我的问题。
当设备向我发送小帧(大约30个字节)时,一切正常,但是当发送较大的帧时,代码无法在帧之间找到任何空闲时间,导致线程永远不能发送任何帧,因为readed
永远不会是0。
如果我增加read()函数内读取的字节数,则无法检测设备何时“侦听”: bool ok = :: ReadFile(handle,buffer_in,50,& bytes_read,& ov_reader); 发生这种情况是因为我的应用程序可以接收帧的结尾以及下一帧的开头。这种行为很常见。
另一方面,如果我通过WaitForMultipleObjects
函数中的有效超时更改INFINITE参数,则会丢失数据。
所以我的问题基本上是......我做错了什么?为什么每次读取1个字节时我都没有找到任何空闲时间发送自己的帧?
谢谢
答案 0 :(得分:2)
我不确定这是否有帮助,但是因为你已经知道串口设备的输入队列(stat.cbInQue
)中有多少字节,这可能会有助于读取字节而不是只有1个字节或任意数量的字节(如50):
bool ok = ::ReadFile( handle, buffer_in, stat.cbInQue, &bytes_read, &ov_reader );
当然,您需要确保buffer_in
具有该字节数的容量,因此可能需要添加一些其他逻辑以确保没有缓冲区溢出。
此外,由于串行驱动程序和ReadFile()
API在很大程度上依赖于缓冲来处理接收到的字符,因此您可能能够更准确地指示何时收到(并且未收到)字符使用
WaitCommEvent()
和SetCommMask()
API。
答案 1 :(得分:1)
“大框架”有多大?当您一次调用ReadFile
一个字节时,显然需要很长时间才能完成整个帧,这可能比由于调用开销而发送帧本身所花费的时间更长。
一些替代方案:
设备是否随时发送帧?如果您有机会设计协议的两端,是否可以切换到命令/响应通信方式?
您可以从数据包的开头预测其余数据包中的字符数吗?如果是这样,您可以在read
函数中构建状态机。您可以一次轮询一个字节,然后当您检测到数据包的开始时,在一次调用中读取大部分数据包,然后一次切换回一个字节。
您可以使用DSR / CTS来控制时间吗?
通常,在串行端口读取功能中读取整个数据包确实很困难。通常的过程是读取一堆字符并将它们传递到更高级别以进行协议解析。听起来你必须拥有比该方法允许的更严格的时序控制。祝你好运......