【问题标题】:Application Not Receiving Serial Data from COM Port - C++应用程序未从 COM 端口接收串行数据 - C++
【发布时间】:2011-01-12 22:57:26
【问题描述】:

我的应用程序没有从 COM 端口正确接收数据。这曾经奏效。我不知道发生了什么。我知道正在通过线路发送/接收正确的数据,因为我可以在我的协议分析器上看到它。

PC 进入WAIT_OBJECT_0 + 1 状态,但缓冲区内容始终为零。我知道这很多,但如果有人能指出我做错了什么,我会非常感激。我可以根据要求添加/删除详细信息。谢谢。

编辑:附加信息

我已经能够验证 PC 拨打了ReadFileEx,并且它“成功”了。但是,PC 永远不会进入FileIOCompletionRoutine。有任何想法吗? (我从代码中删除了错误处理以使生活更简单。)此外,从我在 MSDN 网站上阅读的内容来看,FileIOCompletionRoutine 似乎将在其自己的线程中被异步调用。那是对的吗?谢谢。

编辑:最终解决方案

这就是我想出的。显然,这里没有初始化和错误处理代码。我们不能让事情变得太容易。 :)

// Load event handles.
pHandles[0] = s_hSerialPortRxThreadExitEvent;
// OVERLAPPED structure event handle is loaded in loop.

while ( blContinue )
{
    // Wait for a communications event.
    if ( !::WaitCommEvent( s_hSerialPort, &dwEventMask, &s_ov ) )
    {
        if ( ::GetLastError() != ERROR_IO_PENDING )
        {
            blContinue = FALSE;
            continue;
        }
        else if ( ::WaitForSingleObject( pHandles[0], 0 ) == WAIT_OBJECT_0 )
        {
            // The thread-exit event has been signaled.  Get out of here.
            blContinue = FALSE;
            continue;
        }
    }
    else
    {
        // Load OVERLAPPED structure event handle.
        pHandles[1] = s_ov.hEvent;
    }

    if ( dwEventMask & EV_RXCHAR )
    {
        if ( !::ReadFile( s_hSerialPort, pBuf, RX_BUF_SIZE, NULL, &s_ov ) )
        {
            if ( ::GetLastError() == ERROR_IO_PENDING )
            {
                // Wait for events.
                dwObjectWaitState = ::WaitForMultipleObjects( 2, pHandles, FALSE, INFINITE );

                // Switch on event.
                switch ( dwObjectWaitState )
                {
                case WAIT_OBJECT_0:             // thread exit event signaled
                    blContinue = FALSE;
                    continue;

                case WAIT_OBJECT_0 + 1:         // OVERLAPPED structure event signalled
                    // Reset event first to mitigate underrun condition.
                    if ( !::ResetEvent( pHandles[1] ) )
                    {
                        blContinue = FALSE;
                        continue;
                    }

                    // Get the OVERLAPPED result.
                    if ( !::GetOverlappedResult( s_hSerialPort, &s_ov, &dwBytesRead, FALSE ) )
                    {
                        blContinue = FALSE;
                        continue;
                    }
                    break;

                default:                        // Error
                    blContinue = FALSE;
                    continue;
                }
            }
        }
        else if ( !::GetOverlappedResult( s_hSerialPort, &s_ov, &dwBytesRead, FALSE ) )
        {
            blContinue = FALSE;
            continue;
        }

        // If bytes were read...
        if ( dwBytesRead > 0 )
        {
            // Copy received data from local buffer to thread-safe serial port buffer.
            ::EnterCriticalSection( &s_csRxRingBuffer );
            blSuccess = s_pobjRxRingBuffer->Add( pBuf, dwBytesRead, TRUE );
            ::LeaveCriticalSection( &s_csRxRingBuffer );

            if ( !blSuccess )
            {
                blContinue = FALSE;
                continue;
            }

            // Set the received data event.
            if ( !::SetEvent( s_phEventIds[RECEIVE_EVENT] ) )
            {
                blContinue = FALSE;
                continue;
            }
        }
    }

    if ( dwEventMask & EV_TXEMPTY )
    {
        // Set the transmit complete event.
        if ( !::SetEvent( s_phEventIds[TRANSMIT_EVENT] ) )
        {
            blContinue = FALSE;
            continue;
        }
    }

} // end while ( blContinue );

【问题讨论】:

  • 你不应该像这样强制转换函数指针(除非你绝对确定它是一个好的强制转换)。您松散了函数签名检查。
  • 我知道微软已经想出了各种混淆的方法来制作和强制转换指针,但最后一个指针是一个指针是一个指针,一个32位(或64位)表示内存位置地址的原子值。我确保在使用前将其放回原处;在这里(和其他地方)开始的线程可以正常工作。
  • @Jim Fell:每个函数(除了名称和参数)都有一个调用约定(如 _cdecl ,stdcall ,thiscall ),当您使用不同的调用约定转换为指针时,没有捕获该错误的方法。您很可能会遇到某种运行时错误,最终可能会花费数小时,有时甚至是一天才能找到错误。
  • 只是为了简单的 rs232 通信?该代码正在乞求重写
  • 您几乎从不检查您正在调用的函数的结果,还是因为您清理了代码以便更轻松地发布?

标签: c++ serial-port serial-communication overlapped-io


【解决方案1】:

我不知道我是否看到您的代码错误,但我看到您正在设置事件,并等待事件而不告诉 O/S 您在等待什么,您应该先执行 ReadFileEx()当缓冲区中有数据要读取时,告诉 O/S 你正在等待事件,然后你执行 WFSO()/WFMO(),这就是我在我的串行端口库(在接收器线程上)所做的:

do
{
    byteRead = 0;
    readBuffer = 0;
    if(!::ReadFile(this->commHandle,&readBuffer,
                   1,NULL,&this->readOverlapIO))
    {
         if(GetLastError() == ERROR_IO_PENDING)
         {
             if(::WaitForSingleObject(this->readOverlapIO.hEvent,INFINITE) == WAIT_OBJECT_0)
             {
                 if(!::GetOverlappedResult(this->commHandle,&this->readOverlapIO,&byteRead,FALSE))
                 {
                     byteRead = 0;
                 }
             }
         }
    }
    else
    {
         if(!::GetOverlappedResult(this->commHandle,&this->readOverlapIO,&byteRead,FALSE))
         {
              byteRead = 0;
         }
    }
    if(byteRead > 0)
    {
          totalByteRead += this->ringBuffer.push(readBuffer,1);
    }
}while(byteRead);

我这里使用的是使用事件信号的完成事件,如果你愿意,你可以把它改成完成函数。

【讨论】:

  • 嗨,乌雷。我接受了您的回答,因为这最终使我走上了正轨。如您所见,它非常接近我在 OP 中发布的最终解决方案。
【解决方案2】:

另外,根据我在 MSDN 上阅读的内容 网站,看起来像 FileIOCompletionRoutine 会得到 自己异步调用 线。对吗?

不,这是不正确的。完成例程在调用 ReadFileEx 的线程的上下文中被调用。当它准备好运行时,它会排队,直到线程处于“警报等待状态”。当您调用其中一个 Wait* 函数时,通常会发生这种情况。此外,来自ReadFileEx 的 MSDN,它指出:

ReadFileEx 函数忽略 OVERLAPPED 结构的 hEvent 成员。 应用程序可以免费使用 会员为自己的目的在 ReadFileEx 调用的上下文。 ReadFileEx 表示其完成 通过调用或排队读取操作 对完成例程的调用 由 lpCompletionRoutine 指向,所以 它不需要事件句柄。

所以你创建的事件没有效果。此外,在调用 ReadFileEx 之前不会处理读取,因此您必须在等待时反转顺序。您在阅读循环中应该做的事情是这样的(在伪代码中):

while(continue)
{
    ReadFileEx();
    WaitForSingleObject(s_hSerialPortRxThreadExitEvent);
    . . . etc . . .
}

【讨论】:

    【解决方案3】:

    如果你确定某件(应该)已经收到,那么这可能是 &dwWritten 的双重用法。尝试使用两个单独的变量(一个用于 ReadFile,另一个用于 GetOverlappedResult)。 当然要同时检查它们。

    另外,您是否将完全重叠的结构初始化为 0(在将事件分配给它之前)?

    【讨论】:

    • 嗨,埃德温。感谢您的提示。我用最新的代码更新了我的帖子。
    猜你喜欢
    • 1970-01-01
    • 2014-09-20
    • 1970-01-01
    • 1970-01-01
    • 2018-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-18
    相关资源
    最近更新 更多