应用程序未从 COM 端口接收串行数据 - C++

发布于 2024-10-11 09:58:31 字数 3826 浏览 2 评论 0原文

我的应用程序无法正确从 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 );

My application is not properly receiving data from the COM port. This used to work. I don't know what happened. I know that the proper data is being sent/received over the line because I can see it on my protocol analyzer.

The PC gets into the WAIT_OBJECT_0 + 1 state, but the buffer contents are always zero. I know this is a lot, but if anyone can point out what I'm doing wrong, I'd really appreciate it. I can add/remove details as requested. Thanks.

EDIT: Additional Information

I have been able to verify that the PC makes the call to ReadFileEx, and it "succeeds." However, the PC never gets into FileIOCompletionRoutine. Any ideas? (I removed the error-handling from the code to make life simpler.) Also, from what I read on the MSDN website, it looks like FileIOCompletionRoutine will get called asynchronously in its own thread. Is that correct? Thanks.

EDIT: Final Solution

This is what I came up with. Obviously, the initialization and error-handling code is not here. We can't make things too easy. :)

// 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 );

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

洛阳烟雨空心柳 2024-10-18 09:58:31

我不知道我是否看到你的代码错误,但我看到你正在设置事件,并等待事件而不告诉O/S你在等什么,你应该首先执行ReadFileEx()来告诉O/当缓冲区中有数据要读取时,您正在等待事件,然后执行 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);

我在这里使用事件信号来使用完成事件,如果您愿意,可以将其更改为完成功能。

I don't know if I see your code wrong, but I see that you are setting up event, and waiting for the event without telling the O/S what are you waiting for, you should do ReadFileEx() first to tell O/S you are waiting for event when there's data in the buffer to read, then you do WFSO()/WFMO(), this what I do on my Serial port library (on receiver thread):

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);

I use completion event using event signal here, you can change it to completion function if you wanted to.

心凉 2024-10-18 09:58:31

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

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

ReadFileEx 函数忽略
OVERLAPPED 结构的 hEvent 成员。
一个应用程序可以免费使用
会员为了自己的目的
ReadFileEx 调用的上下文。
ReadFileEx 发出信号完成
通过调用或排队的方式进行读操作
调用完成例程
lpCompletionRoutine 指向,所以
它不需要事件句柄。

所以你创建的事件没有效果。此外,在调用 ReadFileEx 之前不会处理读取,因此您必须在等待时颠倒顺序。您应该在阅读循环中执行如下操作(以伪代码形式):

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

Also, from what I read on the MSDN
website, it looks like
FileIOCompletionRoutine will get
called asynchronously in its own
thread. Is that correct?

No, that is not correct. The completion routine gets called in the context of the thread in which it the ReadFileEx called is made. When it is ready to run, it is queued until the thread is in an "alertable wait state". That typically happens when you call one of the Wait* functions. Furthermore, from the MSDN for ReadFileEx, it states:

The ReadFileEx function ignores the
OVERLAPPED structure's hEvent member.
An application is free to use that
member for its own purposes in the
context of a ReadFileEx call.
ReadFileEx signals completion of its
read operation by calling, or queuing
a call to, the completion routine
pointed to by lpCompletionRoutine, so
it does not need an event handle.

So the event you have created has no effect. Also, the reads will not be processed until ReadFileEx is called, so you have to reverse the order with your wait. What you should be doing in your reading loop is something like this (in Pseudocode):

while(continue)
{
    ReadFileEx();
    WaitForSingleObject(s_hSerialPortRxThreadExitEvent);
    . . . etc . . .
}
友谊不毕业 2024-10-18 09:58:31

如果您确定(应该)已收到某些内容,那么可能是 &dwWritten 的双重使用。尝试使用两个单独的变量(一个用于 ReadFile,另一个用于 GetOverlappedResult)。
当然检查它们两个。

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

if youre sure that something (should) have been received ,then may it is the double usage of &dwWritten. Try using two separate variables (one for ReadFile and another for GetOverlappedResult).
Check both of them of course.

Also ,Have you initialized the full overlapped structure to 0's (beffore assigning the event to it)?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文