S 串口编程 详解2 CSerialPort类 (SerialPort.h头文件和SerialPort.ccp实现文件)

来源:互联网 发布:fpga 开发板 数据采集 编辑:程序博客网 时间:2024/06/08 12:27

串口编程 详解2   CSerialPort类   (SerialPort.h头文件和SerialPort.ccp实现文件)

这个类也是从网上找的,目前只用了其中的 InitPort( )成员函数函数。assert(portnr > 0 && portnr < 5);这个值能用到COM1、COM2、COM3、COM4,用到COM5及以上就会报错的,如果要使用更多的COM,则修改portnr<5的值。

SerialPort.h头文件

#ifndef __SERIALPORT_H__#define __SERIALPORT_H__#define WM_COMM_BREAK_DETECTED  WM_USER+1 // A break was detected on input.#define WM_COMM_CTS_DETECTED  WM_USER+2 // The CTS (clear-to-send) signal changed state. #define WM_COMM_DSR_DETECTED  WM_USER+3 // The DSR (data-set-ready) signal changed state. #define WM_COMM_ERR_DETECTED  WM_USER+4 // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. #define WM_COMM_RING_DETECTED  WM_USER+5 // A ring indicator was detected. #define WM_COMM_RLSD_DETECTED  WM_USER+6 // The RLSD (receive-line-signal-detect) signal changed state. #define WM_COMM_RXCHAR    WM_USER+7 // A character was received and placed in the input buffer. #define WM_COMM_RXFLAG_DETECTED  WM_USER+8 // The event character was received and placed in the input buffer.  #define WM_COMM_TXEMPTY_DETECTED WM_USER+9 // The last character in the output buffer was sent. class CSerialPort{               public: int m_nWriteSize; void ClosePort(); // contruction and destruction CSerialPort(); virtual  ~CSerialPort(); // port initialisation            BOOL  InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 19200, char parity = 'N', UINT databits = 8, UINT stopbits = 1, DWORD dwCommEvents = EV_RXCHAR, UINT writebuffersize = 1024); HANDLE    m_hComm; // start/stop comm watching BOOL  StartMonitoring(); BOOL  RestartMonitoring(); BOOL  StopMonitoring(); DWORD  GetWriteBufferSize(); DWORD  GetCommEvents(); DCB   GetDCB(); void  WriteToPort(char* string); void  WriteToPort(char* string,int n); void  WriteToPort(LPCTSTR string); void  WriteToPort(LPCTSTR string,int n);protected: // protected memberfunctions void  ProcessErrorMessage(char* ErrorText); static UINT CommThread(LPVOID pParam); static void ReceiveChar(CSerialPort* port, COMSTAT comstat); static void WriteChar(CSerialPort* port); // thread CWinThread*   m_Thread; // synchronisation objects CRITICAL_SECTION m_csCommunicationSync; BOOL    m_bThreadAlive; // handles HANDLE    m_hWriteEvent; HANDLE    m_hShutdownEvent; // Event array.  // One element is used for each event. There are two event handles for each port. // A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent). // There is a general shutdown when the port is closed.  HANDLE    m_hEventArray[3]; // structures OVERLAPPED   m_ov; COMMTIMEOUTS  m_CommTimeouts; DCB     m_dcb; // owner window CWnd*    m_pOwner; // misc UINT    m_nPortNr; char*    m_szWriteBuffer; DWORD    m_dwCommEvents; DWORD    m_nWriteBufferSize;};#endif __SERIALPORT_H__

SerialPort.ccp实现文件

#include "stdafx.h"#include "SerialPort.h"#include <assert.h> //// Constructor//CSerialPort::CSerialPort(){ m_hComm = NULL; // initialize overlapped structure members to zero m_ov.Offset = 0; m_ov.OffsetHigh = 0; // create events m_ov.hEvent = NULL; m_hWriteEvent = NULL; m_hShutdownEvent = NULL; m_szWriteBuffer = NULL; m_nWriteSize=1; m_bThreadAlive = FALSE;}//// Delete dynamic memory//CSerialPort::~CSerialPort(){ do {  SetEvent(m_hShutdownEvent); } while (m_bThreadAlive); TRACE("Thread ended/n"); delete [] m_szWriteBuffer;}//// Initialize the port. This can be port 1 to 4.//BOOL CSerialPort::InitPort(CWnd* pPortOwner, // the owner (CWnd) of the port (receives message)         UINT  portnr,  // portnumber (1..4)         UINT  baud,   // baudrate         char  parity,  // parity          UINT  databits,  // databits          UINT  stopbits,  // stopbits          DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc         UINT  writebuffersize) // size to the writebuffer{ assert(portnr > 0 && portnr < 5); assert(pPortOwner != NULL); // if the thread is alive: Kill if (m_bThreadAlive) {  do  {   SetEvent(m_hShutdownEvent);  } while (m_bThreadAlive);  TRACE("Thread ended/n"); } // create events if (m_ov.hEvent != NULL)  ResetEvent(m_ov.hEvent); m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (m_hWriteEvent != NULL)  ResetEvent(m_hWriteEvent); m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  if (m_hShutdownEvent != NULL)  ResetEvent(m_hShutdownEvent); m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // initialize the event objects m_hEventArray[0] = m_hShutdownEvent; // highest priority m_hEventArray[1] = m_ov.hEvent; m_hEventArray[2] = m_hWriteEvent; // initialize critical section InitializeCriticalSection(&m_csCommunicationSync);  // set buffersize for writing and save the owner m_pOwner = pPortOwner; if (m_szWriteBuffer != NULL)  delete [] m_szWriteBuffer; m_szWriteBuffer = new char[writebuffersize]; m_nPortNr = portnr; m_nWriteBufferSize = writebuffersize; m_dwCommEvents = dwCommEvents; BOOL bResult = FALSE; char *szPort = new char[50]; char *szBaud = new char[50]; // now it critical! EnterCriticalSection(&m_csCommunicationSync); // if the port is already opened: close it if (m_hComm != NULL) {  CloseHandle(m_hComm);  m_hComm = NULL; } // prepare port strings sprintf(szPort, "COM%d", portnr); sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits); // get a handle to the port m_hComm = CreateFile(szPort,      // communication port string (COMX)          GENERIC_READ | GENERIC_WRITE, // read/write types          0,        // comm devices must be opened with exclusive access          NULL,       // no security attributes          OPEN_EXISTING,     // comm devices must use OPEN_EXISTING          FILE_FLAG_OVERLAPPED,   // Async I/O          0);       // template must be 0 for comm devices if (m_hComm == INVALID_HANDLE_VALUE) {  // port not found  delete [] szPort;  delete [] szBaud;  return FALSE; } // set the timeout values m_CommTimeouts.ReadIntervalTimeout = 1000; m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000; m_CommTimeouts.ReadTotalTimeoutConstant = 1000; m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000; m_CommTimeouts.WriteTotalTimeoutConstant = 1000; // configure if (SetCommTimeouts(m_hComm, &m_CommTimeouts)) {           if (SetCommMask(m_hComm, dwCommEvents))  {   if (GetCommState(m_hComm, &m_dcb))   {    m_dcb.EvtChar = 'q';    m_dcb.fRtsControl = RTS_CONTROL_ENABLE;  // set RTS bit high!    if (BuildCommDCB(szBaud, &m_dcb))    {     if (SetCommState(m_hComm, &m_dcb))      ; // normal operation... continue     else      ProcessErrorMessage("SetCommState()");    }    else     ProcessErrorMessage("BuildCommDCB()");   }   else    ProcessErrorMessage("GetCommState()");  }  else   ProcessErrorMessage("SetCommMask()"); } else  ProcessErrorMessage("SetCommTimeouts()"); delete [] szPort; delete [] szBaud; // flush the port PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); // release critical section LeaveCriticalSection(&m_csCommunicationSync); TRACE("Initialisation for communicationport %d completed./nUse Startmonitor to communicate./n", portnr); return TRUE;}////  The CommThread Function.//UINT CSerialPort::CommThread(LPVOID pParam){ // Cast the void pointer passed to the thread back to // a pointer of CSerialPort class CSerialPort *port = (CSerialPort*)pParam;  // Set the status variable in the dialog class to // TRUE to indicate the thread is running. port->m_bThreadAlive = TRUE;    // Misc. variables DWORD BytesTransfered = 0;  DWORD Event = 0; DWORD CommEvent = 0; DWORD dwError = 0; COMSTAT comstat; BOOL  bResult = TRUE;   // Clear comm buffers at startup if (port->m_hComm)  // check if the port is opened  PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); // begin forever loop.  This loop will run as long as the thread is alive. for (;;)  {  // Make a call to WaitCommEvent().  This call will return immediatly  // because our port was created as an async port (FILE_FLAG_OVERLAPPED  // and an m_OverlappedStructerlapped structure specified).  This call will cause the   // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to   // be placed in a non-signeled state if there are no bytes available to be read,  // or to a signeled state if there are bytes available.  If this event handle   // is set to the non-signeled state, it will be set to signeled when a   // character arrives at the port.  // we do this for each port!  bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);  if (!bResult)    {    // If WaitCommEvent() returns FALSE, process the last error to determin   // the reason..   switch (dwError = GetLastError())    {    case ERROR_IO_PENDING:      {      // This is a normal return value if there are no bytes     // to read at the port.     // Do nothing and continue     break;    }   case 87:    {     // Under Windows NT, this value is returned for some reason.     // I have not investigated why, but it is also a valid reply     // Also do nothing and continue.     break;    }   default:    {     // All other error codes indicate a serious error has     // occured.  Process this error.     port->ProcessErrorMessage("WaitCommEvent()");     break;    }   }  }  else  {   // If WaitCommEvent() returns TRUE, check to be sure there are   // actually bytes in the buffer to read.     //   // If you are reading more than one byte at a time from the buffer    // (which this program does not do) you will have the situation occur    // where the first byte to arrive will cause the WaitForMultipleObjects()    // function to stop waiting.  The WaitForMultipleObjects() function    // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state   // as it returns.     //   // If in the time between the reset of this event and the call to    // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again   // to the signeled state. When the call to ReadFile() occurs, it will    // read all of the bytes from the buffer, and the program will   // loop back around to WaitCommEvent().   //    // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,   // but there are no bytes available to read.  If you proceed and call   // ReadFile(), it will return immediatly due to the async port setup, but   // GetOverlappedResults() will not return until the next character arrives.   //   // It is not desirable for the GetOverlappedResults() function to be in    // this state.  The thread shutdown event (event 0) and the WriteFile()   // event (Event2) will not work if the thread is blocked by GetOverlappedResults().   //   // The solution to this is to check the buffer with a call to ClearCommError().   // This call will reset the event handle, and if there are no bytes to read   // we can loop back through WaitCommEvent() again, then proceed.   // If there are really bytes to read, do nothing and proceed.     bResult = ClearCommError(port->m_hComm, &dwError, &comstat);   if (comstat.cbInQue == 0)    continue;  } // end if bResult  // Main wait function.  This function will normally block the thread  // until one of nine events occur that require action.  Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);  switch (Event)  {  case 0:   {    // Shutdown event.  This is event zero so it will be    // the higest priority and be serviced first.    CloseHandle(port->m_hComm);    port->m_hComm=NULL;    port->m_bThreadAlive = FALSE;        // Kill this thread.  break is not needed, but makes me feel better.    AfxEndThread(100);    break;   }  case 1: // read event   {    GetCommMask(port->m_hComm, &CommEvent);    if (CommEvent & EV_RXCHAR)     // Receive character event from port.     ReceiveChar(port, comstat);    if (CommEvent & EV_CTS)     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);    if (CommEvent & EV_BREAK)     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);    if (CommEvent & EV_ERR)     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);    if (CommEvent & EV_RING)     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);        if (CommEvent & EV_RXFLAG)     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);         break;   }    case 2: // write event   {    // Write character event from port    WriteChar(port);    break;   }  } // end switch } // close forever loop return 0;}//// start comm watching//BOOL CSerialPort::StartMonitoring(){ if (!(m_Thread = AfxBeginThread(CommThread, this)))  return FALSE; TRACE("Thread started/n"); return TRUE; }//// Restart the comm thread//BOOL CSerialPort::RestartMonitoring(){ TRACE("Thread resumed/n"); m_Thread->ResumeThread(); return TRUE; }//// Suspend the comm thread//BOOL CSerialPort::StopMonitoring(){ TRACE("Thread suspended/n"); m_Thread->SuspendThread();  return TRUE; }//// If there is a error, give the right message//void CSerialPort::ProcessErrorMessage(char* ErrorText){ char *Temp = new char[200];  LPVOID lpMsgBuf; FormatMessage(   FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,  NULL,  GetLastError(),  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language  (LPTSTR) &lpMsgBuf,  0,  NULL  ); sprintf(Temp, "WARNING:  %s Failed with the following error: /n%s/nPort: %d/n", (char*)ErrorText, lpMsgBuf, m_nPortNr);  MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP); LocalFree(lpMsgBuf); delete [] Temp;}//// Write a character.//void CSerialPort::WriteChar(CSerialPort* port){ BOOL bWrite = TRUE; BOOL bResult = TRUE; DWORD BytesSent = 0; ResetEvent(port->m_hWriteEvent); // Gain ownership of the critical section EnterCriticalSection(&port->m_csCommunicationSync); if (bWrite) {  // Initailize variables  port->m_ov.Offset = 0;  port->m_ov.OffsetHigh = 0;  // Clear buffer  PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  bResult = WriteFile(port->m_hComm,       // Handle to COMM Port       port->m_szWriteBuffer,     // Pointer to message buffer in calling finction//       strlen((char*)port->m_szWriteBuffer), // Length of message to send       port->m_nWriteSize, // Length of message to send       &BytesSent,        // Where to store the number of bytes sent       &port->m_ov);       // Overlapped structure  // deal with any error codes  if (!bResult)    {   DWORD dwError = GetLastError();   switch (dwError)   {    case ERROR_IO_PENDING:     {      // continue to GetOverlappedResults()      BytesSent = 0;      bWrite = FALSE;      break;     }    default:     {      // all other error codes      port->ProcessErrorMessage("WriteFile()");     }   }  }   else  {   LeaveCriticalSection(&port->m_csCommunicationSync);  } } // end if(bWrite) if (!bWrite) {  bWrite = TRUE;   bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port            &port->m_ov,  // Overlapped structure           &BytesSent,  // Stores number of bytes sent           TRUE);    // Wait flag  LeaveCriticalSection(&port->m_csCommunicationSync);  // deal with the error code //  if (!bResult)    {//   port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");  }  } // end if (!bWrite) // Verify that the data size send equals what we tried to send// if (BytesSent != strlen((char*)port->m_szWriteBuffer)) {//  TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d/n", BytesSent, strlen((char*)port->m_szWriteBuffer)); }// ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_TXEMPTY_DETECTED, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr); ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_TXEMPTY_DETECTED,0,(LPARAM) port->m_nPortNr);}//// Character received. Inform the owner//void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat){ BOOL  bRead = TRUE;  BOOL  bResult = TRUE; DWORD dwError = 0; DWORD BytesRead = 0; unsigned char RXBuff; for (;;)  {   // Gain ownership of the comm port critical section.  // This process guarantees no other part of this program   // is using the port object.     EnterCriticalSection(&port->m_csCommunicationSync);  // ClearCommError() will update the COMSTAT structure and  // clear any other errors.    bResult = ClearCommError(port->m_hComm, &dwError, &comstat);  LeaveCriticalSection(&port->m_csCommunicationSync);  // start forever loop.  I use this type of loop because I  // do not know at runtime how many loops this will have to  // run. My solution is to start a forever loop and to  // break out of it when I have processed all of the  // data available.  Be careful with this approach and  // be sure your loop will exit.  // My reasons for this are not as clear in this sample   // as it is in my production code, but I have found this   // solutiion to be the most efficient way to do this.    if (comstat.cbInQue == 0)  {   // break out when all bytes have been read   break;  }        EnterCriticalSection(&port->m_csCommunicationSync);  if (bRead)  {   bResult = ReadFile(port->m_hComm,  // Handle to COMM port           &RXBuff,    // RX Buffer Pointer          1,     // Read one byte          &BytesRead,   // Stores number of bytes read          &port->m_ov);  // pointer to the m_ov structure   // deal with the error code    if (!bResult)     {     switch (dwError = GetLastError())     {      case ERROR_IO_PENDING:        {        // asynchronous i/o is still in progress        // Proceed on to GetOverlappedResults();       bRead = FALSE;       break;      }     default:      {       // Another error has occured.  Process this error.       port->ProcessErrorMessage("ReadFile()");       break;      }     }   }   else   {    // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()    bRead = TRUE;   }  }  // close if (bRead)  if (!bRead)  {   bRead = TRUE;   bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port             &port->m_ov,  // Overlapped structure            &BytesRead,  // Stores number of bytes read            TRUE);    // Wait flag   // deal with the error code    if (!bResult)     {    port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");   }   }  // close if (!bRead)      LeaveCriticalSection(&port->m_csCommunicationSync);  // notify parent that a byte was received  ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr); } // end forever loop}//// Write a string to the port//void CSerialPort::WriteToPort(char* string){   assert(m_hComm != 0);  memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); strcpy(m_szWriteBuffer, string); m_nWriteSize=strlen(string); // set event for write SetEvent(m_hWriteEvent);}void CSerialPort::WriteToPort(char* string,int n){   assert(m_hComm != 0); memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));// memset(m_szWriteBuffer, 0, n);// strncpy(m_szWriteBuffer, string, n); memcpy(m_szWriteBuffer, string, n); m_nWriteSize=n; // set event for write SetEvent(m_hWriteEvent);}void CSerialPort::WriteToPort(LPCTSTR string){   assert(m_hComm != 0); memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); strcpy(m_szWriteBuffer, string); m_nWriteSize=strlen(string); // set event for write SetEvent(m_hWriteEvent);}void CSerialPort::WriteToPort(LPCTSTR string,int n){   assert(m_hComm != 0); memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));// strncpy(m_szWriteBuffer, string, n); memcpy(m_szWriteBuffer, string, n); m_nWriteSize=n; // set event for write SetEvent(m_hWriteEvent);}//// Return the device control block//DCB CSerialPort::GetDCB(){ return m_dcb;}//// Return the communication event masks//DWORD CSerialPort::GetCommEvents(){ return m_dwCommEvents;}//// Return the output buffer size//DWORD CSerialPort::GetWriteBufferSize(){ return m_nWriteBufferSize;}void CSerialPort::ClosePort(){ SetEvent(m_hShutdownEvent);}