반응형

시리얼 포트 제어

1. 포트 열기

1) 포트 파일명에 대해서

시리얼 포트는 기본적으로 “COM1”, “COM2”와 같이 파일명이 “COM” + 포트아이디로 구성되어 있습니다.

주의할 점은 시리얼 포트가 10개이상 설치된 PC에서는 “COM10”번부터는 조금 다른게 포트에 접근하여야 한다는 것입니다.

시리얼포트 10번미만은 “COM1”과 같이 파일명을 기술하시면 되구요

시리얼포트 10번이상은 “\\.\\COM10”과 같이 포트명을 기술하셔야 합니다.


2) DCB에 대해서

DCB에는 시리얼 포트를 통한 데이터 통신시 필요한 환경 설정 정보가 저장됩니다.

자세한 내용은 MSDN을 참조하시구요

놓치기 쉬운 것 중에서 몇 가지를 설명하자면


DCB::fNULL 항목을 가지고 binary 또는 text 통신을 설정하게 됩니다.

Binary 통신을 하려면 DCB::fNULL c FALSE : 0x00값을 송수신 가능

Text 통신을 하려면 DCB::fNULL c TRUE : 0x00값 송수신 불가능, 통신시

데이터를 버려버림


Parity 설정시 NOPARITY(패리티 사용안함)가 아니면 DCB::fParity c TRUE


DCB:: ByteSize : 7 / 8 값만이 유효한 값임

일반적으로 요즘 나오는 RS232 칩들은 8bit를 데이터 1바이트로 인식합니다.

데이터 1바이트 표현시 Parity / Stopbit은 데이터를 구성하는 비트에 포함이 되지 않습니다.



3) 시리얼 포트를 열기 위한 절차

- 시리얼 포트 파일명을 만든다.(뭐 하드 코딩하시겠다면 할말이 없지만)

CString strPort = "";

if( nPort < 10 )

       strPort.Format("COM%d", nPort);

else strPort.Format("\\\\.\\COM%d", nPort);

- 시리얼 포트 파일을 연다. (헉 너무 간단한가?)

m_hComm = CreateFile(..);

if( m_hComm == INVALID_HANDLE_VALUE )

{

       Close();//시리얼 포트에 접근 못함

       return FALSE;

}


-시리얼 포트에 대한 현재 환경설정정보(DCB)를 얻어온다.

DCB dcb; //communication eviroment을 얻어오고 고친다.

   memset( &dcb, 0x00, sizeof( dcb ) );

   dcb.DCBlength = sizeof( dcb );

   if( !GetCommState(m_hComm, &dcb) )

   {//DCB 정보를 얻어오는데 실패함

       Close();

       return FALSE;

   }


- 환경설정정보를 데이터 통신하기 위하여, 수정이 필요하다면 수정한다.

   dcb.BaudRate = nSpeed; //속도 패리티, 바이트 크기, 스탑비트수 세팅

   dcb.Parity = nParity;

   dcb.fNull   = FALSE;

   if( nParity != NOPARITY )

       dcb.fParity = TRUE;

   else dcb.fParity = FALSE;

   dcb.ByteSize= nByteSize;

   dcb.StopBits= nStopBits;


- 수정된 환경설정정보를 저장한다.

   SetCommState( m_hComm, &dcb); //시리얼 포트 환경 세팅 완료



- SetCommMask 함수를 이용한 시리얼 포트에서 수신할 이벤트 종류 설정

 SetCommMask( m_hComm, EV_RXCHAR);// RX에 데이터가 수신된 경우에만 이벤트를 받음


- PurgeComm함수를 이용해 시리얼 포트에 남아있는 데이터를 모두 지워버림

 PurgeComm( m_hComm, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ); //clear buffer


- ClearCommError 함수를 이용해서 시리얼 포트에 저장된 에러 정보를 지움

DWORD dwCommError = 0;

ClearCommError( m_hComm, &dwCommError, NULL ); //시리얼 포트에 수신된 모든 에러를 초기화함


- COMMTIMEOUTS 구조체를 이용한 데이터 송수신에 대한 Time Out 시간을 설정

그러나, 비동기 통신을 사용하는 경우 데이터가 송신 완료 되는 시점은 알 수 있지만 데이터가 수신되는 시점을 정확히 알 수 없기 때문에 일반적으로 시간 설정을 사용하지 않습니다.

이거 잘 못 사용하면, 프로그램이 가다서다하는 듯한 현상이 발생하기 때문에 개인적으로는 타임아웃 시간 설정하는 것을 추천하지 않습니다.

COMMTIMEOUTS CommTimeOut;

GetCommTimeouts( m_hComm, &CommTimeOut);

CommTimeOut.ReadTotalTimeoutConstant= CommTimeOut.ReadIntervalTimeout= CommTimeOut.ReadTotalTimeoutMultiplier  = MAXDWORD;

SetCommTimeouts( m_hComm, &CommTimeOut ); //타임아웃사용안함설정


- 비동기 통신을 위한 데이터 수신 감시를 위한 쓰레드를 만들기

만드는 이유는 데이터가 수신되는 시점을 정확히 알 수 없기 때문에 쓰레드를 만들어서 데이터가 수신되는 것을 지속적으로 감시를 하여야 합니다.

중요한 것은 데이터가 수신된다고 OS가 발생하는 메시지 숫자와 수신된 데이터의 바이트 크기가 동일 하지 않다는 것입니다.

따라서 메시지가 발생하면 무조건 읽을 수 있을 만큼 최대한 읽어 주어야 합니다.

                       //시리얼 포트를 감시하는 쓰레드 만들기

   DWORD dwThreadId=0;

   m_hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)

CommMonitor, this , 0, &dwThreadId);



2. 포트 닫기

1) 시리얼 포트 닫기 절차

- 수신 데이터 감시 쓰레드가 있는 경우, 쓰레드를 종료한다.

   ::TerminateThread( m_hThread, dwExitCode );


- 오버랩핑을 사용했다면, 오버랩핑 핸들을 모두 닫는다.

   if( m_ReadOverlap.hEvent  )

   {

       CloseHandle( m_ReadOverlap.hEvent );

       m_ReadOverlap.hEvent = NULL;

   }

   if( m_WriteOverlap.hEvent )

   {

       CloseHandle( m_WriteOverlap.hEvent );

       m_WriteOverlap.hEvent = NULL;

   }


- 시리얼 포트 파일을 닫는다.

if( m_hComm )

   {

       CloseHandle( m_hComm );

       m_hComm = NULL;

   }

반응형

+ Recent posts