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

📄 win32port.cpp

📁 DOS下采用中断接收数据的串口通讯的例子,很难找到的好东西!
💻 CPP
📖 第 1 页 / 共 5 页
字号:
//
//  WIN32PORT.CPP
//
//  Source code from:
//
//  Serial Communications: A C++ Developer's Guide, 2nd Edition
//  by Mark Nelson, M&T Books, 1999
//
//  Please see the book for information on usage.
//
// This file contains the class implementation for Win32Port.
// Win32Port implements a version of class RS232 that works with
// the Win32 serial API. The implementation of this class is in
// file Win32Port. Although the sample programs in this book use
// MFC, this class should work independently of any application
// framework.
//
//

#include <process.h>
#include <cassert>
#include <sstream>
#include <iomanip>
using namespace std;

#include "Win32Port.h"


//
// Under MFC, some heap use tracking is done in debug mode, in an
// attempt to track down memory leaks. This code enables that.
// Feel free to delete the five lines that enable this feature,
// as they are not necessary for proper operation of the
// Win32Port class.
//
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//
// The arguments to the Win32Port constructor are the same as the
// arguments passed to the constructurs for all of the other
// RS232 derived classes, with one noticeable difference. Instead
// of passing a numeric value such as COM1 or COM2, this guy
// expects a string, e.g. "COM2". This is necessary to accomodate
// two special cases. First, com ports greater than 9 must be
// opened with a special string that looks like this:
// "\\.\COM10". Second, some drivers could be using a completely
// different name, like "USB Port 1".
//
//
// The constructor has a member initialization list that creates
// the input and output queues at the size chosen by the
// enumerated value in the class definition. The MTQueue class
// limits insertions into the internal deque<char> member to the
// value specified in the constructor.
//
Win32Port::Win32Port( const string &port,
                      long baud_rate   /* = UNCHANGED */,
                      char parity      /* = UNCHANGED */,
                      int word_length  /* = UNCHANGED */,
                      int stop_bits    /* = UNCHANGED */,
                      int dtr          /* = SET       */,
                      int rts          /* = SET       */,
                      int xon_xoff     /* = DISABLE   */,
                      int rts_cts      /* = DISABLE   */,
                      int dtr_dsr      /* = DISABLE   */ )
    : m_TxQueue( MAX_OUTPUT_BUFFER_SIZE ),
      m_RxQueue( MAX_INPUT_BUFFER_SIZE )
{
//
// Win32Port has to share the debug output with the parent class.
// To determine where our first line starts, we call the
// FormatDebugOutput() function from our parent class.
//
    first_debug_output_line = RS232::FormatDebugOutput();
    debug_line_count = FormatDebugOutput();
    string temp = port;
//
// One of the base class members holds the port_name, which for
// most other class implemenations is a value ranging from COM1
// to COM9. Although not every port passed to this class conforms
// to that naming structure, most of them do. We try to extract
// that port name from the one passed in wherever possible. If no
// possible match is made, the value -1 is inserted into the
// port_name member. The only important place where the port_name
// member is used is in the dump status output routine of the
// base class. Initializing it here ensures that the value
// displayed in the debug output will usually match up with the
// value the calling program passed as a port name.
//
    if ( temp.substr( 0, 4 ) == "\\\\.\\" )
        temp = temp.substr( 4, string::npos );
    if ( toupper( temp[ 0 ] ) == 'C' &&
         toupper( temp[ 1 ] ) == 'O' &&
         toupper( temp[ 2 ] ) == 'M' ) {
        temp = temp.substr( 3, string::npos );
        port_name = (RS232PortName) ( atoi( temp.c_str() ) - 1 );
    } else
        port_name = (RS232PortName) -1;
    //
    // Here is where the real work starts. We open the port using the
    // flags appropriate to a serial port. Using FILE_FLAG_OVERLAPPED
    // is critical, because our input and output threads depend on 
    // the asynchronous capabilities of Win32.
    //
    m_hPort = CreateFile( port.c_str(), 
                          GENERIC_READ | GENERIC_WRITE,
                          0,
                          0,
                          OPEN_EXISTING,
                          FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                          0 );
    //
    // If I get a valid handle, it means the port could at least be
    // opened. I then try to set a bunch of miscellaneous things that
    // the port needs to run properly. If everything goes according to
    // plan, when that is all done I start the input and output threads
    // and the port is then really running.
    //
    if ( m_hPort != INVALID_HANDLE_VALUE ) {
        m_dwErrors = 0;            //Clear cumulative line status errors
        m_iBreakDuration = 0;    //No break in progress, initialize to 0
        SetLastError( 0 );        //Clear any Win32 error from this thread
        read_settings();           //Read and save current port settings 
        saved_settings = settings; //Only needed because base class dumps 
                                   //the saved settings in debug output
        //Init timeous to ensure our overlapped reads work
        COMMTIMEOUTS timeouts = { 0x01, 0, 0, 0, 0 };
        SetCommTimeouts( m_hPort, &timeouts );
        SetupComm( m_hPort, 500, 500 ); //set buffer sizes
        error_status = RS232_SUCCESS;     //clear current class error
        settings.Dtr = 1;                //Set these five values to their
        settings.Rts = 1;                //default values, the Adjust()
        settings.XonXoff = 0;            //function will modify them if                                    
        settings.RtsCts = 0;            //new values were passed in the args
        settings.DtrDsr = 0;            //to the constructor
        settings.Adjust( baud_rate,
                         parity,
                         word_length,
                         stop_bits,
                         dtr,
                         rts,
                         xon_xoff,
                         rts_cts,
                         dtr_dsr );
        //
        // Now write the new settings to the port. If this or any other
        // operation has failed, we abort here. The user will be able
        // to see that the port is not working due to having an error
        // set immediately upon opening. 
        //
        error_status = write_settings();    
        if ( error_status != RS232_SUCCESS ) {
            CloseHandle( m_hPort );
            m_hPort = 0;
        } else {
            //
            // Since the port opened properly, we're ready to start the
            // input and output threads. Before they start we create the
            // five Win32 events that will be used to pass requests to
            // the threads. Note that the only argument passed to the
            // thread initialization is a pointer to this. The thread
            // needs that to find all of the data in the Win32Port 
            // object that it will be manipulating.
            //
            m_hKillInputThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
            m_hKillOutputThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
            m_hWriteRequestEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
            m_hReadRequestEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
            m_hBreakRequestEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
            m_hInputThread = _beginthread( InputThread, 0, (void *) this );
            m_hOutputThread = _beginthread( OutputThread, 0, (void *) this );
        }
    } 
    //
    // If the CreateFile() function didn't succeed, we're really in a bad
    // state. Just translate any error into something intellible and
    // exit. The user will have tofigure out that the open failed due to
    // the bad error_status member.
    //
    else
        translate_last_error();
}


//
// The destructur only has a couple of important things to do. If
// the port was up and running, the handle that was created as a
// result of the call to CreateFile() has to be closed with a
// call to CloseHandle(). The two worker threads created when the
// port was opened both have to be killed, which is done by
// setting an Event that the threads will be continually watching
// for. Once that is complete, the five events created in the
// ctor can be destroyed as well.
//
// Note that because of the way this dtor works, we are
// vulnerable to getting hung up if one of the two worker threads
// doesn't exit properly. This is a strong incentive towards
// keeping the code in the threads nice and simple.
//
Win32Port::~Win32Port()
{
    if ( m_hPort != INVALID_HANDLE_VALUE ) {
        SetEvent( m_hKillOutputThreadEvent );
        SetEvent( m_hKillInputThreadEvent );
        long handles[ 2 ] = { m_hInputThread, m_hOutputThread };
        WaitForMultipleObjects( 2,
                                (HANDLE *) handles,
                                TRUE,
                                INFINITE );
        CloseHandle( m_hPort );
        m_hPort = INVALID_HANDLE_VALUE;
        CloseHandle( m_hKillInputThreadEvent );
        CloseHandle( m_hKillOutputThreadEvent );
        CloseHandle( m_hWriteRequestEvent );
        CloseHandle( m_hReadRequestEvent );
        CloseHandle( m_hBreakRequestEvent );
    }
}

//
// The structure of the RS232 class called for a single global
// function that was called when the library was sitting in a
// loop waiting for input data to show up. Under Win32, there are
// a couple of different appropaches you might want to take when
// implementing this routine. If you are calling the routine from
// your main thread, you want to make sure that your GUI is
// getting some CPU cycles, so you probably want to replace this
// function with a version that operates your message pump. The
// routine might also check for a cancel flag that a user can set
// from inside your app.
//
// If you are calling this routine from a background thread, you
// could conceivably have it sleep for 5 or 10 milliseconds each
// time it is called. If you are actually idle and truly waiting
// for data to either arrive or be sent, it shouldn't cause any
// degradation of your app's throughput, and it will free up time
// for other processes and threads.
//
// Since this is a virtual function, you can override it in your
// derived class and use some object specific data to determine
// how it behaves.
//
int RS232::IdleFunction( void )
{
    return RS232_SUCCESS;
}

//
// The RS232 base class expects a global ReadTime() function that
// returns the current time in milliseconds. This matches up well
// with the GetTickCount() in the Windows API.
//
long ReadTime( void )
{
    return GetTickCount();
}

//
// This is our class specific implementation of the RS232 virtual
// function called Set(). It is called to set the four principal
// attributes of the serial port: baud rate, parity, word size,
// and number of stop bits. It relies on two internal functions
// to actually do all of the work. Member function Adjust() is
// called to accept the new settings and store them in the
// internal settings member. write_settings() is then called to
// take those values, make any modifications needed to the DCB,
// then call the Win32 API functions needed to update the
// physical values in the port. Illegal values can result in the
// error_status member being set to a bad value, which will
// prevent the port from working at all.
//
RS232Error Win32Port::Set( long baud_rate  /* = UNCHANGED */,
                           int parity      /* = UNCHANGED */,
                           int word_length /* = UNCHANGED */,
                           int stop_bits   /* = UNCHANGED */ )
{
    settings.Adjust( baud_rate,
                     parity,
                     word_length,
                     stop_bits,
                     UNCHANGED,
                     UNCHANGED,
                     UNCHANGED,
                     UNCHANGED,
                     UNCHANGED );
    return write_settings();

}

//
// This is an implementation of the virtual Dtr() function from
// the base class RS232. It doesn't have to do too much work, it
// merely updates the settings member and then calls the
// write_settings() member function to do all the work. It does
// check to see if DTR/DSR handshaking is in place, and if it is,
// it returns the non-fatal warning to the calling routine. If
// that doesn't happen, it updates the Dtr member in the settings
// object and calls write_settings() to update the physical port.
// Note that it returns the current setting of the Dtr member. If
// this function is called with no arguments by the end user, it
// doesn't do anything except return that value.
//
int Win32Port::Dtr( int setting /* = UNCHANGED */ )
{
    if ( error_status < RS232_SUCCESS )
        return error_status;
    if ( setting != UNCHANGED ) {
        if ( settings.DtrDsr == 1 )
            return WIN32_HANDSHAKE_LINE_IN_USE;
        settings.Dtr = setting != 0;
        RS232Error error = write_settings();
        if ( error < RS232_SUCCESS )
            return error;

⌨️ 快捷键说明

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