2010년 6월 3일 목요일

Serial Port MFC

// SerialDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Serial.h"
#include "SerialDlg.h"
#include "comm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
 CAboutDlg();

// Dialog Data
 enum { IDD = IDD_ABOUTBOX };

 protected:
 virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
 DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{

}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CSerialDlg dialog

 


CSerialDlg::CSerialDlg(CWnd* pParent /*=NULL*/)
 : CDialog(CSerialDlg::IDD, pParent)
{
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSerialDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 DDX_Text(pDX, IDC_EDIT1, m_EditData);
 DDX_CBIndex(pDX, IDC_PORT, m_nSettingPort);
 DDX_CBIndex(pDX, IDC_BAUDRATE, m_nSettingBaud);
}

BEGIN_MESSAGE_MAP(CSerialDlg, CDialog)
 ON_WM_SYSCOMMAND()
 ON_WM_PAINT()
 ON_WM_QUERYDRAGICON()
 //}}AFX_MSG_MAP
 ON_BN_CLICKED(IDC_CHECK_COMM, &CSerialDlg::OnBnClickedCheckComm)
 ON_BN_CLICKED(IDOK, &CSerialDlg::OnBnClickedOk)
 ON_BN_CLICKED(IDCANCEL, &CSerialDlg::OnBnClickedCancel)
 ON_MESSAGE(WM_RECEIVEDATA,OnReceiveData)

END_MESSAGE_MAP()


// CSerialDlg message handlers

BOOL CSerialDlg::OnInitDialog()
{
 CDialog::OnInitDialog();

 // Add "About..." menu item to system menu.

 // IDM_ABOUTBOX must be in the system command range.
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);

 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 // Set the icon for this dialog.  The framework does this automatically
 //  when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE);   // Set big icon
 SetIcon(m_hIcon, FALSE);  // Set small icon

 // TODO: Add extra initialization here
 //for(int i=0; i<2; i++){
  m_Comm.SetHwnd(this->m_hWnd);
  LoadSettings();

 //}
 UpdateData(FALSE);

 return TRUE;  // return TRUE  unless you set the focus to a control
}

void CSerialDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
 if ((nID & 0xFFF0) == IDM_ABOUTBOX)
 {
  CAboutDlg dlgAbout;
  dlgAbout.DoModal();
 }
 else
 {
  CDialog::OnSysCommand(nID, lParam);
 }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CSerialDlg::OnPaint()
{
 if (IsIconic())
 {
  CPaintDC dc(this); // device context for painting

  SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

  // Center icon in client rectangle
  int cxIcon = GetSystemMetrics(SM_CXICON);
  int cyIcon = GetSystemMetrics(SM_CYICON);
  CRect rect;
  GetClientRect(&rect);
  int x = (rect.Width() - cxIcon + 1) / 2;
  int y = (rect.Height() - cyIcon + 1) / 2;

  // Draw the icon
  dc.DrawIcon(x, y, m_hIcon);
 }
 else
 {
  CDialog::OnPaint();
 }
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CSerialDlg::OnQueryDragIcon()
{
 return static_cast<HCURSOR>(m_hIcon);
}


void CSerialDlg::OnBnClickedCheckComm()
{
 // TODO: Add your control notification handler code here
 UpdateData(TRUE);
 BOOL bCheck =((CButton*)GetDlgItem(IDC_CHECK_COMM))->GetCheck();
 if(bCheck)//통신시작
 {
 // for(int i=0; i<2; i++){

   if(m_Comm.OpenCommPort(&Int2TTY())!=TRUE)
   {
    CString str;
    str.Format("COM%d이 이미 사용중인지 확인하세요",m_nSettingPort);
    AfxMessageBox(str);

    ((CButton*)GetDlgItem(IDC_CHECK_COMM))->SetCheck(!bCheck);
    return;
   
   }
 

  GetDlgItem(IDC_CHECK_COMM)->SetWindowTextA("통신종료");
 
  GetDlgItem(IDC_PORT)->EnableWindow(FALSE);
  GetDlgItem(IDC_PORT2)->EnableWindow(FALSE);

  GetDlgItem(IDC_BAUDRATE)->EnableWindow(FALSE);
 }
 
 else{
  //for(int i=0; i<2; i++){
   m_Comm.CloseConnection();
  //}
  GetDlgItem(IDC_CHECK_COMM)->SetWindowTextA("통신시작");
  GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
  GetDlgItem(IDC_PORT2)->EnableWindow(TRUE);
  GetDlgItem(IDC_BAUDRATE)->EnableWindow(TRUE);
 }
}
TTYSTRUCT CSerialDlg::Int2TTY()
{
 TTYSTRUCT tty;
 ZERO_MEMORY(tty);

 tty.byCommPort = (BYTE) m_nSettingPort;
 tty.byXonXoff = FALSE;
 tty.byByteSize = (BYTE)_nDataValues[m_nSettingData];
 tty.byFlowCtrl = (BYTE)_nFlow[m_nSettingFlow];
 tty.byParity = (BYTE)m_nSettingParity;
 tty.byStopBits = (BYTE)_nStopBits[m_nSettingStop];
 tty.dwBaudRate = (DWORD)_nBaudRates[m_nSettingBaud];
 return tty;
}
TTYSTRUCT CSerialDlg::LoadSettings()
{
 CWinApp *pApp = AfxGetApp();

 m_nSettingPort = pApp->GetProfileInt(CS_REGKEY_SETTINGS, CS_REGENTRY_PORT,1);
 m_nSettingParity = 0;
 m_nSettingBaud = pApp->GetProfileIntA(CS_REGKEY_SETTINGS, CS_REGENTRY_BAUD,1);
 m_nSettingData = 1;
 m_nSettingStop = 0;
 m_nSettingFlow = 0;

 return Int2TTY();

}
void CSerialDlg::SaveSettings()
{
 CWinApp *pApp = AfxGetApp();
 pApp->WriteProfileInt(CS_REGKEY_SETTINGS, CS_REGENTRY_PORT, m_nSettingPort);
 pApp->WriteProfileInt(CS_REGKEY_SETTINGS, CS_REGENTRY_PARITY,m_nSettingParity);
 pApp->WriteProfileInt(CS_REGKEY_SETTINGS, CS_REGENTRY_BAUD, m_nSettingBaud);
 pApp->WriteProfileInt(CS_REGKEY_SETTINGS, CS_REGENTRY_DATABITS, m_nSettingData);
 pApp->WriteProfileInt(CS_REGKEY_SETTINGS, CS_REGENTRY_STOPBITS, m_nSettingStop);
 pApp->WriteProfileInt(CS_REGKEY_SETTINGS, CS_REGENTRY_FLOW, m_nSettingFlow);
}
void CSerialDlg::OnBnClickedOk()
{
 // TODO: Add your control notification handler code here

 char start[] = {0x02, 0x00, 0xf0};
//for(int i=0; i<2; i++)
  m_Comm.WriteCommBlock(start,3);
// SaveSettings();
// CDialog::OnOK();
}

void CSerialDlg::OnBnClickedCancel()
{
// TODO: Add your control notification handler code here
// for(int i=0; i<2; i++)
// { 
  SaveSettings();
  m_Comm.CloseConnection();
 //}
  CDialog::OnCancel();
}
LONG CSerialDlg::OnReceiveData(UINT WParam, LONG a)
{
 int i;
 int nLen = WParam;
 CString str;

 UpdateData(TRUE);
 BYTE ch;
 int temp;

// for(int j=0; j<2; j++){
  for(i=0;i<nLen;i++)
  {
   ch =(int)m_Comm.abIn[i];
   str.Format("%x",ch);
   m_EditData += str;
  }
// }
 UpdateData(FALSE);
 return TRUE;
}

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);
}