【发布时间】:2010-03-16 21:18:24
【问题描述】:
我正面临一个与 win32 下的串行通信有关的特殊问题。 我正在与设备通信时只能接受尚未通信的帧。所以我必须找到一个有效的框架,然后立即发送我的请求。
我开发了一个名为 Serial 的类,它处理串行端口上的基本操作(打开、关闭、读取、写入),然后在循环读取和写入函数中调用线程。
线程循环
//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 个字节时我都没有空闲时间来发送自己的帧?
谢谢
【问题讨论】:
-
你有一个更大的问题,这里有一场不可避免的比赛。您可能会在设备开始发送之前决定设备没有发送一微秒。你无法完成这项工作。
-
@nobugz 提出了一个非常非常重要的观点!您是否假设状态数据包之间存在长时间的停顿?
-
虽然这种设备通信不是通用的,但也不是闻所未闻。协议通常要求的是,如果您开始发送并且设备也启动,您将丢失并且必须停止,处理设备正在发送的任何内容然后重试。这是一种穷人在 RS232 链路上的碰撞检测。这很痛苦,最好在 UART 驱动程序级别处理,但这在 Windows 上可能不是一个现实的选择。
-
@mtrw 设备每 150 毫秒发送一个帧。这是长时间的停顿吗?
-
如果您假设每个字符 9 位(8 个数据位,1 个停止位,无奇偶校验)和每个数据包 90 个字符,那就是 180 位。在 19200 位/秒(例如)下,这将需要大约 10 毫秒。因此,对于这些特定数字,数据包之间的 150 毫秒似乎没问题。
标签: c++ windows serial-port