COMDIO - Comm Port Digital I/O

COMDIO provides two extra Digital Inputs and two extra Digital Outputs per COM port by utilizing the RTS/CTS and DTR/DSR hardware handshake pins. Many systems do not utilize their RS-232 channels for Serial I/O so they can provide a small number of "free" DIO channels.

A pulse generator, suitable for triggering an oscilloscope, has also been provided. This can be used to measure the rep rate of control loops, the elapsed time between events, etc.

COMDIO.h

/***************************************************************************************
* Copyright (c) 2009 _ 2010 Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384_5342 *
***************************************************************************************/

#if !defined(AFX_COMDIO_H__BADE34F2_B710_4782_81EA_91E50F4A141E__INCLUDED_)
#define AFX_COMDIO_H__BADE34F2_B710_4782_81EA_91E50F4A141E__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// COMDIO.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CCOMDIO Base Class

class CCOMDIO

   {

   // Construction

   public:

      static const float fltNaN;

      CCOMDIO ( int nCom = 0 );
      ~CCOMDIO();

   // Implementation

   protected:

      bool     m_fIsValid;

      int      m_nCom;

      HANDLE   m_hCom;

      bool     m_fDI00_DSR_Pin_6;
      bool     m_fDI01_CTS_Pin_8;

      bool     m_fDO00_DTR_Pin_4;
      bool     m_fDO01_RTS_Pin_7;

   public:

      bool  GetDI       ( int nChan ) { switch ( nChan ) { case 0:  return m_fDI00_DSR_Pin_6;  case 1:  return m_fDI01_CTS_Pin_8; default:  return false; } }
      bool  GetDO       ( int nChan ) { switch ( nChan ) { case 0:  return m_fDO00_DTR_Pin_4;  case 1:  return m_fDO01_RTS_Pin_7; default:  return false; } }

      bool  GetDTR      ( void ) { return m_fDO00_DTR_Pin_4; }
      bool  GetRTS      ( void ) { return m_fDO01_RTS_Pin_7; }

      bool  IsValid     ( void );

      bool  Assert      ( int nChan, bool fAssert );

      bool  GetStatus   ( void );

      bool  Pulse       ( int nChan );

   };

#endif // !defined(AFX_COMDIO_H__BADE34F2_B710_4782_81EA_91E50F4A141E__INCLUDED_)

COMDIO.cpp

/***************************************************************************************
* Copyright (c) 2009 - 2010 Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342 *
***************************************************************************************/

/*********************************************************************************
*                                                                                *
*  COMDIO lets you use pin 4 (DTR) and pin 7 (RTS) of your RS-232 COMn: channels *
*  as Digital Outputs and pin 6 (DSR) and pin 8 (CTS) as Digital Inputs.         *
*                                                                                *
*  COMDIO::Pulse() produces a pulse that is a suitable signal to see with an     *
*  oscilloscope for timing and diagnostic information.                           *
*                                                                                *
*********************************************************************************/

#define WIN32_LEAN_AND_MEAN   // Exclude rarely-used stuff from Windows headers

#include <WINDOWS.H>

#include <STDIO.H>

#include "COMDIO.h"

/////////////////////////////////////////////////////////////////////////////
// CCOMDIO

CCOMDIO::CCOMDIO ( int nCom ) :

   m_fIsValid  ( false                 ),

   m_nCom      ( nCom                  ),

   m_hCom      ( INVALID_HANDLE_VALUE  )

   {
   char szCom [] = "\\\\.\\COM0";

   // Validate the Parameter

   if ( m_nCom < 1 || m_nCom > 9 ) return;

   // Itentify the COMn: Port Number

   szCom [ 7 ] = ( char ) ( szCom [ 7 ] + m_nCom );

   // Open the Handle to COMn:

   m_hCom = CreateFile (

      szCom,                  // lpFileName              - Name of the COMn: Port Using Special Prefix to Identify it as a Device
      0,                      // dwDesiredAccess         - Device Query Access
      0,                      // dwShareMode             - Exclusive Access
      NULL,                   // lpSecurityAttributes    - No Security Attributes
      OPEN_EXISTING,          // dwCreationDisposition   - COMn: Port Must Exist
      FILE_ATTRIBUTE_NORMAL,  // dwFlagsAndAttributes    - Normal
      NULL                    // hTemplateFile           - No Template File

   );

   if ( m_hCom == INVALID_HANDLE_VALUE )
      {
      DWORD dwError = GetLastError();

      fprintf ( stderr, "CreateFile Returned %lu\n", dwError );

      return;
      }

   m_fIsValid = true;

   // Initially Turn Both DOs Off

   Assert ( 0, false );
   Assert ( 1, false );

   }  // CCOMDIO::CCOMDIO ( int nCom )

CCOMDIO::~CCOMDIO()

   {

   // Close the COMn: Handle

   if ( m_hCom != INVALID_HANDLE_VALUE )
      CloseHandle ( m_hCom );

   }  // CCOMDIO::~CCOMDIO()

bool CCOMDIO::IsValid ( void )

   {

   // Assure that "this" Points to a Valid Region of Memory

   if ( IsBadWritePtr ( this, sizeof ( CCOMDIO ) ) )
      return false;

   // Return the Validity Indicator

   return this->m_fIsValid;

   }  // bool CCOMDIO::IsValid ( void )

bool CCOMDIO::Assert ( int nChan, bool fAssert )

   {

   switch ( nChan )
      {
      case 0:  return EscapeCommFunction ( m_hCom, ( ( m_fDO00_DTR_Pin_4 = fAssert ) == true ) ? SETDTR : CLRDTR ) ? true : false;
      case 1:  return EscapeCommFunction ( m_hCom, ( ( m_fDO01_RTS_Pin_7 = fAssert ) == true ) ? SETRTS : CLRRTS ) ? true : false;

      default: return false;
      }

   }  // bool CCOMDIO::Assert ( int nChan, bool fAssert )

bool CCOMDIO::Pulse ( int nChan )

   {

   switch ( nChan )
      {
      case 0:  return ( EscapeCommFunction ( m_hCom, SETDTR ) && EscapeCommFunction ( m_hCom, CLRDTR ) );
      case 1:  return ( EscapeCommFunction ( m_hCom, SETRTS ) && EscapeCommFunction ( m_hCom, CLRRTS ) );

      default: return false;
      }

   }  // bool CCOMDIO::Pulse ( int nChan )

bool CCOMDIO::GetStatus ( void )

   {
   DWORD dwCommStatus = 0LU;

   if ( ! GetCommModemStatus ( m_hCom, &dwCommStatus ) )
      return false;

   m_fDI00_DSR_Pin_6 = dwCommStatus & MS_DSR_ON ? true : false;
   m_fDI01_CTS_Pin_8 = dwCommStatus & MS_CTS_ON ? true : false;

// fprintf ( stdout, "0x%8.8X  %c %c ==> ", dwCommStatus, m_fDI00_DSR_Pin_6 ? 'T' : 'F', m_fDI01_CTS_Pin_8 ? 'T' : 'F' );

   return true;

   }  // bool CCOMDIO::GetStatus ( void )

Try.cpp

/********************************************************************************
* Copyright (c) 2011 Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342 *
********************************************************************************/

/************************************************************************************
*                                                                                   *
*  This application exercises the COMn: DIO (both inputs and outputs).  Run a wire  *
*  from the channel 0 DO (DTR on Pin 4) to the channel 0 DI (DSR on Pin 6) or from  *
*  the channel 1 DO (RTS on Pin 7) to the channel 1 DI (CTS on Pin 8).  This will   *
*  allow you to monitor the DO by watching the associated DI to see that it is      *
*  toggling values correctly.                                                       *
*                                                                                   *
*  The specified DO channel is set to 0 at initalization.  It is then toggled to 1  *
*  to assert the channel and then set back to 0 to deassert it.                     *
*                                                                                   *
*  While most industrial laptops (i.e. Toughbooks) have COM1: and COM2: ports       *
*  available, I am using a Dell Inspiron 1545 to develop this code and it does      *
*  not have a COM port.  I use a USB Serial Port on COM6: to develop to Rabbit      *
*  processors and this code has been tested with it.  You will have to initialize   *
*  the CCOMDIO object to the appropriate COM port for your system and set nChan to  *
*  select the appropriate DI/DO pin pair before compiling so that the demo will     *
*  work correctly.                                                                  *
*                                                                                   *
*  The Pulse function toggles the DO on and then immediately back off to provide a  *
*  signal that can provide a trigger to an oscilloscope.  This can provide good     *
*  dignostic information to compute loop timing or to prove that a particular       *
*  section of code is being executed for example.                                   *
*                                                                                   *
*  To compile from the command line:  "CL Try.cpp COMDIO.cpp"                       *
*                                                                                   *
************************************************************************************/

#define WIN32_LEAN_AND_MEAN   // Exclude rarely-used stuff from Windows headers

#include <WINDOWS.H>

#include <ASSERT.H>
#include <STDIO.H>

#include "COMDIO.h"

int main ( int /* argc */, char * /* argv */ [], char * /* envp */ [] )

   {
   int nChan = 0; // Set to Zero 0 the DSR/DTR Pair and 1 for the CTS/RTS Pair

   CCOMDIO COMDIO ( 6 );   // COM6: is USB Serial Port on My System

   if ( ! COMDIO.IsValid() )
      {
      fprintf ( stderr, "Unable to open the COMDIO() object!\n\a" );
      return 1;
      }

   // Test the DI/DO functionality

   COMDIO.Assert ( nChan, true );   // Set the Digital Output to 1
   Sleep ( 2LU );                   // Allow time for Output to Turn On
   COMDIO.GetStatus();              // Read the Digital Inputs

// fprintf ( stdout, "%c\n", COMDIO.GetDTR()          ? 'T' : 'F' ); // nChan == 0
// fprintf ( stdout, "%c\n", COMDIO.GetRTS()          ? 'T' : 'F' ); // nChan == 1
   fprintf ( stdout, "%c\n", COMDIO.GetDI ( nChan )   ? 'T' : 'F' ); // Either Channel

   COMDIO.Assert ( nChan, false );  // Set the Digital Output to 0
   Sleep ( 2LU );                   // Allow time for Output to Turn On
   COMDIO.GetStatus();              // Read the Digital Inputs

// fprintf ( stdout, "%c\n", COMDIO.GetDTR()          ? 'T' : 'F' ); // nChan == 0
// fprintf ( stdout, "%c\n", COMDIO.GetRTS()          ? 'T' : 'F' ); // nChan == 1
   fprintf ( stdout, "%c\n", COMDIO.GetDI ( nChan )   ? 'T' : 'F' ); // Either Channel

   return 0;

   }  // int main ( int argc, char * argv [], char * envp [] )