[C/C++] 시리얼 통신 (Serial Port)


 최근 회사에서 GPS 수신을 위해 Serial 통신 관련 개발을 잠깐 하게되어 포스팅 합니다.

 어짜피 GPS가 아니더라도 Serial 통신이란게 어짜피 Com Port 를 통해 Read Write 하는 구조는 같고, 이외에도 써먹을만한데는 많을 것 같아 적어두면 좋겠다 했죠 :)

 Serial 통신은 Port의 Open과 Close, 그리고 Read, Write 의 네가지로 모든 것이 이루어집니다. 좀더 깊게 들어간다면 BaudRate, StopBit, Parity 등 알아야 할 것이 많지만, 저는 기본적으로 사용 방법정도만 설명할 생각입니다.

 이쪽 전문가가 아니기 때문도 있구요.

 아래는 통신을 간단하게 사용하기 위한 클래스입니다.

 - SerialPort.h

  1. class CSerialPort  
  2. {  
  3. public:  
  4.     CSerialPort(void);  
  5.     virtual ~CSerialPort(void);  
  6.   
  7. private:  
  8.     HANDLE  m_hComm;  
  9.     DCB     m_dcb;  
  10.     COMMTIMEOUTS m_CommTimeouts;  
  11.     bool    m_bPortReady;  
  12.     bool    m_bWriteRC;  
  13.     bool    m_bReadRC;  
  14.     DWORD   m_iBytesWritten;  
  15.     DWORD   m_iBytesRead;  
  16.     DWORD   m_dwBytesRead;  
  17.   
  18. public:  
  19.     void ClosePort();  
  20.     bool ReadByte(BYTE &resp);  
  21.     bool ReadByte(BYTE* &resp, UINT size);  
  22.     bool WriteByte(BYTE bybyte);  
  23.     bool OpenPort(CString portname);  
  24.     bool SetCommunicationTimeouts(DWORD ReadIntervalTimeout,  
  25.         DWORD ReadTotalTimeoutMultiplier, DWORD ReadTotalTimeoutConstant,  
  26.         DWORD WriteTotalTimeoutMultiplier,DWORD WriteTotalTimeoutConstant);  
  27.     bool ConfigurePort(DWORD BaudRate, BYTE ByteSize, DWORD fParity,   
  28.         BYTE  Parity,BYTE StopBits);  
  29. }  

 - SerialPort.cpp

  1. #include "SerialPort.h"  
  2.   
  3. CSerialPort::CSerialPort()  
  4. {  
  5. }  
  6.   
  7. CSerialPort::~CSerialPort()  
  8. {  
  9. }  
  10.   
  11. bool CSerialPort::OpenPort(CString portname)  
  12. {  
  13.     m_hComm = CreateFile(L"//./" + portname,  
  14.             GENERIC_READ | GENERIC_WRITE,  
  15.             0,  
  16.             0,  
  17.             OPEN_EXISTING,  
  18.             0,  
  19.             0);  
  20.         if(m_hComm == INVALID_HANDLE_VALUE)  
  21.         {  
  22.             return false;  
  23.         }  
  24.         else  
  25.             return true;  
  26. }  
  27.   
  28. bool CSerialPort::ConfigurePort(DWORD BaudRate, BYTE ByteSize, DWORD fParity,   
  29.         BYTE Parity, BYTE StopBits)  
  30. {  
  31.     if((m_bPortReady = GetCommState(m_hComm, &m_dcb))==0)  
  32.     {  
  33.         MessageBox(L"GetCommState Error", L"Error", MB_OK + MB_ICONERROR);  
  34.         CloseHandle(m_hComm);  
  35.         return false;  
  36.     }  
  37.   
  38.     m_dcb.BaudRate          = BaudRate;  
  39.     m_dcb.ByteSize          = ByteSize;  
  40.     m_dcb.Parity            = Parity ;  
  41.     m_dcb.StopBits          = StopBits;  
  42.     m_dcb.fBinary           = true;  
  43.     m_dcb.fDsrSensitivity   = false;  
  44.     m_dcb.fParity           = fParity;  
  45.     m_dcb.fOutX             = false;  
  46.     m_dcb.fInX              = false;  
  47.     m_dcb.fNull             = false;  
  48.     m_dcb.fAbortOnError     = true;  
  49.     m_dcb.fOutxCtsFlow      = false;  
  50.     m_dcb.fOutxDsrFlow      = false;  
  51.     m_dcb.fDtrControl       = DTR_CONTROL_DISABLE;  
  52.     m_dcb.fDsrSensitivity   = false;  
  53.     m_dcb.fRtsControl       = RTS_CONTROL_DISABLE;  
  54.     m_dcb.fOutxCtsFlow      = false;  
  55.     m_dcb.fOutxCtsFlow      = false;  
  56.   
  57.     m_bPortReady = SetCommState(m_hComm, &m_dcb);  
  58.   
  59.     if(m_bPortReady == 0)  
  60.     {  
  61.         MessageBox(L"SetCommState Error", L"Error", MB_OK + MB_ICONERROR);  
  62.         CloseHandle(m_hComm);  
  63.         return false;  
  64.     }  
  65.   
  66.     return true;  
  67. }  
  68.   
  69. bool CSerialPort::SetCommunicationTimeouts(DWORD ReadIntervalTimeout,  
  70.         DWORD ReadTotalTimeoutMultiplier, DWORD ReadTotalTimeoutConstant,  
  71.         DWORD WriteTotalTimeoutMultiplier, DWORD WriteTotalTimeoutConstant)  
  72. {  
  73.     if((m_bPortReady = GetCommTimeouts(m_hComm, &m_CommTimeouts)) == 0)  
  74.         return false;  
  75.   
  76.     m_CommTimeouts.ReadIntervalTimeout          = ReadIntervalTimeout;  
  77.     m_CommTimeouts.ReadTotalTimeoutConstant     = ReadTotalTimeoutConstant;  
  78.     m_CommTimeouts.ReadTotalTimeoutMultiplier   = ReadTotalTimeoutMultiplier;  
  79.     m_CommTimeouts.WriteTotalTimeoutConstant    = WriteTotalTimeoutConstant;  
  80.     m_CommTimeouts.WriteTotalTimeoutMultiplier  = WriteTotalTimeoutMultiplier;  
  81.       
  82.     m_bPortReady = SetCommTimeouts(m_hComm, &m_CommTimeouts);  
  83.           
  84.     if(m_bPortReady == 0)  
  85.     {  
  86.         MessageBox(L"StCommTimeouts function failed",L"Com Port Error",MB_OK+MB_ICONERROR);  
  87.         CloseHandle(m_hComm);  
  88.         return false;  
  89.     }  
  90.   
  91.     return true;  
  92. }  
  93.   
  94. bool CSerialPort::WriteByte(BYTE bybyte)  
  95. {  
  96.     iBytesWritten=0;  
  97.     if(WriteFile(m_hComm, &bybyte, 1, &m_iBytesWritten, NULL) == 0)  
  98.         return false;  
  99.     else  
  100.         return true;  
  101. }  
  102.   
  103. bool CSerialPort::ReadByte(BYTE &resp)  
  104. {  
  105.     BYTE rx;  
  106.     resp=0;  
  107.   
  108.     DWORD dwBytesTransferred=0;  
  109.   
  110.     if(ReadFile(m_hComm, &rx, 1, &dwBytesTransferred, 0))  
  111.     {  
  112.         if(dwBytesTransferred == 1)  
  113.         {  
  114.             resp=rx;  
  115.             return true;  
  116.         }  
  117.     }  
  118.                 
  119.     return false;  
  120. }  
  121.   
  122. bool CSerialPort::ReadByte(BYTE* &resp, UINT size)  
  123. {  
  124.     DWORD dwBytesTransferred=0;  
  125.   
  126.     if(ReadFile(m_hComm, resp, size, &dwBytesTransferred, 0))  
  127.     {  
  128.         if(dwBytesTransferred == size)  
  129.             return true;  
  130.     }  
  131.   
  132.     return false;  
  133. }  
  134.   
  135. void CSerialPort::ClosePort()  
  136. {  
  137.     CloseHandle(m_hComm);  
  138.     return;  
  139. }  



생각보다 별거 없습니다. 파일 읽고 쓰는거랑 별반 차이 없고, 단지 실시간으로 Read/Write 하려면 Timer 혹은 Thread 를 통해 작업해주면 되겠습니다.

사용법은 아래와 같이 간단해요~


  1. // 이것저것 다 생략하고...  
  2. CSerialPort _serial;  
  3.   
  4. if(_serial.OpenPort(L"COM4"))   // 실제 사용될 COM Port 를 넣어야합니다.  
  5. {  
  6.     // BaudRate, ByteSize, fParity, Parity, StopBit 정보를 설정해줍니다.  
  7.     _serial.ConfigurePort(CBR_9600, 8, FALSE, NOPARITY, ONESTOPBIT);  
  8.     // Timeout 설정입니다. 별다른거 없으면 전부 zero 설정해도 되구요.  
  9.     _serial.SetCommunicationTimeouts(0, 0, 0, 0, 0);  
  10.   
  11.     // Buffer를 잡고  
  12.     BYTE* pByte = new BYTE[512];  
  13.   
  14.     // 읽는데 성공하면 처리하고  
  15.     if(_serial.ReadByte(pByte, 512))  
  16.     {  
  17.         pByte[511] = '\0';  
  18.         _insertData(CString(reinterpret_cast<char*>(pByte)));  
  19.     }  
  20.   
  21.     // 삭제해주면 되는 간편한 놈이죠... 클래스를 보시면 알겠지만  
  22.     // 1byte씩 읽거나 그 이상도 읽을 수 있습니다.  
  23.     // Write 해주는 부분도 비슷합니다. 응용해보세요 ~  
  24.     delete [] pByte;  
  25. }  
  26.   
  27. // 다 썼으니 닫아주면 끝~  
  28. _serial.ClosePort();  


 워낙 시리얼 통신에 대한 글이 많지만 여러모로 찾아다니는것도 골치고, 언제 또 사용할 지도 모르는 일이니 가볍게 포스팅 해봅니다.