win32port.cpp

来自「DOS下采用中断接收数据的串口通讯的例子,很难找到的好东西!」· C++ 代码 · 共 1,585 行 · 第 1/5 页

CPP
1,585
字号
// Note that it has a helper functions that is used to format single
// characters for display in a nice usable format.
//

static string DisplayChar( int val ) {
    char temp[ 2 ] = { val, 0 };
    if ( val >= ' ' && val <= 0x73 )
        return string( temp );
    ostringstream s;
    s << "0x" << setw( 2 ) << setfill( '0' ) << hex << val;
    return s.str();
}

int Win32Port::FormatDebugOutput( char *buffer, int line_number )
{
    char *StopBits[ 4 ] = {
        "1",
        "1.5",
        "2",
        "???"
    };
    char *DtrControl[ 4 ] = {
        "DISABLE",
        "ENABLE",
        "HANDSHAKE",
        "???"
    };
    char *RtsControl[ 4 ] = {
        "DISABLE",
        "ENABLE",
        "HANDSHAKE",
        "TOGGLE"
    };
    if ( buffer == 0 )
        return( first_debug_output_line +  9 );
    if ( line_number < first_debug_output_line )
        return RS232::FormatDebugOutput( buffer, line_number );
    switch ( line_number - first_debug_output_line ) {
    case 0 :
        sprintf( buffer, 
                 "DCB-> Baud: %6d  " 
                 "fBinary: %1d  "
                 "fParity: %1d  "
                 "fOutxCtsFlow: %1d  "
                 "fOutxDsrFlow: %1d",
                 m_Dcb.BaudRate,
                 m_Dcb.fBinary,
                 m_Dcb.fParity,
                 m_Dcb.fOutxCtsFlow,
                 m_Dcb.fOutxDsrFlow );
        break;
    case 1 :
        sprintf( buffer, 
                 "DCB-> fDtrControl: %9s  "
                 "fDsrSensitivity: %1d  "
                 "fTXContinueOnXoff: %1d  ",
                 DtrControl[ m_Dcb.fDtrControl ],
                 m_Dcb.fDsrSensitivity,
                 m_Dcb.fTXContinueOnXoff );
        break;
    case 2 :
        sprintf( buffer, 
                 "DCB-> fOutX: %1d  "
                 "fInX: %1d  "
                 "fErrorChar: %1d  "
                 "fNull: %1d  "
                 "fRtsControl: %9s",
                 m_Dcb.fOutX,
                 m_Dcb.fInX,
                 m_Dcb.fErrorChar,
                 m_Dcb.fNull,
                 RtsControl[ m_Dcb.fRtsControl ] );
        break;
    case 3 :
        sprintf( buffer, 
                 "DCB-> fAbortOnError: %1d  "
                 "XonLim: %4d  "
                 "XoffLim: %4d  "
                 "ByteSize: %1d  "
                 "Parity: %1c ",
                 m_Dcb.fAbortOnError,
                 m_Dcb.XonLim,
                 m_Dcb.XoffLim,
                 m_Dcb.ByteSize,
                 "NOEMS"[ m_Dcb.Parity] );
        break;
    case 4 :
        sprintf( buffer,
                 "DCB-> StopBits: %3s  "
                 "XonChar: %s  "
                 "XoffChar: %s  "
                 "ErrorChar: %s  ",
                 StopBits[ m_Dcb.StopBits & 3 ],
                 DisplayChar( m_Dcb.XonChar ).c_str(),
                 DisplayChar( m_Dcb.XoffChar ).c_str(),
                 DisplayChar( m_Dcb.ErrorChar ).c_str() );
        break;
    case 5 :
        sprintf( buffer,
                 "DCB-> EofChar: %s  "
                 "EvtChar: %s  ",
                 DisplayChar( m_Dcb.EofChar ).c_str(),
                 DisplayChar( m_Dcb.EvtChar ).c_str() );
        break;
    case 6 : 
    {
        COMSTAT comstat;
        clear_error( &comstat );
        sprintf( buffer,
                 "COMSTAT-> fCtsHold: %1d  "
                 "fDsrHold: %1d  "
                 "fRlsHold: %1d  "
                 "fXoffHold: %1d  "
                 "fXoffSent: %1d",
                 comstat.fCtsHold,
                 comstat.fDsrHold,
                 comstat.fRlsdHold,
                 comstat.fXoffHold,
                 comstat.fXoffSent );
        break;
    }
    case 7 :
    {
        COMSTAT comstat;
        clear_error( &comstat );
        sprintf( buffer,
                 "COMSTAT-> fEof: %1d  "
                 "fTxim: %1d  "
                 "cbInQueue: %5d  "
                 "cbOutQueue: %5d",
                 comstat.fEof,
                 comstat.fTxim,
                 comstat.cbInQue,
                 comstat.cbOutQue );
        break;
    }
    case 8 :
        sprintf( buffer, 
                 "CE_-> BREAK: %1d  "
                 "FRAME: %1d  "
                 "OVERRUN: %1d  "
                 "RXOVER: %1d  "
                 "RXPARITY: %1d  "
                 "TXFULL: %1d",
                 ( m_dwErrors & CE_BREAK ) ? 1 : 0,
                 ( m_dwErrors & CE_FRAME ) ? 1 : 0,
                 ( m_dwErrors & CE_OVERRUN ) ? 1 : 0,
                 ( m_dwErrors & CE_RXOVER ) ? 1 : 0,
                 ( m_dwErrors & CE_RXPARITY ) ? 1 : 0,
                 ( m_dwErrors & CE_TXFULL ) ? 1 : 0 );
        break;
    default :
        return RS232_ILLEGAL_LINE_NUMBER;
    }
    return RS232_SUCCESS;
}

//
// write_byte() is a virtual function declared by class RS232 as a
// pure virtual function. Classes derived from RS232 must implement
// this function to work with their particular hardware setup. In our
// case, that simply means checking to see if there is room in the
// output queue, and if there is, adding a byte to it. We set the
// event flag that kick starts the output thread just in case it was
// idle, then return our status. Note that if the port is already in
// an error condition, this routine refuses to do anything except
// return the same error. If the buffer is full, we return the
// non-fatal warning error RS232_TIMEOUT.
//

int Win32Port::write_byte( int c )
{
    if ( error_status < 0 )
        return error_status;
    if ( m_TxQueue.SpaceFree() < 0 )
        return RS232_TIMEOUT;
    m_TxQueue.Insert( c );
    ::SetEvent( m_hWriteRequestEvent );
    return RS232_SUCCESS;
}

//
// write_buffer() is another one of the virtual routines that is declared
// by RS232, the base class, as pure virtual. That means it is our
// responsibility to implement it as best we can in this derived class.
// Writing data to our device is easy, we just have to load it into
// the output queue and set the event that wakes him up. The actual
// number of bytes transferred to the output thread is passed back in
// member ByteCount. If not all bytes could fit in the output buffer,
// the non-fatal warning message RS232_TIMEOUT is passed back to
// the caller.
//

int Win32Port::write_buffer( char *buffer, unsigned int count )
{
    ByteCount = 0;
    if ( error_status < 0 )
        return error_status;
    ByteCount = m_TxQueue.Insert( buffer, count );
    ::SetEvent( m_hWriteRequestEvent );
    if ( ByteCount == count )
        return RS232_SUCCESS;
    else
        return RS232_TIMEOUT;
}

//
// read_byte() is one of the functions that is declared as pure virtual
// in base class RS232. We have to implement a specific version for
// our class that knows how to talk to our hardware. In our case,
// we don't have to talk to the hardware or even the driver. If any
// data has arrived, it has been stuffed into the input queue. We
// get a byte if we can, which will be returned to the caller. If no
// byte is available, the caller receives the non-fatal warning
// message RS232_TIMEOUT.  
//
// One slightly tricky bit in this code is seen where we test to
// see if the input thread is currently reading. If the input thread
// runs out of space in its input buffer, it has to stop reading.
// When we read a new character, we just might free up some space
// so it can start reading again. We aren't going to make that decision
// for the input port, but we do set his event to let him know that
// some space might have freed up.
//
int Win32Port::read_byte( void )
{
    if ( error_status < 0 )
        return error_status;
    int ret_val = m_RxQueue.Extract();
    if ( !m_bInputThreadReading )
        SetEvent( m_hReadRequestEvent );
    if ( ret_val < 0 )
        return RS232_TIMEOUT;
    else
        return ret_val;
}

//
// read_buffer is one of the support routines declared as pure virtual
// in the base class. This means that as a derived class we are 
// responsible for implementing a version that talks to our specific
// hardware or driver. Our implementation here doesn't talk directly
// to the driver, we leave that to the input thread. We simply try
// to take all the characters we can from the input buffer, returning
// the actual count we got in the ByteCount member. If we got every
// character we asked for, we return RS232_SUCCESS. If we didn't
// get everything we asked for, we return the non-fatal warning
// message RS232_TIMEOUT.
//
//
// One slightly tricky bit in this code is seen where we test to
// see if the input thread is currently reading. If the input thread
// runs out of space in its input buffer, it has to stop reading.
// When we read a new character, we just might free up some space
// so it can start reading again. We aren't going to make that decision
// for the input port, but we do set his event to let him know that
// some space might have freed up.
//
int Win32Port::read_buffer( char *buffer, unsigned int count )
{
    ByteCount = 0;
    if ( error_status < 0 )
        return error_status;
    ByteCount = m_RxQueue.Extract( buffer, count );
    buffer[ ByteCount ] = '\0';
    if ( !m_bInputThreadReading )
        SetEvent( m_hReadRequestEvent );
    if ( ByteCount < count )
        return RS232_TIMEOUT;
    else
        return RS232_SUCCESS;
}

//
// clear_error() is an internal support routine that is called in
// response to the reception of an asynchronous error event. It
// first clears the comm error flag so that normal operation of
// the port can resume. It ORs the newly received error flags with
// the ones we have already seen in m_dwErrors. Finally, it calls
// a notification function for any incoming errors that have just occurred.
//
// One potential trouble spot in this function call is that it is 
// called from both the FormatDebugOutput() routine as well as inside
// the receive thread. This opens the window for at least the 
// possibility of missing an incoming error if FormatDebugOutput()
// is called while an incoming error is seen. This could be guarded
// against by adding a critical section, but since FormatDebugOutput()
// is usually only called during testing and diagnostics, I didn't
// add it to this routine.
//

void Win32Port::clear_error( COMSTAT *comstat /* = 0 */ )
{
    COMSTAT c;
    if ( comstat == 0 )
        comstat = &c;
    DWORD errors;
    ClearCommError( m_hPort, &errors, comstat );
    m_dwErrors |= errors;
    if ( errors & CE_BREAK )
        BreakDetectNotify();
    if ( errors & CE_FRAME )
        FramingErrorNotify();
    if ( errors & CE_OVERRUN )
        HardwareOverrunErrorNotify();
    if ( errors & CE_RXPARITY )
        ParityErrorNotify();
    if ( errors & CE_RXOVER )
        SoftwareOverrunErrorNotify();
}

//
// write_settings() is an internal support routine that is only used
// in Win32Port(). Its job is to write the data in the settings member
// to the DCB being used for this port. The settings member has the
// four commonly used settings: baud rate, word size, parity, and
// number of stop bits. It also has current settings for the three

⌨️ 快捷键说明

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