⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 win32port.cpp

📁 DOS下采用中断接收数据的串口通讯的例子,很难找到的好东西!
💻 CPP
📖 第 1 页 / 共 5 页
字号:
                                                   INFINITE );
             //
            // We can return from the wait call with one of four possible
            // results. 0-3 mean that one of the event handles in the 
            // array defined above was set by some process. The other is
            // a timeout. If you are nervous about waiting forever, you can
            // put a 10 or 20 second timeout on this wait, and print a TRACE
            // message every time the wait times out. This gives you a little
            // bit of a warm feeling that lets you know things are really 
            // working.
            //
            switch ( result ) {
            // If the first event handle in the array was set, it means
            // the main thread of the program wants to close the port.
            // It sets the kill event, and we in turn set the done flag.
            // We will then exit from the bottom of the loop.
            case 0 : // kill thread event
                done = true; 
                break;  
            // This case is selected if an overlapped read of data has
            // signalled that it is complete. If the ReadFile() call
            // completed because it passed in some data, we stuff that
            // data into the RX queue and call the notification function
            // to alert the user's process.
            case 1 : 
                if ( GetOverlappedResult( port->m_hPort, 
                                          &AsyncReadInfo, 
                                          &dwBytesRead, 
                                          FALSE ) ) 
                {      // read completed successfully
                    if ( dwBytesRead ) {
                        port->m_RxQueue.Insert( read_buffer, dwBytesRead );
                        port->RxNotify( dwBytesRead );
                    }
                }
                // Since the last ReadFile() operation completed, we are
                // no longer reading data. We set this flag to false so 
                // that we can kick off a new read at the top of the loop.
                port->m_bInputThreadReading = false;
                break;
            // When we reach case 2, it means that the WaitCommEvent()
            // call completed, which means that one of the line status
            // or modem status events has been triggered. We don't 
            // know which one it is, so we call the handler for both
            // possibilities, allowing them to decide if something has
            // happened, and who to notify about it. 
            case 2 : { /* Status event */ 
                DWORD dwOverlappedResult;
                if ( GetOverlappedResult( port->m_hPort, 
                                          &AsyncStatusInfo, 
                                          &dwOverlappedResult, 
                                          FALSE ) ) 
                {
                    port->check_modem_status( false, dwCommEvent );
                    port->clear_error();
                }
                // Clear this flag so that a new call to WaitCommEvent()
                // can be made at the top of the loop.
                waiting_on_status = false;
                break;
            }
            // When we reach case 3, it means that another thread read
            // some data from the RX queue, opening up some room, and
            // noticed that we weren't actively reading data. When this
            // happens, we need to wake up anc start a new call to
            // ReadFile() so that we can fill up all that empty 
            // space in the RX queue.
            case 3 :
                break;
            default :
                assert( false );
            }
        }
    }
    //
    // When the input thread has decided to exit, it first kills the
    // two events it created, then exits via a return statement.
    CloseHandle( AsyncReadInfo.hEvent );
    CloseHandle( AsyncStatusInfo.hEvent );
}

//
// The output thread is very similar in structure to the input thread,
// with a few notable differences. Instead of including the wait
// for output data as part of its main loop, the output thread calls
// a subroutine to actually perform the data output. And that output
// function runs to completion, so we will never wait for output to
// be complete in the main body of the thread shown here. It would
// probably be a useful modification of this thread to change it so
// that pending output is waited for in the main thread. 
//
void Win32Port::OutputThread(void * arglist)
{
    //
    // As was the case for the input thread, we get a pointer to the
    // Win32Port object passed to us from the calling routine. We just
    // have to cast the pointer, then we have full access to all members
    // ofthe port's structure.
    //
    Win32Port *port = (Win32Port *) arglist;
    // The array of handles I wait for in the main loop has one
    // fewer element than the same array in the input thread.
    // We're only waiting for three things here: a request to send
    // more data via WriteFile(), a request to kill the thread, or
    // a request to send a break.
    HANDLE handles[ 3 ] = { port->m_hKillOutputThreadEvent,
                            port->m_hWriteRequestEvent,
                            port->m_hBreakRequestEvent };
    port->TxNotify();
    for ( bool done = false ; !done ; ) {
        switch ( WaitForMultipleObjects( 3, handles, FALSE, INFINITE ) ) {
        // 
        // There are three possible returns from the call that waits
        // for something to happens: the three events and a timeout.
        // The first case means that a thread has attempted to close
        // the port, which results in the kill thread event being
        // set. When that's the case, we modify the control flag for
        // the loop so that we'll all out of the loop when we reach
        // the bottom.
        //
        case 0 : //m_hKillOutputThreadEvent
            done = true; 
            break;
        //
        // Much of the time this loop will be sitting here doing nothing.
        // When an output request comes through, the appropriate event
        // is set and we end up here. We then call the output worker
        // routine to actually dump the output through the serial port.
        //
        case 1 : //m_hWriteRequestEvent
            done = port->output_worker();
            break;
        //
        // If the break event is set, it means we are being requested
        // to send a break out through the given port. The duration
        // of the break has already been set in a member of the object,
        // so all we have to do here is make it happen. Since this is
        // a background thread with no GUI, it's safe to just sit in
        // a sleep call for the duration of the break.
        //
        case 2 : //m_hBreakRequestEvent
            SetCommBreak( port->m_hPort );
            SleepEx( port->m_iBreakDuration, FALSE );
            ClearCommBreak( port->m_hPort );
            break;
        //
        // This can only be bad!
        //
        default :
            assert( false );
            break;
        }
    }
}

//
// When a request comes in to transmit some data, this routine is called.
// It starts things up by calling WriteFile(), then waits for the
// asynchronous I/O to complete. Note that while it is waiting for
// WriteFile() to complete it can also be notified of a kill thread
// event, in which case it returns immediately.
//

bool Win32Port::output_worker()
{
    OVERLAPPED AsyncWriteInfo = { 0 };
    AsyncWriteInfo.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    assert( AsyncWriteInfo.hEvent );
    HANDLE handles[ 2 ] = { m_hKillOutputThreadEvent,
                            AsyncWriteInfo.hEvent };
    bool killed = false;
    for ( bool done = false ; ; !done ) {
        //
        // First get as much data from the output buffer as we can
        //
        char data[ 500 ];
        int count = m_TxQueue.Extract( data, 500 );
        if ( count == 0 ) {
            TxNotify();
            break;
        }
        //
        // Now we enter the transmit loop, where the data will actually
        // be sent out the serial port.
        //
        DWORD result_count;
        if ( !WriteFile( m_hPort, 
                         data, 
                         count, 
                         &result_count, 
                         &AsyncWriteInfo ) ) {
            //
            // WriteFile() returned an error. If the error tells us that the I/O
            // operation didn't complete, that's okay.
            //
            if ( GetLastError() == ERROR_IO_PENDING ) {
                switch ( WaitForMultipleObjects( 2, handles, FALSE, INFINITE ) ) {
                //
                // Case 0 is the thread kill event, we need to give this up. Since
                // the event is cleared when I detect it, I have to reset it
                //
                case 0 : //m_hKillOutputThreadEvent
                    done = true; 
                    killed = true;
                    PurgeComm( m_hPort, PURGE_TXABORT ); 
                    break;
                //
                // Case 1 means the WriteFile routine signaled completion. 
                // We're not out of the woods completely, there are a 
                // few errors we have to check.
                //
                case 1 : //AsyncWriteInfo.hEvent
                    // The overlapped result can show that the write
                    // event completed, or it stopped for some other
                    // reason. If it is some other reason we exit the
                    // loop immediately. Otherwise we will pass through
                    // the loop and kick off another write. Note that
                    // a TXFlush() call creates an error here, which
                    // we ignore.
                    if ( !GetOverlappedResult( m_hPort, 
                                               &AsyncWriteInfo, 
                                               &result_count, 
                                               FALSE ) ||
                          result_count != count ) {
                        if ( GetLastError() == ERROR_IO_PENDING )
                            clear_error();
                        else
                            translate_last_error();
                        done = true;                        
                    } 
                    break;
                //
                // We had better not ever land here
                //
                default :
                    assert( false );
                }
            } else {
              translate_last_error();
              done = true;                        
            }
        } else {
            //
            // If we fall through to this point, it means that WriteFile() 
            // returned immediately. If it did, it means that we were able
            // to send all of the characters requested in the call. If 
            // we get here and all the characters weren't send, 
            // something bad happened.
            //
            if ( result_count != count ) {
                translate_last_error();
                done = true;
            }
        }
                         
    }
    //
    // On the way out, close the event handle
    //
    CloseHandle( AsyncWriteInfo.hEvent );
    return killed;
}

//
// When an asynchronous event is processed from the call to 
// WaitCommEvent(), we have to call this guy to determine what
// happened. We check each of the modemstatus lines to see who
// changed, then call the notification functions to let the
// calling process know what happened.
//
void Win32Port::check_modem_status(bool first_time, DWORD event_mask )
{
    //
    // There shouldn't be anything to prevent us from reading
    // the input lines. If an error occurs, it is bad.
    ///
    if ( !GetCommModemStatus( m_hPort, &m_dwModemStatus ) )
        assert( false );
    //
    // The first_time flag is set one time when this guy is 
    // called to force the notification functions to be called.
    // Forcing this to happen means that an application can be
    // sure that it will can use the notification functions to
    // determine status.
    //
    if ( first_time ) //report everything
    {
        CtsNotify( ( MS_CTS_ON & m_dwModemStatus ) != 0 );
        DsrNotify( ( MS_DSR_ON & m_dwModemStatus ) != 0 );
        CdNotify( ( MS_RLSD_ON & m_dwModemStatus ) != 0 );
        RiNotify( 0 );
    } else { //Only report events
        //
        // If it isn't the first time, we only send notification for
        // events that actually occured in the event that caused
        // this function to be invoked/
        //
        if ( event_mask & EV_CTS )
            CtsNotify( ( MS_CTS_ON & m_dwModemStatus ) != 0 );
        if ( event_mask & EV_DSR )
            DsrNotify( ( MS_DSR_ON & m_dwModemStatus ) != 0 );
        if ( event_mask & EV_RLSD )
            CdNotify( ( MS_RLSD_ON & m_dwModemStatus ) != 0 );
        //
        // Win95/98 *always* reports an RI event if RI is high when any
        // other line changes. This is not really a good thing. All
        // I'm interested is seeing EV_RINGTE, so I only report an
        // event if RI is low
        //
        if ( event_mask & ( EV_RING | EV_RINGTE ) )
            if ( ( MS_RING_ON & m_dwModemStatus ) == 0 )
            RiNotify( 0 );
    }
}

// EOF Win32Port.cpp

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -