Author Topic: Windows Com port control C++  (Read 5051 times)

0 Members and 1 Guest are viewing this topic.

Offline TeddyPythonTopic starter

  • Contributor
  • Posts: 36
  • Country: gb
Windows Com port control C++
« on: September 26, 2019, 04:31:29 pm »
I'm trying to write a terminal program in Visual Studio to allow me to communicate with a E3632A over a serial link. I've managed to send and receive data using Termite, but only when RS/CTS flow control is enabled.

The example code (from here https://aticleworld.com/serial-port-programming-using-win32-api/) I have used thusfar did not have any flow control, but I think I have managed to add the RTS/CTS handshaking with

Code: [Select]
dcbSerialParams.fOutxCtsFlow = TRUE;                    // CTS used for output flow control
dcbSerialParams.fRtsControl = RTS_CONTROL_HANDSHAKE;    // Enable RTS


before SetCommState().

However, when I attempt to run my code, it fails to send any bytes, returning "Number of bytes written to the serial port = 0". Do I need to specifically control the RTS/CTS lines myself, using EscapeCommFunction() and GetCommModemStatus()? In another example (here http://www2.eng.cam.ac.uk/~dmh/ptiialab/3B2/handouts/LA-code/raw-code/ComPort.cpp) they have used the fRtsControl and fOutxCtsFlow, but have no needed to using EscapeCommFunction() and GetCommModemStatus()...

Why is the data not being sent?

My code is as follows:

Code: [Select]
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <string.h>


int main(void)
{

HANDLE hComm;  // Handle to the Serial port
BOOL   Status; // Status
DCB dcbSerialParams = { 0 };  // Initializing DCB structure
COMMTIMEOUTS timeouts = { 0 };  //Initializing timeouts structure
char SerialBuffer[64] = { 0 }; //Buffer to send and receive data
DWORD BytesWritten = 0;          // No of bytes written to the port
DWORD dwEventMask;     // Event mask to trigger
char  ReadData;        //temperory Character
DWORD NoBytesRead;     // Bytes read by ReadFile()
unsigned char loop = 0;
wchar_t pszPortName[10] = { 0 }; //com port id
wchar_t PortNo[20] = { 0 }; //contain friendly name



//Enter the com port id
printf_s("Enter the Com Port: ");
wscanf_s(L"%s", pszPortName, (unsigned)_countof(pszPortName));
swprintf_s(PortNo, 20, L"%s", pszPortName);

//Open the serial com port
hComm = CreateFile(PortNo, //friendly name
GENERIC_READ | GENERIC_WRITE,      // Read/Write Access
0,                                 // No Sharing, ports cant be shared
NULL,                              // No Security
OPEN_EXISTING,                     // Open existing port only
0,                                 // Non Overlapped I/O
NULL);                             // Null for Comm Devices

if (hComm == INVALID_HANDLE_VALUE)
{
printf_s("\n Port can't be opened\n\n");
}

//Setting the Parameters for the SerialPort
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

Status = GetCommState(hComm, &dcbSerialParams); //retreives  the current settings
if (Status == FALSE)
{
printf_s("\nError to Get the Com state\n\n");
}

dcbSerialParams.BaudRate = CBR_9600;      //BaudRate = 9600
dcbSerialParams.ByteSize = 8;             //ByteSize = 8
dcbSerialParams.StopBits = TWOSTOPBITS;    //StopBits = 1
dcbSerialParams.Parity = NOPARITY;      //Parity = None

// Enable RTS/CTS handshaking
dcbSerialParams.fOutxCtsFlow = TRUE;                    // CTS used for output flow control
dcbSerialParams.fRtsControl = RTS_CONTROL_HANDSHAKE;    // Enable RTS

// Disable DTR/DSR handshaking
dcbSerialParams.fOutxDsrFlow = FALSE;                   // DSR not used for output flow control
dcbSerialParams.fDsrSensitivity = FALSE;                // Ignore DSR
dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;       // Turn on DTR upon device open

// Disable XON/XOFF handshaking
dcbSerialParams.fOutX = FALSE;                    // No outbound XON/XOFF flow control
dcbSerialParams.fInX = FALSE;                    // No inbound XON/XOFF flow control

// Error handling
dcbSerialParams.fErrorChar = FALSE;               // No replacement of bytes with parity error
dcbSerialParams.fNull = FALSE;                    // Don't discard NULL bytes
dcbSerialParams.fAbortOnError = FALSE;

Status = SetCommState(hComm, &dcbSerialParams);
if (Status == FALSE)
{
printf_s("\nError to Setting DCB Structure\n\n");
}

//Setting Timeouts
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (SetCommTimeouts(hComm, &timeouts) == FALSE)
{
printf_s("\nError to Setting Time outs");
}


printf_s("\n\nEnter your message: ");
scanf_s("%s", SerialBuffer, (unsigned)_countof(SerialBuffer));

//Writing data to Serial Port
EscapeCommFunction(hComm, SETRTS);
GetCommModemStatus(hComm, MS_CTS_ON);
Status = WriteFile(hComm,// Handle to the Serialport
SerialBuffer,            // Data to be written to the port
sizeof(SerialBuffer),   // No of bytes to write into the port
&BytesWritten,  // No of bytes written to the port
NULL);
if (Status == FALSE)
{
printf_s("\nFail to Written");
}

//print numbers of byte written to the serial port
printf_s("\nNumber of bytes written to the serial port = %d\n\n", BytesWritten);

//Setting Receive Mask
Status = SetCommMask(hComm, EV_RXCHAR);

if (Status == FALSE)
{
printf_s("\nError to in Setting CommMask\n\n");
}


//Setting WaitComm() Event
Status = WaitCommEvent(hComm, &dwEventMask, NULL); //Wait for the character to be received
if (Status == FALSE)
{
printf_s("\nError! in Setting WaitCommEvent()\n\n");
}

//Read data and store in a buffer
do
{
Status = ReadFile(hComm, &ReadData, sizeof(ReadData), &NoBytesRead, NULL);
SerialBuffer[loop] = ReadData;
++loop;
} while (NoBytesRead > 0);

--loop; //Get Actual length of received data

printf_s("\nNumber of bytes received = %d\n\n", loop);

//print receive data on console
printf_s("\n\n");
int index = 0;
for (index = 0; index < loop; ++index)
{
printf_s("%c", SerialBuffer[index]);
}

printf_s("\n\n");


CloseHandle(hComm);//Closing the Serial Port
printf_s("Port Closed.\n\n");
return 0;
}


 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 2522
  • Country: us
Re: Windows Com port control C++
« Reply #1 on: September 26, 2019, 11:36:40 pm »
I have not studied your code very close.
Here is some code I wrote as another example (It is for both Windows and Linux):

I HAVE CHANGED ALL '#' CHARACTERS TO '##' BECAUSE THE FORUM CODE TAG IS REMOVING #endif STATEMENTS!

Header:
Code: [Select]
##ifndef __SERIALPORT_H
##define __SERIALPORT_H

##include <stdio.h>
##include <stdlib.h>

##ifdef WIN32
##include <winsock2.h>
##include <commctrl.h>
##else
##include <unistd.h>
##include <sys/types.h>
##include <sys/stat.h>
##include <sys/socket.h>
##include <netinet/in.h>
##include <arpa/inet.h>
##include <fcntl.h>
##include <termio.h>
##include <strings.h>
##endif

##define ASCII_XON       0x11
##define ASCII_XOFF      0x13


class serialPort 
{
public:

   // BAUD RATE
   // 0="110", 1="300", 2="1200", 3="2400", 4="4800", 5="9600", 6="14400", 7="19200", 8="57600", 9="115200"
   int   baudRateIdx;
   
   // DATA BITS
   // 0="4", 1="5", 2="6", 3="7", 4="8"
   int   dataBitsIdx;
   
   // PARITY
   // 0="None", 1="Odd", 2="Even", 3="Mark", 4="Space"
   int   parityIdx;
   
   // STOP BITS
   // 0="1", 1="1.5", 2="2"
   int   stopBitsIdx;
   
   // FLOW CONTROL
   // 0="None", 1="Xon/Xoff", 2="Rts/Cts", 3="Dtr/Dsr"
   int   flowControlIdx;

   serialPort();
   virtual ~serialPort();
   int OpenCommConnection( char *port );
   int CloseCommConnection( void );
   int WriteCommBlock( char *lpByte , int dwBytesToWrite );
   int ReadCommBlock( char *lpszBlock, int nMaxLength );

private:

   int         td;
   int         serialConnected;
##ifdef WIN32
   HANDLE      idComDev;
   OVERLAPPED  osWrite;
   OVERLAPPED  osRead;
##endif

   int SetupCommConnection( void );

};

##endif

Code:
Code: [Select]
##include "serialPort.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

serialPort::serialPort()
{
   baudRateIdx = 5;
   dataBitsIdx = 4;
   parityIdx = 0;
   stopBitsIdx = 0;
   flowControlIdx = 0;
   td = -1;
   serialConnected = 0;
}

serialPort::~serialPort()
{
}


//----------------------------------------------------------------------
int serialPort::OpenCommConnection( char *port )
{
##ifdef WIN32
   int     fRetVal;

   // create I/O event used for overlapped reads / writes
   osRead.Offset = 0;
   osRead.OffsetHigh = 0;
   osRead.hEvent = CreateEvent( NULL, 1,  0, NULL );
   osWrite.Offset = 0;
   osWrite.OffsetHigh = 0;
   osWrite.hEvent = CreateEvent( NULL, 1,  0, NULL );

   // open COMM device
   idComDev = CreateFile( port,
                          GENERIC_READ | GENERIC_WRITE,
                          0,
                          NULL,
                          OPEN_EXISTING,
                          FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
                          NULL );

   if ( idComDev == (HANDLE) -1 ) {
      return 0;
   }
   else {
      // get any early notifications
      SetCommMask( idComDev, EV_RXCHAR );

      // setup device buffers
      SetupComm( idComDev, 4096, 4096 );

      // purge any information in the buffer
      PurgeComm( idComDev, PURGE_TXABORT | PURGE_RXABORT |
                           PURGE_TXCLEAR | PURGE_RXCLEAR );
   }

   fRetVal = SetupCommConnection();

   if (fRetVal) {
      // assert DTR
      EscapeCommFunction( idComDev, SETDTR );
      // set connected flag to 1
      serialConnected = 1;
   }
   else {
      CloseHandle( idComDev );
   }
   return ( fRetVal );

##else
   // Open Serial Port
   if ( (td = open(port,O_RDWR)) == -1) {
      printf("ERROR - Unable to open serial port:  %s\n", port);
      exit(0);
   }

   SetupCommConnection();

   // Clear input and output queues
   tcflush(td,TCIOFLUSH);
   return (1);

##endif
}

//----------------------------------------------------------------------
int serialPort::SetupCommConnection( void )
{
##ifdef WIN32
   int            fRetVal;
   DCB            dcb;
   COMMTIMEOUTS   CommTimeOuts;
   DWORD          baudRateTable[] = {
                     CBR_110, CBR_300, CBR_1200, CBR_2400, CBR_4800, CBR_9600,
                     CBR_14400, CBR_19200, CBR_57600, CBR_115200
                  };


   // set up for overlapped I/O
   CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
   CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
   CommTimeOuts.ReadTotalTimeoutConstant = 1000;
   // CBR_9600 is approximately 1byte/ms. For our purposes, allow
   // double the expected time per character for a fudge factor.
   CommTimeOuts.WriteTotalTimeoutMultiplier = 10*CBR_9600/baudRateTable[ baudRateIdx ];
   CommTimeOuts.WriteTotalTimeoutConstant = 0;
   SetCommTimeouts( idComDev, &CommTimeOuts );

   dcb.DCBlength = sizeof( DCB );

   GetCommState( idComDev, &dcb );

   dcb.BaudRate = baudRateTable[ baudRateIdx ];
   dcb.ByteSize = dataBitsIdx + 4;
   dcb.Parity = parityIdx;
   dcb.StopBits = stopBitsIdx;

   // setup flow control

   switch ( flowControlIdx ) {
   case 1:     // Xon/Xoff control
      dcb.fInX = dcb.fOutX = 1;
      dcb.XonChar = ASCII_XON;
      dcb.XoffChar = ASCII_XOFF;
      dcb.XonLim = 200;
      dcb.XoffLim = 200;
      dcb.fRtsControl = RTS_CONTROL_ENABLE;
      dcb.fDtrControl = DTR_CONTROL_ENABLE;
      break;
   case 2:     // Rts/Cts control
      dcb.fInX = dcb.fOutX = 0;
      dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
      dcb.fDtrControl = DTR_CONTROL_ENABLE;
      break;
   case 3:     // Dtr/Dsr control
      dcb.fInX = dcb.fOutX = 0;
      dcb.fRtsControl = RTS_CONTROL_ENABLE;
      dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
      break;
   default:    // None
      dcb.fInX = dcb.fOutX = 0;
      dcb.fRtsControl = RTS_CONTROL_ENABLE;
      dcb.fDtrControl = DTR_CONTROL_ENABLE;
      break;
   }

   // other various settings

   dcb.fNull = 0;
   dcb.fBinary = 1;
   dcb.fParity = 1;

   fRetVal = SetCommState( idComDev, &dcb );

   return ( fRetVal );

##else
   struct termios  t;

   unsigned int   baudRateTable[] = {
                     B110, B300, B1200, B2400, B4800, B9600, 0, B19200, B57600, B115200
                  };
   unsigned int   charSizeTable[] = {
                     0, CS5, CS6, CS7, CS8
                  };

   // Setup Terminal Parameters
   tcgetattr(td,&t);
   t.c_iflag = 0;                      // input modes
   t.c_oflag = 0;                      // output modes
   t.c_lflag = 0;                      // local modes
   t.c_cc[VMIN] = 0;
   t.c_cc[VTIME] = 0;

   t.c_cflag = CREAD;                  // control modes

   t.c_cflag |= baudRateTable[ baudRateIdx ];

   if (parityIdx == 1)
      t.c_cflag |= PARENB | PARODD;
   else if (parityIdx == 2)
      t.c_cflag |= PARENB;

   t.c_cflag |= charSizeTable[ dataBitsIdx ];

   if (stopBitsIdx == 2)
      t.c_cflag |= CSTOPB;

   if (flowControlIdx < 2) t.c_cflag |= CLOCAL;

   cfsetispeed(&t, baudRateTable[baudRateIdx]);
   cfsetospeed(&t, baudRateTable[baudRateIdx]);
   tcsetattr(td,0,&t);

   return 1;

##endif
}

//----------------------------------------------------------------------
int serialPort::CloseCommConnection( void )
{
##ifdef WIN32
   // set connected flag to 0
   serialConnected = 0;

   // disable event notification and wait for thread to halt
   SetCommMask( idComDev, 0 );

   // drop DTR
   EscapeCommFunction( idComDev, CLRDTR );

   // purge any outstanding reads/writes and close device handle
   PurgeComm( idComDev, PURGE_TXABORT | PURGE_RXABORT |
                        PURGE_TXCLEAR | PURGE_RXCLEAR );

   CloseHandle( idComDev );
   CloseHandle( osRead.hEvent );
   CloseHandle( osWrite.hEvent );

   return 1;

##else
   if (td != -1) {
      close(td);
   }
   return 1;

##endif
}

//----------------------------------------------------------------------
int serialPort::WriteCommBlock( char *lpByte , int dwBytesToWrite )
{
##ifdef WIN32
   int         fWriteStat;
   DWORD       dwBytesWritten;
   DWORD       dwErrorFlags;
   DWORD       dwError;
   DWORD       dwBytesSent=0;
   COMSTAT     ComStat;

   if ( !serialConnected ) return 0;

   fWriteStat = WriteFile( idComDev,
                           (LPSTR)lpByte,
                           dwBytesToWrite,
                           &dwBytesWritten,
                           &osWrite );

   // Note that normally the code will not execute the following
   // because the driver caches write operations. Small I/O requests
   // (up to several thousand bytes) will normally be accepted
   // immediately and WriteFile will return 1 even though an
   // overlapped operation was specified

   if ( !fWriteStat ) {
      if ( GetLastError() == ERROR_IO_PENDING ) {

         // We should wait for the completion of the write operation
         // so we know if it worked or not

         // This is only one way to do this. It might be beneficial
         // to place the write operation in a separate thread
         // so that blocking on completion will not negatively
         // affect the responsiveness of the UI

         // If the write takes too long to complete, this
         // function will timeout according to the
         // CommTimeOuts.WriteTotalTimeoutMultiplier variable.
         // This code logs the timeout but does not retry
         // the write.

         while ( !GetOverlappedResult( idComDev, &osWrite, &dwBytesWritten, 1 ) )
         {
            dwError = GetLastError();
            if ( dwError == ERROR_IO_INCOMPLETE ) {
               // normal result if not finished
               dwBytesSent += dwBytesWritten;
               continue;
            }
            else {
               // an error occurred, try to recover
               ClearCommError( idComDev, &dwErrorFlags, &ComStat );
               break;
            }
         }
         dwBytesSent += dwBytesWritten;
      }
      else {
         // some other error occurred
         ClearCommError( idComDev, &dwErrorFlags, &ComStat );
         return 0;
      }
   }
   return 1;

##else
   int n;

   n = write(td, lpByte, dwBytesToWrite);
   if (n != dwBytesToWrite) return 0;
   return 1;

##endif
}

//----------------------------------------------------------------------
int serialPort::ReadCommBlock( char *lpszBlock, int nMaxLength )
{
##ifdef WIN32
   int         fReadStat;
   COMSTAT     ComStat;
   DWORD       dwErrorFlags;
   DWORD       dwLength;
   DWORD       dwError;

   if ( !serialConnected ) return 0;

   // only try to read number of bytes in queue
   ClearCommError( idComDev, &dwErrorFlags, &ComStat );
   dwLength = min( (DWORD) nMaxLength, ComStat.cbInQue );

   if (dwLength > 0) {

      fReadStat = ReadFile( idComDev,
                            (LPSTR)lpszBlock,
                            dwLength,
                            &dwLength,
                            &osRead );

      if ( !fReadStat ) {
         if ( GetLastError() == ERROR_IO_PENDING ) {

            // We have to wait for read to complete.
            // This function will timeout according to the
            // CommTimeOuts.ReadTotalTimeoutConstant variable
            // Every time it times out, check for port errors

            while ( !GetOverlappedResult( idComDev, &osRead, &dwLength, 1 ) )
            {
               dwError = GetLastError();
               if (dwError == ERROR_IO_INCOMPLETE) {
                  // normal result if not finished
                  continue;
               }
               else {
                  // an error occurred, try to recover
                  ClearCommError( idComDev, &dwErrorFlags, &ComStat );
                  break;
               }
            }
         }
         else {
            // some other error occurred
            dwLength = 0;
            ClearCommError( idComDev, &dwErrorFlags, &ComStat );
         }
      }
   }
   return ( dwLength );

##else
   int dwLength;

   dwLength = read( td, lpszBlock, nMaxLength );
   return ( dwLength );

##endif
}

//----------------------------------------------------------------------
« Last Edit: September 26, 2019, 11:57:18 pm by MarkF »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf