시리얼 통신 프로그램 중 포트 관련 파일 2개 올립니다. 그냥 가져다 쓰시면 됩니다.
mfc는 이미 기술적 발전이 끊겼으므로, 90년대 200년대 초반에 만들어진 거 아무거나 쓰세요.
파일은 위에 2개 쓰시면 됩니다.
C/C++ 시리얼 통신 소스 전체 내용과 주석은 아래 글을 참조해 주세요.
C/C++ 시리얼 통신 프로그램 소스
◆ SERIAL PORT 헤더 파일
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #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 ; // 쓰레드 ID OVERLAPPED osWrite, osRead ; // Overlapped I/O를 위한 구조체 // member function BOOL 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdafx.h> #include "Port.h" #include "resource.h" #define MAXBLOCK 80 DWORD 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 |
C/C++ 시리얼 통신 프로그램 소스
◆ 연결할 MFC 시리얼 포트 설정 SetupConnection
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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 control bSet = (BYTE) ((bFlowCtrl & FC_DTRDSR) != 0) ; dcb.fOutxDsrFlow = bSet ; if (bSet) dcb.fDtrControl = DTR_CONTROL_HANDSHAKE ; else dcb.fDtrControl = DTR_CONTROL_ENABLE ; bSet = (BYTE) ((bFlowCtrl & FC_RTSCTS) != 0) ; dcb.fOutxCtsFlow = bSet ; if (bSet) dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ; else dcb.fRtsControl = RTS_CONTROL_ENABLE ; // setup software flow control bSet = (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 settings dcb.fBinary = TRUE ; dcb.fParity = TRUE ; return SetCommState( idComDev, &dcb ) ; } | cs |
◆ serial 포트 열기 OpenPort
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | 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/O NULL); 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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 코드 작성은 마음대로)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | // 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 |
C/C++ 시리얼 통신 프로그램 소스
관련 글
▷ [임베디드 리눅스] 시리얼 통신 안 될 때 확인 방법, stty speed
▷ 시리얼 통신, 폴링 소스 [C++ Serial COM Poll 예제]
▷ C/C++ 시리얼 통신 timeout 해결, MFC 시리얼 read write 통신 공통
ⓒ written by vicddory
댓글