【问题标题】:Adjust parameters of serial port reading调整串口读取参数
【发布时间】: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


【解决方案1】:

我不确定这是否会有所帮助,但是由于您已经很清楚串行设备的输入队列中有多少字节 (stat.cbInQue),因此读取那么多字节可能会有所帮助仅 1 个字节或任意数量的字节(如 50 个):

bool ok = ::ReadFile( handle, buffer_in, stat.cbInQue, &bytes_read, &ov_reader );

当然,您需要确保 buffer_in 具有该字节数的容量,因此您可能需要添加一些其他逻辑以确保没有缓冲区溢出。

此外,由于串行驱动程序和ReadFile() API 严重依赖缓冲来处理接收到的字符,因此您可以使用 WaitCommEvent()SetCommMask() API。

【讨论】:

  • 好主意,我会试试这个(使用 stat.cbInQue)
  • 好的,我试过了,看起来效果很好。所以我会作为accepter的答案检查,甚至其他人都有非常有价值的信息。
【解决方案2】:

“较大的框架”有多大?当您一次调用一个字节 ReadFile 时,显然需要很长时间才能完成整个帧,由于调用开销,可能比发送帧本身所需的时间更长。

一些替代方案:

  1. 设备是否会随时发送帧?如果有机会设计协议的两端,是否可以切换到命令/响应式的通信方式?

  2. 你能从数据包的开头预测数据包其余部分的字符数吗?如果是这样,您可以在 read 函数中构建一个状态机。您可以一次轮询一个字节,然后当您检测到数据包开始时,在一次调用中读取大部分其余数据包,然后一次切换回一个字节。

  3. 可以使用 DSR/CTS 控制时序吗?

一般来说,从串行端口读取函数中读取整个数据包非常困难。通常的过程是读取一堆字符并将它们传递给更高级别的协议解析。听起来您必须进行比该方法允许的更严格的时序控制。祝你好运...

【讨论】:

  • 较大的帧我的意思是大约 90 个字节...我知道这不是很大... 1. 设备定期发送其状态 2. 第二个字节包含帧 3 的长度。我不知道 :( 我会调查的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-15
  • 2013-09-24
  • 1970-01-01
相关资源
最近更新 更多