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

📄 win32port.cpp

📁 DOS下采用中断接收数据的串口通讯的例子,很难找到的好东西!
💻 CPP
📖 第 1 页 / 共 5 页
字号:
// different types of handshaking, plus the settings for the desired
// output of DTR and RTS. Once all of these things are punched into
// the DCB, a call to SetCommState() will take care of updating
// the physical device.
//
// There are a lot of things that can go wrong when attempting to set
// the port. We don't consider any of these things to be fatal errors,
// we simply pass the error back to the calling routine. However, the
// calling routine might decide to treat it as a fatal error. The
// constructor does this, treating an error at this point as fatal.
//
// Note also that this routine relies heavily on the DCB support class.
// It takes care of writing the correct values to its members by means
// of a bunch of accessor functions.
//

RS232Error Win32Port::write_settings()
{
    RS232Error error = RS232_SUCCESS;
    m_Dcb.SetBaudRate( settings.BaudRate );
    m_Dcb.SetParity( settings.Parity, error );
    m_Dcb.SetWordLength( settings.WordLength, error );
    m_Dcb.SetStopBits( settings.StopBits, error );
    //
    // Even though we think that we're setting up DTR and RTS,
    // we might not actually be pulling it off. If one of the
    // two hardware handshaking protocols is enabled, it will
    // wipe out the DCB setting for the corresponding control
    // line and change it to use handshaking instead.
    //
    m_Dcb.SetDtr( settings.Dtr ); 
    m_Dcb.SetRts( settings.Rts );
    m_Dcb.SetXonXoff( settings.XonXoff );
    m_Dcb.SetRtsCts( settings.RtsCts );
    m_Dcb.SetDtrDsr( settings.DtrDsr );
    SetCommState( m_hPort, &m_Dcb );
    if ( GetLastError() != 0 ) {
        if ( GetLastError() == ERROR_INVALID_HANDLE )
            return (RS232Error) WIN32_SETTINGS_FAILURE;
        else {
            m_dwWindowsError = GetLastError();
            return (RS232Error) WIN32_CHECK_WINDOWS_ERROR;
        }
    }
    return error;
}

//
// read_settings() is an internal support routine only used by member
// functions of Win32Port. It is actually only used in one place: the
// constructor. It is responsible for reading the settings from the 
// port as soon as we open it, giving us a baseline to start with.
// If necessary, this means that we can open the port in the default
// state that the system wants it to be opened in.
//
// Note that I didn't add accessor functions to the DCB to pull these
// settings out, we just look directly at the DCB members to get them.
// It would have probably been better to add translation routines, but
// it is not really an important issue.
//
void Win32Port::read_settings()
{
    DCB dcb;
    GetCommState( m_hPort, &dcb );
    settings.BaudRate = dcb.BaudRate;
    if ( !dcb.fParity )
        settings.Parity = 'N';
    else 
        switch ( dcb.Parity ) {
        case EVENPARITY  : settings.Parity = 'E'; break;
        case ODDPARITY   : settings.Parity = 'O'; break;
        case MARKPARITY  : settings.Parity = 'M'; break;
        case SPACEPARITY : settings.Parity = 'S'; break;
        default          : settings.Parity = 'N'; break;
        }
    settings.WordLength = dcb.ByteSize;
    if ( dcb.StopBits == ONESTOPBIT )
        settings.StopBits = 1;
    else
        settings.StopBits = 2;
    if ( dcb.fDtrControl == DTR_CONTROL_DISABLE )
        settings.Dtr = 0;
    else
        settings.Dtr = 1;
    if ( dcb.fRtsControl == RTS_CONTROL_DISABLE )
        settings.Rts = 0;
    else
        settings.Rts = 1;
    if ( dcb.fOutX || dcb.fInX )
        settings.XonXoff = 1;
    else
        settings.XonXoff = 0;
    if ( dcb.fOutxCtsFlow || dcb.fRtsControl == RTS_CONTROL_HANDSHAKE )
        settings.RtsCts = 1;
    else
        settings.RtsCts = 0;
    if ( dcb.fOutxDsrFlow || dcb.fDtrControl == DTR_CONTROL_HANDSHAKE )
        settings.DtrDsr = 1;
    else
        settings.DtrDsr = 0;
}

//
// translate_last_error() is called any time an error occurs when
// a Win32 API function is called. We know how to translate a couple
// of Win32 errors to native versions, but for the most part we just
// pass the responsibility for interpretation on to the caller of the
// Win32Port function. The value we see is stored in the m_dwWindowsError
// member so that it can be checked even in the presence of other
// intervening problems.
//

RS232Error Win32Port::translate_last_error()
{
    switch ( m_dwWindowsError = GetLastError() )
    {
    case ERROR_ACCESS_DENIED  : return error_status = RS232_PORT_IN_USE;
    case ERROR_FILE_NOT_FOUND : return error_status = RS232_PORT_NOT_FOUND;
    }
    return error_status = (RS232Error) WIN32_CHECK_WINDOWS_ERROR;
}

//
// InputThread is the function that runs as a separate thread that is
// responsible for reading all input data and status information. It is
// a fairly complex thread, which makes it a bit harder to read than
// it should be. Outside of setup and teardown, the routine sits in 
// a giant loop that basically performs two functions. First, it makes
// sure that it is properly set up to be notified when either incoming
// data or status messages arrive. Status messages consist of line status
// errors and modem status changes. (Note that we don't try to read if
// there is no room for data in the input buffer.) Once that is set up,
// we simply wait for one of four potential events to be signaled. The
// events are 1) a kill message fromt the main thread, which comes when
// the port is being closed, 2) incoming data that has been read in from
// the serial port, 3) a line status error or modem status change on the
// serial port, and 4) a read request message, which indicates that some
// room may have been opened up in the input buffer.
//
// The rest of the work in the routine is devoted to figuring out what
// to do in response to those incoming events.
//

void Win32Port::InputThread( void * arglist )
{
    //
    // The thread is passed a pointer to the Win32Port object when it is
    // started up. We have to cast that back to a Win32Port pointer,
    // because of the way Win32 starts a thread function. Since this is
    // a static member function of the class, that gives us carte blanche
    // to access all the protected members of the class. It also means we
    // don't have to mess with creation or management of thread-specific
    // data, as anything we need will be in the object itself.
    //
    Win32Port *port = (Win32Port *) arglist;
    //
    // We call these two functions once when we start up so that all of
    // the initial settings in the modem status and line status words
    // are initialized properly. This also guarantees that the notification
    // functions for the modem status will be called once with the initial
    // values, which will often be a useful thing for the calling program.
    //
    port->check_modem_status( true, 0 );
    port->clear_error();
    //
    // We need OVERLAPPED structures for both of our overlapped
    // functions in this thread: the data read called with ReadFile(),
    // and the status read called with WaitCommEvent. We could have included
    // these in the Win32Port object, but since they aren't used
    // anywhere else, it seemed better to confine them to being automatic
    // objects. Each of these objects gets an event that we create here
    // as well, they are the events used to signal us when we are
    // waiting in the big loop.
    //
    OVERLAPPED AsyncReadInfo = { 0 };
    OVERLAPPED AsyncStatusInfo = { 0 };
    AsyncReadInfo.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    AsyncStatusInfo.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    assert( AsyncReadInfo.hEvent );
    assert( AsyncStatusInfo.hEvent );
    //
    // This word is used as an argument to WaitForCommEvent()
    //
    DWORD dwCommEvent;
    //
    //  Some initialization
    //
    bool waiting_on_status = false;
    port->m_bInputThreadReading = false;
    //
    // This array holds the four event handles that are used to
    // signal this thread. On each pass through the main loop we
    // will be waiting for one of the four to go to the signal
    // state, at which time we take action on it.
    //
    HANDLE handles[ 4 ] = { port->m_hKillInputThreadEvent,
                            AsyncReadInfo.hEvent,
                            AsyncStatusInfo.hEvent,
                            port->m_hReadRequestEvent };

    //
    // We set all of the conditions that we will be waiting for.
    // Note the inclusion of EV_RINGTE, which is an undocumented
    // flag that we define in Win32Port.h.
    //
    SetCommMask( port->m_hPort, 
                 EV_BREAK | EV_CTS  | EV_DSR   | EV_RXCHAR |
                 EV_ERR   | EV_RING | EV_RLSD  | EV_RINGTE );
    //
    // This is the main loop. It executes until the done flag is set,
    // which won't happen until the kill thread message is sent.
    // The first part of the loop sets up the read actions, the second
    // part waits for something to happen, and the final part of the
    // loop deals with whatever happened.
    //
    for ( bool done = false ; !done ; ) {
        //
        // Under normal conditions this loop should have a read action
        // in progress at all times. The only time this won't be true
        // is when there is no room in the RX queue. We have a member
        // in class Win32Port that defines whether or not a read is
        // presently active. This section of code just makes sure that
        // if no read is currently in progress, we do our best to get
        // one started.
        //
        int bytes_to_read = 0;
        char read_buffer[ 256 ];
        DWORD dwBytesRead;
        if ( !port->m_bInputThreadReading ) {
            bytes_to_read = port->m_RxQueue.SpaceFree();
            if ( bytes_to_read > 256 )
                bytes_to_read = 256;
            //
            // If there is room to add new bytes to the RX queue, and
            // we currently aren't reading anything, we kick off the
            // read right here with a call to ReadFile(). There are two
            // possible things that can then happen. If there isn't any
            // data in the buffer, ReadFile() can return immediately
            // with the actual input in progress but not complete. If
            // there was enough data in the input stream already to
            // fulfill the read, it might return with data present.
            //
            if ( bytes_to_read > 0 ) {
                if ( !ReadFile( port->m_hPort, read_buffer, bytes_to_read, &dwBytesRead, &AsyncReadInfo ) )
                {
                    // The only acceptable error condition is the I/O
                    // pending error, which isn't really an error, it
                    // just means the read has been deferred and will
                    // be performed using overlapped I/O.
                    port->m_bInputThreadReading = true;
                } else {    
                    // If we reach this point, ReadFile() returned 
                    // immediately, presumably because it was able
                    // to fill the I/O request. I put all of the bytes
                    // just read into the RX queue, then call the 
                    // notification routine that should alert the caller
                    // to the fact that some data has arrive.
                    if ( dwBytesRead ) {
                        port->m_RxQueue.Insert( read_buffer, dwBytesRead );
                        port->RxNotify( dwBytesRead );
                    }
                }
            } else {
                // If we reach this point, it means there is no room in 
                // the RX queue. We reset the read event (just in case)
                // and go on to the rest of the code in this loop.
                ResetEvent( AsyncReadInfo.hEvent );
            }
        }
        //
        // Unlike the read event, we will unconditionally always have
        // a status even read in progress. The flag waiting_on_status
        // shows us whether or not we are currently waiting. If not,
        // we have to make a call to WaitCommEvent() so that one gets
        // kicked off.
        //
        if ( !waiting_on_status  ) {
            if ( !WaitCommEvent( port->m_hPort, 
                                 &dwCommEvent, 
                                 &AsyncStatusInfo ) ) {
                // WaitCommEvent() can return immediately if there are
                // status events queued up and waiting for us to read.
                // But normally it should return with an error code of
                // ERROR_IO_PENDING, which means that no events are
                // currently queued, and we will have to wait for 
                // something noteworthy to happen.
                waiting_on_status = true;
            } else { 
                // If we reach this point it means that WaitCommEvent()
                // returned immediately, so either a line status error
                // or a modem line state change has occurred. These two
                // routines are called to deal with all those possibilities.
                // The event bits are in dwCommEvent, which was passed to
                // WaitCommEvent() when we called it. The first of these
                // two functions handles all changes in modem status lines,
                // the second deals with line status errors.
                port->check_modem_status( false, dwCommEvent );
                port->clear_error();
            }
        }
        //
        // We've completed the preliminary part of the loop and we are
        // now read to wait for something to happen. Note that it is
        // possible that either the call to ReadFile() or 
        // WaitCommEvent() returned immediately, in which case we aren't
        // actively waiting for data of that event type. If that's true,
        // we have to go back through the loop and try to set up the
        // ReadFile() or WaitCommEvent() again. That's what this first
        // conditional statement is checking for. It would be a simpler
        // statement, but we have to take into account the possibility
        // that we aren't reading because there is no room in the
        // RX queue, in which case we can wait right away.
        if ( waiting_on_status && 
           ( port->m_bInputThreadReading || bytes_to_read == 0 ) ) {
            DWORD result = WaitForMultipleObjects( 4,
                                                   handles,
                                                   FALSE,

⌨️ 快捷键说明

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