티스토리 뷰
목차
시리얼 통신 프로그램 중 포트 관련 파일 2개 올립니다. 그냥 가져다 쓰시면 됩니다.
mfc는 이미 기술적 발전이 끊겼으므로, 90년대 200년대 초반에 만들어진 거 아무거나 쓰세요.
생활정보
MFC 시리얼 통신 프로그램 소스
port.cpp다운로드 port.h다운로드
파일은 위에 2개 쓰시면 됩니다.
C/C++ 시리얼 통신 소스 전체 내용과 주석은 아래 글을 참조해 주세요.
◆ SERIAL PORT 헤더 파일
12345678910111213141516171819202122232425262728293031 #include <windows.h>#include <stdio.h>#include <string.h>class Port{public:char port_name[20];HANDLE idComDev; // 포트 핸들// 포트 상태 관련BOOL fConnected, fXonXoff, fLocalEcho, fNewLine, fAutoWrap,fUseCNReceive, fDisplayErrors;BYTE bByteSize, bFlowCtrl, bParity, bStopBits ;DWORD dwBaudRate ;HANDLE hWatchThread; // 쓰레드 핸들DWORD dwThreadID ; // 쓰레드 IDOVERLAPPED osWrite, osRead ; // Overlapped I/O를 위한 구조체// member functionBOOL OpenPort(long port_number,long baud_rate_select=6);BOOL SetupConnection(void);long ReadCommBlock(LPSTR lpszBlock, long nMaxLength );BOOL WriteCommBlock(LPSTR lpByte , DWORD dwBytesToWrite);BOOL ClosePort(void);};cs
◆ #define
12345678910111213141516171819202122232425 #include <stdafx.h>#include "Port.h"#include "resource.h"#define MAXBLOCK 80DWORD BAUD_RATE_TABLE[] = { CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_14400,CBR_19200,CBR_38400,CBR_56000,CBR_57600,CBR_115200,230400,460800,921600,0};// Flow control flags#define FC_DTRDSR 0x01 //데이터 단말기(DTR) 대기,데이터 세트(DSR) 대기를 위한 신호#define FC_RTSCTS 0x02#define FC_XONXOFF 0x04// ascii definitions#define ASCII_BEL 0x07#define ASCII_BS 0x08#define ASCII_LF 0x0A#define ASCII_CR 0x0D#define ASCII_XON 0x11#define ASCII_XOFF 0x13// Event Watch용 쓰레드 함수DWORD CommWatchProc( LPSTR lpData );cs
◆ 연결할 MFC 시리얼 포트 설정 SetupConnection
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 BOOL Port::SetupConnection(void){// DCB 구조체를 이용하여 포트를 셋팅한다BYTE bSet ;DCB dcb ;dcb.DCBlength = sizeof( DCB ) ;GetCommState( idComDev, &dcb ) ;dcb.BaudRate = dwBaudRate ;dcb.ByteSize = bByteSize;dcb.Parity = bParity;dcb.StopBits = bStopBits;// setup hardware flow controlbSet = (BYTE) ((bFlowCtrl & FC_DTRDSR) != 0) ;dcb.fOutxDsrFlow = bSet ;if (bSet)dcb.fDtrControl = DTR_CONTROL_HANDSHAKE ;elsedcb.fDtrControl = DTR_CONTROL_ENABLE ;bSet = (BYTE) ((bFlowCtrl & FC_RTSCTS) != 0) ;dcb.fOutxCtsFlow = bSet ;if (bSet)dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ;elsedcb.fRtsControl = RTS_CONTROL_ENABLE ;// setup software flow controlbSet = (BYTE) ((bFlowCtrl & FC_XONXOFF) != 0) ;dcb.fInX = dcb.fOutX = bSet ;dcb.XonChar = ASCII_XON ;dcb.XoffChar = ASCII_XOFF ;dcb.XonLim = 100 ;dcb.XoffLim = 100 ;// other various settingsdcb.fBinary = TRUE ;dcb.fParity = TRUE ;return SetCommState( idComDev, &dcb ) ;}cs
◆ serial 포트 열기 OpenPort
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 BOOL Port::OpenPort(long port_number,long baud_rate_select){// 포트 상태 관련 변수들을 초기화 한다idComDev =0 ;fConnected = FALSE ;fLocalEcho = FALSE ;fAutoWrap = TRUE ;dwBaudRate = BAUD_RATE_TABLE[baud_rate_select];bByteSize = 8 ;bFlowCtrl = FC_XONXOFF ;bParity = NOPARITY ;bStopBits = ONESTOPBIT ;fXonXoff = FALSE ;fUseCNReceive = TRUE ;fDisplayErrors = TRUE ;osWrite.Offset = 0 ;osWrite.OffsetHigh = 0 ;osRead.Offset = 0 ;osRead.OffsetHigh = 0 ;// Overlapped I/O에 쓰이는 Event 객체들을 생성// Read를 위한 Event 객체 생성osRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL ) ;if (osRead.hEvent==NULL){return FALSE ;}// Write를 위한 Event 객체 생성osWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL ) ;if (osWrite.hEvent==NULL){CloseHandle( osRead.hEvent ) ;return FALSE;}// 포트를 생성한다char temp[20];strcpy(temp,"\\\\.\\");wsprintf(port_name,"COM%d",port_number);strcat(temp,port_name);idComDev = CreateFile(temp,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // Overlapped I/ONULL);if (idComDev == INVALID_HANDLE_VALUE){CloseHandle(osRead.hEvent);CloseHandle(osWrite.hEvent);return FALSE;}SetCommMask( idComDev, EV_RXCHAR ) ;SetupComm( idComDev, 4096, 4096 ) ; // 버퍼 설정PurgeComm( idComDev, PURGE_TXABORT | PURGE_RXABORT |PURGE_TXCLEAR | PURGE_RXCLEAR ) ; // 버퍼의 모든 데이타를 지운다// Overlapped I/O를 위한 Time Out 설정COMMTIMEOUTS CommTimeOuts ;CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF ;CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;CommTimeOuts.ReadTotalTimeoutConstant = 1000 ;CommTimeOuts.WriteTotalTimeoutMultiplier = 2*CBR_9600/dwBaudRate ; // CBR_9600 기준 ms당 바이트를 두배까지 설정CommTimeOuts.WriteTotalTimeoutConstant = 0 ;SetCommTimeouts( idComDev, &CommTimeOuts ) ;// 포트를 사용가능 상태로 만들고 Event를 감시할 쓰레드를 생성한다if(SetupConnection()==TRUE){fConnected = TRUE ;// 쓰레드 생성hWatchThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,0,(LPTHREAD_START_ROUTINE) CommWatchProc,(LPVOID)this,0,&dwThreadID );if(hWatchThread==NULL) // 쓰레드 생성 실패{fConnected = FALSE ;CloseHandle(osRead.hEvent);CloseHandle(osWrite.hEvent);CloseHandle( idComDev ) ;return FALSE;}else{// 장치에 DTR(Data-Terminal-Ready)을 알린다EscapeCommFunction( idComDev, SETDTR ) ;}}else{fConnected = FALSE ;CloseHandle(osRead.hEvent);CloseHandle(osWrite.hEvent);CloseHandle( idComDev ) ;return FALSE;}return TRUE;}cs
◆ 시리얼 통신 - 읽기 ReadCommBlock
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 long Port::ReadCommBlock(LPSTR lpszBlock, long nMaxLength ){BOOL fReadStat ;COMSTAT ComStat ;DWORD dwErrorFlags;DWORD dwLength;DWORD dwError;char szError[ 10 ] ;// 큐에서 읽어야 할 데이타 크기를 가져온다ClearCommError( idComDev, &dwErrorFlags, &ComStat ) ;dwLength = min( (DWORD) nMaxLength, ComStat.cbInQue ) ;if (dwLength > 0) // 읽어야 할 데이타가 있는 경우{// 데이타를 읽는다. Overlapped I/O임을 주의.fReadStat = ReadFile( idComDev, lpszBlock,dwLength, &dwLength, &osRead) ;if (!fReadStat) // 읽어야 할 바이트를 다 읽지 못했다{if (GetLastError() == ERROR_IO_PENDING) // I/O Pending에 의해 다 읽지 못한 경우{// I/O가 완료되기를 기다린다.while(!GetOverlappedResult( idComDev,&osRead, &dwLength, TRUE )){dwError = GetLastError();if(dwError == ERROR_IO_INCOMPLETE) // I/O가 아직 끝나지 않았다continue;else // 에러 발생{wsprintf( szError, "<CE-%u>\n\r", dwError ) ;printf(szError);// 에러를 클리어 하고 다른 I/O가 가능하도록 한다ClearCommError(idComDev, &dwErrorFlags, &ComStat ) ;break;}}}else // I/O Pending이 아닌 다른 에러가 발생한 경우{dwLength = 0 ;// 에러를 클리어 하고 다른 I/O가 가능하도록 한다ClearCommError( idComDev, &dwErrorFlags, &ComStat ) ;}}}return ( dwLength ) ;}cs
◆ 시리얼 통신 - 보내기 (쓰기) WriteCommBlock
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 BOOL Port::WriteCommBlock(LPSTR lpByte , DWORD dwBytesToWrite){BOOL fWriteStat ;DWORD dwBytesWritten ;DWORD dwErrorFlags;DWORD dwError;DWORD dwBytesSent=0;COMSTAT ComStat;char szError[ 128 ] ;fWriteStat = WriteFile( idComDev, lpByte, dwBytesToWrite,&dwBytesWritten, &osWrite) ;if (!fWriteStat) // 써야할 바이트를 다 쓰지 못했다{if(GetLastError() == ERROR_IO_PENDING) // I/O Pending에 의한 경우{// I/O가 완료되기를 기다린다while(!GetOverlappedResult( idComDev,&osWrite, &dwBytesWritten, TRUE )){dwError = GetLastError();if(dwError == ERROR_IO_INCOMPLETE){// 보낸 전체 바이트 수를 체크dwBytesSent += dwBytesWritten;continue;}else{// 에러 발생wsprintf( szError, "<CE-%u>", dwError ) ;printf("%s\r\n",szError);//WriteTTYBlock( hWnd, szError, lstrlen( szError ) ) ;ClearCommError( idComDev, &dwErrorFlags, &ComStat ) ;break;}}dwBytesSent += dwBytesWritten;if( dwBytesSent != dwBytesToWrite ) // 보내야 할 바이트와 보낸 바이트가 일치하지 않는 경우wsprintf(szError,"\nProbable Write Timeout: Total of %ld bytes sent", dwBytesSent);else // 성공적으로 모두 보낸 경우wsprintf(szError,"\n%ld bytes written", dwBytesSent);OutputDebugString(szError);}else // I/O Pending 외의 다른 에러{ClearCommError(idComDev, &dwErrorFlags, &ComStat ) ;return FALSE;}}return TRUE;}cs
◆ MFC 시리얼 통신 포트 닫기 (연결 끊기) ClosePort
123456789101112131415161718192021222324 BOOL Port::ClosePort(void){fConnected = FALSE ;// 이벤트 발생을 중지한다SetCommMask(idComDev, 0 ) ;// Event Watch 쓰레드가 중지되기를 기다린다while(dwThreadID != 0);// DTR(Data-Terminal-Ready) 시그널을 Clear 한다EscapeCommFunction( idComDev, CLRDTR ) ;// 대기중인 모든 데이타를 지운다PurgeComm( idComDev, PURGE_TXABORT | PURGE_RXABORT |PURGE_TXCLEAR | PURGE_RXCLEAR ) ;// 핸들을 반환한다CloseHandle(osRead.hEvent);CloseHandle(osWrite.hEvent);CloseHandle( idComDev) ;return TRUE;}cs
◆ 이벤트 부분 (48번에 MFC 코드 작성은 마음대로)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 // Event Watch 쓰레드DWORD CommWatchProc( LPSTR lpData ){DWORD dwEvtMask ;Port *pp=(Port *)lpData;OVERLAPPED os ;long nLength ;BYTE abIn[ MAXBLOCK + 1] ;memset( &os, 0, sizeof( OVERLAPPED ) ) ;// Event 객체 생성os.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL ) ;if (os.hEvent == NULL)return FALSE;if (!SetCommMask( pp->idComDev, EV_RXCHAR )){CloseHandle(os.hEvent);return FALSE;}while ( pp->fConnected ){dwEvtMask = 0 ;// Event가 발생할 때 까지 블럭WaitCommEvent( pp->idComDev, &dwEvtMask, NULL );// MSDN 상의 설명과는 달리 WaitCommEvent의 마지막 매개 변수가 NULL이다// 설명에 의하면 FILE_FLAG_OVERLAPPED로 생성된 경우 반드시 NULL이 아닌 Overlapped// 구조체를 사용해야 한다고 되어있으나 몇몇 샘플을 검토한 결과 오히려 Code 87 에러가// 발생한다// 이런 이유로 os의 Event 객체는 별 의미가 없다// 또한 이런 이유로 멀티 쓰레드가 아닌 단일 쓰레드에서는 Overlapped I/O를 사용하는// 의미가 없을 것 같다 ( Polling 시 블럭되기 때문)if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR){do{if (nLength = pp->ReadCommBlock((LPSTR) abIn, MAXBLOCK )){// 여기서 어떤 작업, 예외처리}}while ( nLength > 0 ) ;}else{printf("<Other Event>\r\n");}}CloseHandle( os.hEvent ) ;pp->dwThreadID = 0 ;pp->hWatchThread = NULL ;return TRUE;}cs
mfc 시리얼 통신 프로그램 소스 중 포트 부분
port.cpp다운로드 port.h다운로드
관련 글
▷ [임베디드 리눅스] 시리얼 통신 안 될 때 확인 방법, stty speed
▷ 시리얼 통신, 폴링 소스 [C++ Serial COM Poll 예제]
▷ C/C++ 시리얼 통신 timeout 해결, MFC 시리얼 read write 통신 공통
ⓒ written by vicddory