2010년 6월 3일 목요일

Serial Port 연결 (C)

//Comm.cpp Rs232c통신을 하기 위한 클래스
// 2001년 3월 26일 (주) 마이크로 로보트  S/W팀 정웅식
//
#include "stdafx.h"
#include "comm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNCREATE(CComm, CObject)

// 통신프로토콜 Table
BYTE _nFlow[] = {FC_NONE,FC_DTRDSR,FC_RTSCTS,FC_XONXOFF};
// 통신 데이타 사이즈 테이블
int _nDataValues[] = {7,8};
// 통신속도 Table
int _nBaudRates[] = {// CBR_1200, CBR_2400, CBR_4800,
      CBR_9600, CBR_14400, CBR_19200,
      CBR_38400, CBR_56000, CBR_57600,
      CBR_115200,  };
BYTE _nStopBits[] = { ONESTOPBIT, TWOSTOPBITS };
//BOOL bTxEmpty=TRUE;

UINT ReceMessage;

CComm::CComm( )
{
 idComDev=NULL;
 fConnected=FALSE;
 bTxEmpty=TRUE;
}

CComm::~CComm( )
{
    DestroyComm();
}

//BEGIN_MESSAGE_MAP(CComm, CObject)
 //{{AFX_MSG_MAP(CComm)
  // NOTE - the ClassWizard will add and remove mapping macros here.
 //}}AFX_MSG_MAP
//END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CComm message handlers
//CommWatchProc()
//통신을 하는 프로세저 즉 데이타가 들어왔을대 감시하는
//루틴 본루틴은 OpenComPort 함수 실행시 프로시저로 연결됨
//OpenComPort 함수 참조
DWORD CommWatchProc(LPVOID lpData)
{
 DWORD       dwEvtMask ;
 OVERLAPPED  os ;
 CComm*      npComm = (CComm*) lpData ;
 char InData[COM_MAXBLOCK + 1];
 int        nLength ;
 //idCommDev 라는 핸들에 아무런 com 포트가 안붙어 있으면
 // 에라 리턴
 if ( npComm == NULL ||
  !npComm->IsKindOf( RUNTIME_CLASS( CComm ) ) )
  return (DWORD)(-1);

 memset( &os, 0, sizeof( OVERLAPPED ) ) ;


 os.hEvent = CreateEvent( NULL,    // no security
                            TRUE,    // explicit reset req
                            FALSE,   // initial event reset
                            NULL ) ; // no name
 if ( os.hEvent == NULL )
 {
  MessageBox( NULL, "Failed to create event for thread!", "comm Error!",
     MB_ICONEXCLAMATION | MB_OK );
  return ( FALSE ) ;
 }

 DWORD dwEventFlags = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING |
       EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY;
 if (!SetCommMask(npComm->idComDev, dwEventFlags))
  return ( FALSE ) ;

 while (npComm->fConnected)
 {  
  dwEvtMask = 0;
 
  WaitCommEvent(npComm->idComDev, &dwEvtMask, NULL );
 
  if ((dwEvtMask & EV_BREAK) == EV_BREAK)
  {
   ;;;
  }
  else if ((dwEvtMask & EV_CTS) == EV_CTS)
  {
   ;;;
  }
  else if ((dwEvtMask & EV_ERR) == EV_ERR)
  {
   ;;;
  }
  else if ((dwEvtMask & EV_RING) == EV_RING)
  {
   ;;;
  }
  else if ((dwEvtMask & EV_RLSD) == EV_RLSD)
  {
   ;;;
  }
  else if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR)
  {   
   do
   {  
    memset(InData,0,80);
    if (nLength = npComm->ReadCommBlock((LPSTR) InData, COM_MAXBLOCK ))
    {
                  npComm->SetReadData(InData,nLength);
                  //이곳에서 데이타를 받는다.
         }
   }
   while ( nLength > 0 ) ;
   
  }
  else if ((dwEvtMask & EV_RXFLAG) == EV_RXFLAG)
  {
   ;;;
  }
  else if ((dwEvtMask & EV_TXEMPTY) == EV_TXEMPTY)
  {
   npComm->bTxEmpty = TRUE;
  }
 }
 
 CloseHandle( os.hEvent );

 return( TRUE );
}

//데이타를 읽고 데이타를 읽었다는
//메세지를 리턴한다.
void CComm::SetReadData(LPSTR data, int nLen)
{
 memcpy(abIn,data,nLen);
 //설정된 윈도우에 WM_RECEIVEDATA메세지를
 //날려주어 현제 데이타가 들어왔다는것을
 //알려준다.
 
 //SendMessage(m_hwnd,ReceMessage,nLen,0);
 SendMessage(m_hwnd,WM_RECEIVEDATA,nLen,0);
}

//메세지를 전달할 hwnd설정
void CComm::SetHwnd(HWND hwnd)
{
 m_hwnd=hwnd;
}

//com 포트를 열고 연결을 시도한다.
//OpenComport()

BOOL CComm::OpenCommPort(LPTTYSTRUCT lpTTY)
{           
 char       szPort[ 15 ] ;
 BOOL       fRetVal ;
 COMMTIMEOUTS  CommTimeOuts ;

 osWrite.Offset = 0 ;
 osWrite.OffsetHigh = 0 ;
 osRead.Offset = 0 ;
 osRead.OffsetHigh = 0 ;

 //이벤트 창구 설정
 osRead.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
 if (osRead.hEvent == NULL)
 {
  return FALSE ;
 }
 osWrite.hEvent = CreateEvent( NULL,   TRUE,  FALSE,   NULL );
 if (NULL == osWrite.hEvent)
 {
  CloseHandle( osRead.hEvent );
  return FALSE;
 }

  
 if (lpTTY->byCommPort > COM_MAXPORTS)
  lstrcpy( szPort, "\\\\.\\TELNET" ) ;
 else
  wsprintf( szPort, "COM%d", lpTTY->byCommPort );

 // COMM device를 화일형식으로 연결한다.

 if ((idComDev =
  CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
                  0,                    // exclusive access
                  NULL,                 // no security attrs
                  OPEN_EXISTING,
                  FILE_ATTRIBUTE_NORMAL |
                  FILE_FLAG_OVERLAPPED, // overlapped I/O
                  NULL )) == (HANDLE) -1 )
  return ( FALSE ) ;
 else
 {
  //컴포트에서 데이타를 교환하는 방법을 char단위를 기본으로 설정하자
  SetCommMask( idComDev, EV_RXCHAR ) ;
  SetupComm( idComDev, 4096, 4096 ) ;
  //디바이스에 쓰레기가 있을지 모르니까 깨끗이 청소를 하자!
  PurgeComm( idComDev, PURGE_TXABORT | PURGE_RXABORT |
                                      PURGE_TXCLEAR | PURGE_RXCLEAR ) ;

    
  CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF ;
  CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
  CommTimeOuts.ReadTotalTimeoutConstant = 1000 ;
  CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ;
  CommTimeOuts.WriteTotalTimeoutConstant = 1000 ;
  SetCommTimeouts( idComDev, &CommTimeOuts ) ;
 }

 fRetVal = SetupConnection(lpTTY) ;

 if (fRetVal)//연결이 되었다면 fRetVal TRUE이므로
 {
  fConnected = TRUE ;//연결되었다고 말해줌
  //프로시전를 CommWatchProc에 연결하니까 나중에 데이타가 왔다갔다
  //하면 모든 내용은 CommWatchProc가 담당한다.
  AfxBeginThread((AFX_THREADPROC)CommWatchProc,(LPVOID)this);
 }
 else
 {
  fConnected = FALSE ;
  CloseHandle( idComDev) ;
 }

 return ( fRetVal ) ;
}

//화일로 설정된 컴포트와 실질 포트와 연결을 시킨다.
//SetupConnection 이전에 CreateComPort를 해주어야 한다.
BOOL CComm::SetupConnection(LPTTYSTRUCT lpTTY)
{
 BOOL       fRetVal;
 DCB        dcb;

 dcb.DCBlength = sizeof( DCB ) ;

 GetCommState( idComDev, &dcb ) ;//dcb의 기본값을 받는다.


 dcb.BaudRate = lpTTY->dwBaudRate;
 dcb.ByteSize = lpTTY->byByteSize;
 dcb.Parity = lpTTY->byParity;
 dcb.StopBits = lpTTY->byStopBits;
 
 // setup hardware flow control FC_DTRDSR
 BOOL bSet = (BYTE)((lpTTY->byFlowCtrl & FC_DTRDSR) != 0);

 dcb.fOutxDsrFlow = bSet;
 
 if(bSet)
  dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
 else
  dcb.fDtrControl = DTR_CONTROL_ENABLE;
 
 // FC_RTSCTS
 bSet = (BYTE)((lpTTY->byFlowCtrl & FC_RTSCTS) != 0);
 dcb.fOutxCtsFlow = bSet;
 
 if(bSet)
  dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
 else
  dcb.fRtsControl = RTS_CONTROL_ENABLE;
 
 // setup software flow control FC_XONXOFF
 bSet = (BYTE)((lpTTY->byFlowCtrl & FC_XONXOFF) != 0);

 dcb.fInX = dcb.fOutX = bSet;
 dcb.XonChar = ASCII_XON;
 dcb.XoffChar = ASCII_XOFF;
 dcb.XonLim = 1;
 dcb.XoffLim = 1;
 
 // other various settings
 dcb.fBinary = TRUE;
 dcb.fParity = TRUE;

 fRetVal = SetCommState( idComDev, &dcb ) ; //변경된 Dcb 설정

 return ( fRetVal ) ;  
}

//컴포트로 부터 데이타를 읽는다.
int CComm::ReadCommBlock(LPSTR lpszBlock, int nMaxLength )
{
 BOOL       fReadStat ;
 COMSTAT    ComStat ;
 DWORD      dwErrorFlags;
 DWORD      dwLength;

 // 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, lpszBlock, dwLength, &dwLength, &osRead ) ;
  if (!fReadStat)
  {
          if(GetLastError() == ERROR_IO_PENDING)
   {
    OutputDebugString("\n\rIO 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, TRUE))
    {
     DWORD dwError = GetLastError();
     if(dwError == ERROR_IO_INCOMPLETE)
     {
      // normal result if not finished
      continue;
     }
     else
     {
      // CAN DISPLAY ERROR MESSAGE HERE
      // an error occured, try to recover
      ::ClearCommError(idComDev, &dwErrorFlags, &ComStat);
      if(dwErrorFlags > 0)
      {
       // CAN DISPLAY ERROR MESSAGE HERE
      }
      break;
     }
    }
   }
   else
   {
    // some other error occured
    dwLength = 0;
    ClearCommError(idComDev, &dwErrorFlags, &ComStat);
    if(dwErrorFlags > 0)
    {
     // CAN DISPLAY ERROR MESSAGE HERE
    }
   }
  }
   }
  
   return ( dwLength ) ;
}

// 컴포트에 데이터를 쓴다.
BOOL CComm::WriteCommBlock( LPSTR lpByte , DWORD dwBytesToWrite)
{
 DWORD  dwErrorFlags;
 BOOL        fWriteStat ;
 DWORD       dwBytesWritten ;
 COMSTAT ComStat;

 DWORD dwLength = dwBytesToWrite;

 bTxEmpty = FALSE;
 fWriteStat = WriteFile( idComDev, lpByte, dwBytesToWrite, &dwBytesWritten, &osWrite ) ;
 //if (dwBytesToWrite != dwBytesWritten)
 
 if(!fWriteStat)
 {
  if(GetLastError() == ERROR_IO_PENDING)
  {
   OutputDebugString("\n\rIO Pending");
   // 읽을 문자가 남아 있거나 전송할 문자가 남아 있을 경우 Overapped IO의
   // 특성에 따라 ERROR_IO_PENDING 에러 메시지가 전달된다.
   //timeouts에 정해준 시간만큼 기다려준다.
   while(!GetOverlappedResult(idComDev, &osWrite,
      &dwLength, TRUE))
   {
    DWORD dwError = GetLastError();
    if(dwError == ERROR_IO_INCOMPLETE)
    {
     // normal result if not finished
     continue;
    }
    else
    {
     // CAN DISPLAY ERROR MESSAGE HERE
     // an error occured, try to recover
     ::ClearCommError(idComDev, &dwErrorFlags, &ComStat);
     if(dwErrorFlags > 0)
     {
      // CAN DISPLAY ERROR MESSAGE HERE
      AfxMessageBox("ERROR");
     }
     break;
    }
   }
  }
  else
  { // some other error occured
   dwLength = 0;
   ClearCommError(idComDev, &dwErrorFlags, &ComStat);
   if(dwErrorFlags > 0)
   {
    // CAN DISPLAY ERROR MESSAGE HERE    
    AfxMessageBox("ERROR");
   }
  } 
 }

 return ( TRUE ) ;
}


//컴포트를 완전히 해제한다.
BOOL CComm::DestroyComm()
{
 if (fConnected)
  CloseConnection();

 if (osRead.hEvent!=NULL)
  CloseHandle( osRead.hEvent ) ;

 if (osWrite.hEvent != NULL)
  CloseHandle( osWrite.hEvent ) ;

 return ( TRUE ) ;
}

//연결을 닫는다.
BOOL CComm::CloseConnection()
{

   // set connected flag to FALSE

   fConnected = FALSE ;

   // disable event notification and wait for thread
   // to halt

   SetCommMask( idComDev, 0 ) ;


   EscapeCommFunction( CLRDTR ) ;
   EscapeCommFunction( CLRRTS ) ;


   PurgeComm( idComDev, PURGE_TXABORT | PURGE_RXABORT |
                                   PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
   CloseHandle( idComDev ) ;
 
   return ( TRUE ) ;

}

void CComm::EscapeCommFunction(DWORD dwFunc)
{
 // TODO: Add your control notification handler code here
 ::EscapeCommFunction(idComDev,dwFunc);
}

댓글 없음:

댓글 쓰기