[VC++] 시리얼 통신 프로그램 만들기[2-4] – 통신기능 부여하기

CMainFrame 이 CSerialMFC 의 객체를 멤버변수로서 가지게 된다고 했으니 CMainFrame 의 멤버변수를 선언합니다.

아래 그림과 같이 VC++ Work Space 화면의 ClassView 탭에서 CMainFrame 클래스가 표시된 곳에 마우스를 위치시키고 오른쪽 마우스 버튼을 눌러 “Add Member Variable” 메뉴를 선택합니다.

ComConstructor_MouseToAddVariable

 

위 그림처럼 ClassView 에서 각 클래스 이름에 대고 마우스 오른쪽버튼을 누르면 나오는 팝업메뉴에는 멤버함수를 추가시킬 수 있는 “Add Member Function”, 멤버 변수를 추가시킬 수 있는 “Add Member Variable”, 가상함수를 정의할 수 있는 “Add Virtual Function”, 윈도우 클래스 (CWnd 로부터 상속되어진 클래스) 이고 소스에 메세지맵이 정의돼 있으면 나오는 “Add Windows Message Handler” 등이 보여집니다.

VC++ 에선 위와같이 Work Space 의 ClassView 탭 에서 팝업메뉴를 사용하거나 Class Wizard 를 직접 띄워(Ctrl+W) 메뉴를 사용하여 간편하게 멤버함수나 변수를 추가시킬 수 있습니다.

그리고 다음 그림과 같이 멤버변수를 Public 으로 선언해줍니다.

( Protected 가 아닙니다. 이전 게시물에서 Protected 로 하라고 돼 있었습니다. 이전 게시물을 보고 컴파일 에러가 나왔던 분들은 이부분을 수정해주세요.)

ComConstructor_AddVariable

 

마찬가지 방식으로 int 형 변수를 m_nPort 란 이름으로 Protected: 로 선언해줍니다.
마찬가지 방식으로 CSerial::EBaudrate 형 변수를 m_eBaudrate 라는 이름으로 Protected: 로 선언해줍니다.

마지막의 m_eBardrate 선언시 위와같은 방식의 변수선언 위저드통한 변수 선언이 안될땐 CMainFrame 클래스가 정의된 헤더파일에 직접 변수선언 부분을 기입합니다.

그러면 아래와 같이 CMainFrame 클래스에 세개의 변수가 public: 으로 새로 선언되었을 것입니다.

	CSerialMFC		m_Serial;
	int			m_nPort;
	CSerial::EBaudrate 	m_eBaudrate;

m_Serial 변수는 CSerial 클래스가 상속되어져 정의된 CSerialMFC 클래스가 실제 메모리에 인스턴스화 된 것입니다.
m_nPort 와 m_eBaudrate 는 m_Serial 이 사용할 시리얼 포트 번호와 보레이트를 기억시키기 위하여 만들었습니다.

위에서 선언한 변수들을 초기화 시켜주기 위해 CMainFrame 의 OnCreate 함수의 return 0; 구문 바로 이전에 다음과 같이 기입하여 줍니다.


	if(CSerial::CheckPort(_T("COM1"))
		== CSerial::EPortAvailable)
		m_nPort = 1;
	else if(CSerial::CheckPort(_T("COM2"))
		== CSerial::EPortAvailable)
		m_nPort = 2;
	else if(CSerial::CheckPort(_T("COM3"))
		== CSerial::EPortAvailable)
		m_nPort = 3;
	else if(CSerial::CheckPort(_T("COM4"))
		== CSerial::EPortAvailable)
		m_nPort = 4;
	else
		m_nPort = 1;

	m_eBaudrate = CSerial::EBaud9600;	// 9600 bits/sec

CSerial::CheckPort 함수는 static 함수이므로 CSerial 을 오브젝트로 생성하지 않아도 호출할 수 있습니다.
이 함수는 매개변수로 넘겨진 시리얼 포트가 사용 가능한지 체크해 볼 수 있는 기능이 있습니다.

다음으로 Work Space 의 ResourceView 탭에서 Menu 를 선택해 하위 IDR_MAINMENU 를 클릭하면 나오는 메뉴에디터에서 아래 그림과 같이 “접속”, “접속종료”, “통신포트 설정” 세개의 메뉴 항목을 삽입하고 차례대로 “ID_CONNECT”, “ID_DISCONNECT”, “ID_COMSETTING” 라고 기입합니다.

ComConstructor_EditMenu

 

새로운 메뉴를 삽입하였으므로 이 메뉴가 선택되었을 때 호출되는 함수들을 정의합니다.
Ctrl+W 키를 눌러 클래스 위저드를 실행시키고 방금 삽입한 세개의 메뉴들에 해당하는 ID 값에 대한 COMMNAND 메세지 처리함수를 만들어줍니다.

아래 그림처럼 클래스 위저드가 이름지어주는대로 OnConnect, OnDisconnect, OnComsetting 함수들을 CMainFrame 에 만듭니다.

위와같은 방식으로 OnConnect 와 OnDisconnect 에 대한 UPDATE_COMMAND_UI 메세지 핸들러 함수도 만들어줍니다.
클래스 위저드가 OnUpdateConnect 와 OnUpdateDisconnect 라고 이름지어줄 것입니다.
이 함수들 안에서 위 두 메뉴의 시각적 효과를 특징지워줄 수 있습니다.
접속 메뉴를 클릭해서 포트를 열었다면 접속종료를 클릭해 포트를 닫을 때 까지 이 접속 메뉴를 비활성화 시키야 하고 접속종료 메뉴도 마찬가지의 시각효과를 주어야 하니까요.

ComConstructor_OnConnect

 

OnConnect 와 OnDisconnect 멤버변수내의 코드를 아래와 같이 추가시켜줍니다.

void CMainFrame::OnConnect()
{
	CString szPort;
	szPort.Format("COM%d", m_nPort);
	if (m_Serial.Open(szPort,this) != ERROR_SUCCESS)
	{
		szPort += _T(" 포트를 열 수 없습니다.");
		AfxMessageBox((LPCTSTR)szPort,MB_ICONSTOP|MB_OK);
		GetParent()->PostMessage(WM_CLOSE);
		return;
	}
	m_Serial.Setup(m_eBaudrate,
		CSerial::EData8,
		CSerial::EParNone,
		CSerial::EStop1);
	m_Serial.SetupHandshaking(CSerial::EHandshakeOff);
}

 

void CMainFrame::OnDisconnect()
{
	if(m_Serial.IsOpen())
		m_Serial.Close();
}

그리고 OnUpdateConnect 와 OnUpdateDisconnect 멤버함수내의 코드를 다음과 같이 기입해줍니다.

 

 

 


void CMainFrame::OnUpdateConnect(CCmdUI* pCmdUI)
{
	if(m_Serial.IsOpen())
		pCmdUI->Enable(FALSE);
	else
		pCmdUI->Enable(TRUE);
}

 

void CMainFrame::OnUpdateDisconnect(CCmdUI* pCmdUI)
{
	if(m_Serial.IsOpen())
		pCmdUI->Enable(TRUE);
	else
		pCmdUI->Enable(FALSE);
}

CSerialMFC::Open() 함수 호출시 열기를 원하는 COM 포트와 함께 현재윈도우 포인터를 넘겨줌으로서 시리얼 포트에 데이터입력이 포착되었을때 시리얼 입력을 처리할 함수가 있는 윈도우가 현재윈도우(CMainFrame) 임을 알려줍니다.

그리고 시리얼 클래스가 이 시점에서 윈도우메세지를 보낼때 이를 받을 메세지 핸들러도 정의해줍니다.

MainFrm.cpp 의 메세지맵안에 다음과 같이 ON_WM_SERIAL(OnSerialMsg) 문구를 삽입합니다. 반드시 //}}AFX_MSG_MAP 구문 바깥에 기입되어야 합니다.

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
	//{{AFX_MSG_MAP(CMainFrame)
	ON_WM_CREATE()
	ON_COMMAND(ID_CONNECT, OnConnect)
	ON_COMMAND(ID_DISCONNECT, OnDisconnect)
	ON_COMMAND(ID_COMSETTING, OnComsetting)
	//}}AFX_MSG_MAP
	ON_WM_SERIAL(OnSerialMsg)		// <-- 삽입된 문구
END_MESSAGE_MAP()

MainFrm.h 파일에서 CMainFrame 클래스가 정의된 곳의 메세지 핸들러 함수를 선언해주는 곳에서 다음과 같이 afx_msg LRESULT OnSerialMsg (WPARAM wParam, LPARAM lParam); 문구를 삽입합니다.

	//{{AFX_MSG(CMainFrame)
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnConnect();
	afx_msg void OnDisconnect();
	afx_msg void OnComsetting();
	//}}AFX_MSG

	//삽입된 문구
	afx_msg LRESULT OnSerialMsg (WPARAM wParam, LPARAM lParam);
	DECLARE_MESSAGE_MAP()

이제 MainFrm.cpp 에 다음과 같이 OnSerialMsg 멤버함수를 정의해줍니다.

LRESULT CMainFrame::OnSerialMsg (WPARAM wParam, LPARAM /*lParam*/)
{
	CSerial::EEvent eEvent = CSerial::EEvent(LOWORD(wParam));
	CSerial::EError eError = CSerial::EError(HIWORD(wParam));

	if (eEvent & CSerial::EEventRecv)
	{
	}
	return 0;
}

그리고 현재까지의 작업이 오류없이 진행되었는지를 컴파일 해봅니다.
프로그램을 실행시켜 메뉴에서 “접속” 을 누르면 자신 PC 의 COM1 포트가 이상이 없다면 COM1 시리얼 포트를 통해 프로그램이 통신됩니다.
그러나 타겟을 COM1 으로 연결해 데이터를 전송해도 프로그램에는 아무 정보도 표시되지 않고 있습니다.
바로 위 메세지 핸들러가 이때의 처리를 해줍니다만 if 문 안에 아무런 처리루틴도 기입하지 않았기 때문에 아무런 정보도 표시되고 있지 않습니다.

 

이 글을 공유하기:

Be the first to comment

Leave a Reply