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 + -
显示快捷键?