본문 바로가기
C++ 200제/코딩 IT 정보

mfc 시리얼 통신 프로그램 소스, c/c++ serial 예제

by vicddory 2019. 6. 9.

시리얼 통신 프로그램 중 포트 관련 파일 2개 올립니다. 그냥 가져다 쓰시면 됩니다.


mfc는 이미 기술적 발전이 끊겼으므로, 90년대 200년대 초반에 만들어진 거 아무거나 쓰세요.


MFC 시리얼 통신 프로그램 소스


port.cpp

port.h


파일은 위에 2개 쓰시면 됩니다.


C/C++ 시리얼 통신 소스 전체 내용과 주석은 아래 글을 참조해 주세요.


mfc 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


mfc 시리얼 통신 프로그램 소스, serial 예제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, 40964096 ) ;    // 버퍼 설정
    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, 0sizeof( 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


시리얼 통신 프로그램 serial c++C/C++ 시리얼 통신 프로그램 소스


mfc 시리얼 통신 프로그램 소스 중 포트 부분


port.cpp

port.h



관련 글


[임베디드 리눅스] 시리얼 통신 안 될 때 확인 방법, stty speed


시리얼 통신, 폴링 소스 [C++ Serial COM Poll 예제]


C/C++ 시리얼 통신 timeout 해결, MFC 시리얼 read write 통신 공통



ⓒ written by vicddory

댓글