SW 개발

[MFC] 시리얼통신 간단프로젝트 (예제코드)

. . . 2010. 8. 12. 09:33
반응형
  • 출처 : MSDN 구글링/ 여기저기 +
    • http://snslab.kangwon.ac.kr/home/?doc=bbs/gnuboard.php&bo_table=openclass&page=3&wr_id=80
  • 기타사항 : winCE5 / EVC 테스트완료, 오류사항이나 틀린점은 댓글로 지적해주세여
  • 간단한 winCE + evc 에서 간단한 시리얼 프로그래밍을 하기위한 연습용 간단 프로젝트..

일반사항

RS - 232 인터페이스 사이의 제어 기능은 하드웨어를 통해 제어된다.

일반적으로 사용되는 비동기 기능은 UARD ( UNIVERSAL ASYNCHROUS RECEIVE/TRANSMITTER : 범용 비동기 송수신 장치)로 알려져 있는 컨트롤러 IC내에 내장되어 있다.

이 장치는 제어 기능만이 아니라 데이터 기능도 가지고 있다. UART를 채택하면 시작/종료 비동기 I/O, 데이터 형식, RS - 232 인터페이스와 같은 사항은 프로그래머가 신경쓰지 않아도 된다.

사실 UART로 바이트를 송수신하는 것은 RAM 위치 즉 I/O 포트에 기록하고 판독하는 작업이다. 이와 유사한 형태로, RS - 232 입출력도 어려움없이 감시하고 조종할 수 있다.

배경지식

물론 도스에서는 I/O 포트에 직접 접근을 해서 데이터를 읽어 오지만 윈도우에서는 핸들을 생성해서 핸들을 통해서 데이터를 읽고 쓰고 해야함

헤더 파일

// SCom.h: interface for the CSCom class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_SCOM_H__5A71548F_CE4C_11D4_A6B3_00E09833FB7C__INCLUDED_)
#define AFX_SCOM_H__5A71548F_CE4C_11D4_A6B3_00E09833FB7C__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CSCom 
{
public:
    CString ReadCmd();
    void WriteCmd(CString write_str);
    void ComEnd();
    void ComStart();
    CSCom();
    virtual ~CSCom();
    DCB dcb;
    OVERLAPPED osRead;
    OVERLAPPED osWrite;
    COMMTIMEOUTS CommTimeOuts;
    HANDLE idCom;
};
#endif // !defined(AFX_SCOM_H__5A71548F_CE4C_11D4_A6B3_00E09833FB7C__INCLUDED_)

소스파일

// SCom.cpp: implementation of the CSCom class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Serial.h"
#include "SCom.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSCom::CSCom()
{
}

CSCom::~CSCom()
{   
}

void CSCom::ComStart()
{
    if((idCom = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL,                      OPEN_EXISTING, 
                           FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,                            NULL)) != (HANDLE)-1)
    {
        SetCommMask(idCom, EV_RXCHAR);
        SetupComm(idCom,4096,4096);
        PurgeComm(idCom,PURGE_TXABORT | PURGE_RXABORT | 
            PURGE_TXCLEAR | 
            PURGE_RXCLEAR);

        CommTimeOuts.ReadIntervalTimeout = 5000;//0xFFFFFFFF;
        CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
        CommTimeOuts.ReadTotalTimeoutConstant = 0;
        CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
        CommTimeOuts.WriteTotalTimeoutConstant = 5000;
        SetCommTimeouts(idCom, &CommTimeOuts);
    }
    GetCommState(idCom,&dcb);

    dcb.BaudRate = CBR_9600;
    dcb.ByteSize = 8;
    dcb.Parity = EVENPARITY;
    dcb.StopBits = ONESTOPBIT;

    if(SetCommState(idCom,&dcb) != TRUE)
        AfxMessageBox(_T("Error : Set COM1 State"));

    ////////////////////////////////////////////////////
    osRead.Offset = 0;
    osRead.OffsetHigh = 0;
    //--> Read 이벤트 생성에 실패..
    if ( !(osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) ) 
    {
        AfxMessageBox("!(osRead.hEvent = CreateEvent(NULL, TRUE, 
            FALSE, NULL))");
            // return FALSE;
    }

    osWrite.Offset = 0;
    osWrite.OffsetHigh = 0;
    //--> Write 이벤트 생성에 실패..
    if (! (osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
    {
        AfxMessageBox("! (osWrite.hEvent = CreateEvent(NULL, TRUE, 
            FALSE, NULL))");
            // return FALSE;
    }
    ///////////////////////////////////////////////
}

void CSCom::ComEnd()
{
    if(idCom != NULL)
    {
        PurgeComm(idCom,PURGE_TXABORT | PURGE_RXABORT | 
            PURGE_TXCLEAR | PURGE_RXCLEAR);
        if(CloseHandle(idCom) != TRUE)
        {
            AfxMessageBox(_T("Error : Close COM Port"));
            return;
        }
    }

}

void CSCom::WriteCmd(CString write_str)
{
    DWORD nToWrite , dwWrite;
    char imsi ;
    imsi = 0x0d ;
    nToWrite = write_str.GetLength()+1 ;
    write_str = write_str + imsi ;

    WriteFile(idCom,write_str,nToWrite,&dwWrite,&osWrite);

}

CString CSCom::ReadCmd()
{
    DWORD dwRead, dwErrorFlags;
    DWORD nToRead = 1;
    BYTE pBuff[40], pBuff1[40];
    int BufIndex;
    COMSTAT comstat;

    memset(&pBuff,"\0",40);

    BufIndex = 0;
    do
    {
        ClearCommError(idCom, &dwErrorFlags, &comstat);
        dwRead = comstat.cbInQue;
        if(dwRead >= 1)
        {
            ReadFile(idCom,pBuff1,nToRead,&nToRead,&osRead);
            pBuff[BufIndex++] = pBuff1[0];
        }
    }while(pBuff1[0] != 0x0d);

    CString RtnMsg;
    RtnMsg = pBuff;
    // int str_su ;
    // str_su = RtnMsg.GetLength();
    // RtnMsg = RtnMsg.Left(str_su-1);

    return RtnMsg;
}

소스분석

시리얼 통신시 계속해서 데이터를 체크 해야 하는 경우에는 이벤트 쓰레드를 가동 한다. 이벤트쓰레드로 큐를 체크하는 방식으로 작성을 하면 됨.. (위에 방법은 일반적으로 통신 프로그램에서 쓰임.. )

  • 쓰레드 동기화 관련 ..
    • 쓰레드 동기화 에는 크게 4종류에 쓰레드가 있음..
    • 크리티컬 섹션 ( criticalsection ) 뮤덱스 ( mutex ) 세마포어 ( semaphore ) 위에서 언급한 이벤트 ( event )
  • criticalsection
    • 내용 : 공유 데이터나 리소스(메모리나 파일) 에 대한 접근을 하나의 스레드로 제한할 때 사용함.. ( dll 이나 기타 active x 혹은 컴포넌트로 만들 수 없음 ) .. 만들고 싶음 만들어 보세요..
    • 특징 : 동일한 프로세스 내의 스레드간 동기화에 사용한다.
  • mutex
    • 내용 : 크리티컬 섹션과 동일한 기능을 가지지만 차이가 있다면 다른 프로세스에 속한 스레드를 제어 할 수 있다. ( dll 이나 기타 active x 혹은 컴포넌트로 만들 수 있음 )
    • 특징 : 다른 프로세스 내의 스레드간에 사용가능
  • semaphore
    • 내용 : 뮤텍스와 유사하지만 동시에 수행할 수 있는 스레드를 여러 개로 제한할 수 있다. 심지어. 먼저실행할 스레드까지도 가능함.. ( 이건 잘 모르겠당.. 브이씨에서는 돼던데.. )( 예 : 4 개의 스레드중 2 개씩을 cpu에서 돌린다는가 하는... )
    • 특징 : 다른 프로세스 내의 스레드간에 사용가능
  • event
    • 내용 : 스레드간 동기화 방법 중 가장 널리 사용되는 것으로 하나의 스레드가 다른 스레드에 특정이벤트가 발생했음을 알려준다.
    • 특징 : 다른 프로세스 내의 스레드간에 사용가능 시리얼 통신에서 염두할 것은.. 데이터에 신빙성과..( 깨진 데이터를 가지고 장난 하면 안돼겠쥐영..) 도스에서는 인터럽트를 직접 건드려서 시리얼 통신을 하지만.. 윈도우에서는 핸들이라는 놈을 통해서 ( 위에 땜시 VXD 파일이라는 놈도 있고 디바이스 드라이버 프로그래머도 먹고 사는 것이쥐영 ) 모든 하드웨어 통신을 하는데 각 포트당 핸들은 하나라는 점.... ( 공장 자동제어 쪽에서는 특성상 크리티컬 섹션이 용이함.. ) <= DLL 이나 ACTIVE X 나 COMPONENT 로 만든다면 MUTEX 로... 단 간단한 프로토콜은 이벤트가 훨씬 유리함.. 어떤 스레드를 사용할지는 그 상황에 맏게 적절히 선택해야 한다.. ( 잘못하면 엿먹는 수가 있음... ) < = 소스 전체를 뒤집어 업는.........
    • 델파이에서는 시리얼 통신을 컴포넌트야 장난 이고.... 위에 개념만 알면.. API 함수를 끌어다가 하면 돼겠쥐영..참고.. 시리얼 통신할때에는 아스키코드 테이블 필수입니다...
반응형