본문 바로가기
프로그래밍/C/C++/C#

VC++을 이용한 Serial 통신 프로그램 만들기 [기초]

by choies1 2009. 12. 25.
참고 사이트: http://mnlt.tistory.com/119

본 VC++ Serial 통신 프로그램 코드는 위의 참고 사이트의 코드를 이용해서 만들어졌습니다.

크게 아래의 세부분으로 구성되어 있습니다.

1. SerialPort.h 로 Serial Port 관련 Class 정의하기
2. SerialPort.cpp로 Serial Port 관련 Class 구현하기
3. Serial Port 관련 Class 이용하기

(참고로 아래의 코드를 이용할 때 마우스로 그대로 긁어서 이용하면 앞에 번호까지 복사되어 불편하니 코드에 마우스를 갖다대면 오른쪽 상단에 뜨는 아이콘을 클릭하여 코드를 복사해서 이용하세요.)

1. Serial Port 관련 Class 정의하기(SeriaPort.h)
Serial Port 관련 Class를 정의하는 부분입니다. Class 정의하는 부분 마지막에 ';'를 꼭 입력해야 됩니다. 참고 사이트에는 이것이 빠져 있네요.
class CSerialPort
{
public:
	CSerialPort(void);
	virtual ~CSerialPort(void);

private:
	HANDLE	m_hComm;
	DCB		m_dcb;
	COMMTIMEOUTS m_CommTimeouts;
	bool	m_bPortReady;
	bool	m_bWriteRC;
	bool	m_bReadRC;
	DWORD	m_iBytesWritten;
	DWORD	m_iBytesRead;
	DWORD	m_dwBytesRead;

public:
	void ClosePort();
	bool ReadByte(BYTE &resp);
	bool ReadByte(BYTE* &resp, UINT size);
	bool WriteByte(BYTE bybyte);
	bool OpenPort(CString portname);
	bool SetCommunicationTimeouts(DWORD ReadIntervalTimeout,
		DWORD ReadTotalTimeoutMultiplier, DWORD ReadTotalTimeoutConstant,
		DWORD WriteTotalTimeoutMultiplier,DWORD WriteTotalTimeoutConstant);
	bool ConfigurePort(DWORD BaudRate, BYTE ByteSize, DWORD fParity, 
		BYTE  Parity,BYTE StopBits);
};
2. Serial Port 관련 Class 구현하기(SeriaPort.cpp)
Serial Port 관련 Class를 구현하는 부분입니다. 만약 콘솔 프로그래밍을 하시는 분이라면 아래 코드중 Messagebox 부분을 적당히 printf로 바꾸어서 사용하면 됩니다.
#include "SerialPort.h"

CSerialPort::CSerialPort()
{
}

CSerialPort::~CSerialPort()
{
}

bool CSerialPort::OpenPort(CString portname)
{
	m_hComm = CreateFile(L"//./" + portname,
			GENERIC_READ | GENERIC_WRITE,
			0,
			0,
			OPEN_EXISTING,
			0,
			0);
		if(m_hComm == INVALID_HANDLE_VALUE)
		{
			return false;
		}
		else
			return true;
}

bool CSerialPort::ConfigurePort(DWORD BaudRate, BYTE ByteSize, DWORD fParity, 
		BYTE Parity, BYTE StopBits)
{
	if((m_bPortReady = GetCommState(m_hComm, &m_dcb))==0)
	{
		MessageBox(L"GetCommState Error", L"Error", MB_OK + MB_ICONERROR);
		CloseHandle(m_hComm);
		return false;
	}

	m_dcb.BaudRate			= BaudRate;
	m_dcb.ByteSize			= ByteSize;
	m_dcb.Parity			= Parity ;
	m_dcb.StopBits			= StopBits;
	m_dcb.fBinary			= true;
	m_dcb.fDsrSensitivity	= false;
	m_dcb.fParity			= fParity;
	m_dcb.fOutX				= false;
	m_dcb.fInX				= false;
	m_dcb.fNull				= false;
	m_dcb.fAbortOnError		= true;
	m_dcb.fOutxCtsFlow		= false;
	m_dcb.fOutxDsrFlow		= false;
	m_dcb.fDtrControl		= DTR_CONTROL_DISABLE;
	m_dcb.fDsrSensitivity	= false;
	m_dcb.fRtsControl		= RTS_CONTROL_DISABLE;
	m_dcb.fOutxCtsFlow		= false;
	m_dcb.fOutxCtsFlow		= false;

	m_bPortReady = SetCommState(m_hComm, &m_dcb);

	if(m_bPortReady == 0)
	{
		MessageBox(L"SetCommState Error", L"Error", MB_OK + MB_ICONERROR);
		CloseHandle(m_hComm);
		return false;
	}

	return true;
}

bool CSerialPort::SetCommunicationTimeouts(DWORD ReadIntervalTimeout,
		DWORD ReadTotalTimeoutMultiplier, DWORD ReadTotalTimeoutConstant,
		DWORD WriteTotalTimeoutMultiplier, DWORD WriteTotalTimeoutConstant)
{
	if((m_bPortReady = GetCommTimeouts(m_hComm, &m_CommTimeouts)) == 0)
		return false;

	m_CommTimeouts.ReadIntervalTimeout			= ReadIntervalTimeout;
	m_CommTimeouts.ReadTotalTimeoutConstant		= ReadTotalTimeoutConstant;
	m_CommTimeouts.ReadTotalTimeoutMultiplier	= ReadTotalTimeoutMultiplier;
	m_CommTimeouts.WriteTotalTimeoutConstant	= WriteTotalTimeoutConstant;
	m_CommTimeouts.WriteTotalTimeoutMultiplier	= WriteTotalTimeoutMultiplier;
	
	m_bPortReady = SetCommTimeouts(m_hComm, &m_CommTimeouts);
		
	if(m_bPortReady == 0)
	{
		MessageBox(L"StCommTimeouts function failed",L"Com Port Error",MB_OK+MB_ICONERROR);
		CloseHandle(m_hComm);
		return false;
	}

	return true;
}

bool CSerialPort::WriteByte(BYTE bybyte)
{
	iBytesWritten=0;
	if(WriteFile(m_hComm, &bybyte, 1, &m_iBytesWritten, NULL) == 0)
		return false;
	else
		return true;
}

bool CSerialPort::ReadByte(BYTE &resp)
{
	BYTE rx;
	resp=0;

	DWORD dwBytesTransferred=0;

	if(ReadFile(m_hComm, &rx, 1, &dwBytesTransferred, 0))
	{
		if(dwBytesTransferred == 1)
		{
			resp=rx;
			return true;
		}
	}
			  
	return false;
}

bool CSerialPort::ReadByte(BYTE* &resp, UINT size)
{
	DWORD dwBytesTransferred=0;

	if(ReadFile(m_hComm, resp, size, &dwBytesTransferred, 0))
	{
		if(dwBytesTransferred == size)
			return true;
	}

	return false;
}

void CSerialPort::ClosePort()
{
	CloseHandle(m_hComm);
	return;
}
3. Seria Port Class 이용하기
Serial Port 관련 Class를 실제로 이용하는 부분입니다. 아래를 참고하셔서 상황에 맞게 이용하시면 됩니다. 만약 일정시간 계속해서 Serial Port로 부터 값을 읽어오는 경우라면 Thread를 이용해서 구현할 수도 있습니다. 또한 간단한 응용 프로그램이라면 While문과 Timeouts 설정을 이용해도 됩니다.
(Timeouts를 설정하는 부분은 다음에 설명하겠습니다.)

아래에 _insertData() 부분은 상황에 따라서 printf()등으로 변경해서 이용하면 됩니다.
보다 세밀한 기능을 위해서 다른 함수를 활용할 필요가  있는데 이것은 다음에 설명하겠습니다.
// 이것저것 다 생략하고...
CSerialPort _serial;

if(_serial.OpenPort(L"COM4"))	// 실제 사용될 COM Port 를 넣어야합니다.
{
	// BaudRate, ByteSize, fParity, Parity, StopBit 정보를 설정해줍니다.
	_serial.ConfigurePort(CBR_115200, 8, FALSE, NOPARITY, ONESTOPBIT);

	// Timeout 설정입니다. 별다른거 없으면 전부 zero 설정해도 됩니다.
	// 경우에 따라서 Timeouts 관련 설정을 해줄 필요가 있습니다.
	// 이것에 대한 설명은 다음에 하도록 하겠습니다.
	_serial.SetCommunicationTimeouts(0, 0, 0, 0, 0);

	// Buffer를 잡고
	BYTE* pByte = new BYTE[512];

	// 읽는데 성공하면 처리하고
	if(_serial.ReadByte(pByte, 512)) 
	{
		pByte[511] = '\0';
		_insertData(CString(reinterpret_cast(pByte)));
	}
	// 위에서 ReadByte는 1byte 이상 읽을 수 있습니다.
	// Write 해주는 부분도 비슷합니다.
	delete [] pByte;
}

// 다 썼으니 닫아주면 끝~
_serial.ClosePort();