我遇到了一个与 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...
}
}
}
}
Thread 基本上一直在读取,当没有收到任何内容时,查看是否有人发出发送帧的信号(这意味着刚刚收到有效帧)。 发生这种情况时,它会通过串行端口写入帧。
我的问题来了。
在 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 字节)时,一切正常,但是当发送大帧时,代码无法在帧之间找到任何空闲时间,导致线程永远无法发送任何帧,因为 read
永远不会为 0。
如果我在 read() 函数中增加要读取的字节数,则会失去检测设备何时“监听”的能力: bool ok =::ReadFile(handle, buffer_in, 50, &bytes_read, &ov_reader ); 发生这种情况是因为我的应用程序可以接收到一帧的结尾以及下一帧的开始。这种行为很常见。
另一方面,如果我在 WaitForMultipleObjects
函数中通过有效超时更改 INFINITE 参数,我将丢失数据。
所以我的问题基本上是......我做错了什么?为什么每次读取 1 个字节时我找不到任何空闲时间来发送我自己的帧?
谢谢
最佳答案
我不确定这是否有帮助,但由于您已经很清楚串行设备的输入队列 (stat.cbInQue
) 中有多少字节,也许它会有所帮助读取那么多字节而不是仅仅 1 个字节或任意数量的字节(如 50):
bool ok = ::ReadFile( handle, buffer_in, stat.cbInQue, &bytes_read, &ov_reader );
当然,您需要确保 buffer_in
具有容纳该字节数的容量,因此您可能需要添加一些其他逻辑以确保没有缓冲区溢出。
此外,由于串行驱动程序和 ReadFile()
API 在很大程度上依赖于缓冲来处理接收到的字符,因此您可能能够更准确地指示何时接收到(和未接收到)字符使用
WaitCommEvent()
和 SetCommMask()
API。
关于c++ - 调整串口读取参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2458232/