Visual Studio를 사용하여 개발하는 환경에서, USB 포트를 사용한 시리얼 통신 방법을 알아봅니다.
윈도우는 시리얼 통신의 구현을 위해서 다양한 구조체와 함수들을 지원합니다. 그것들을 사용하기 위해서 먼저 Windows.h
를 include 합니다. 시리얼 핸들을 열고 포트를 지정해서 시리얼 포트를 오픈합니다. COM
시리얼 포트로 통신하는 것은 동명의 파일로 입출력을 시키는 작업입니다.
hSerial = CreateFile("COM5", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
위의 코드는 COM5
포트를 읽고 쓰기 가능한 모드로 열고 있습니다. 통신하고 싶은 포트를 알맞게 지정하면 됩니다.
그리고 원도우에서 시리얼 포트를 열 때 주의해야 할 부분이 있습니다. COM10
이상의 시리얼 포트들은, \\.\
다음에 포트 이름을 붙여 주어야 접근이 가능합니다. 예를 들어 COM16
에 연결할 때는 다음과 같이 해주어야 합니다.
hSerial = CreateFile("\\\\.\\COM16", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
다음으로, 해당 시리얼 포트와 현재 통신할 수 있는지 확인합니다. 시리얼 포트에 통신 가능한 장치가 연결되어 있지 않다면 해당 COMx
포트는 열리지 않습니다.
if (hSerial == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
// 해당 시리얼 포트가 존재하지 않음
printf("Serial port not found!\n");
}else{
// 해당 시리얼 포트가 존재함
}
시리얼 포트가 열렸는지 확인했다면, 통신 데이터의 크기, 보드레이트 등 변수 설정을 진행합니다. 이 작업은 DCB
라는 이름의 구조체를 이용해서 진행합니다.
DCB dcbSerialParams = { 0 };
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
그 후, 해당 COM
시리얼 포트의 상태를 체크합니다.
if (!GetCommState(hSerial, &dcbSerialParams)) {
// Error getting COM port state
printf("Error : Getting COM port state\n");
}
이제 시리얼 통신을 설정해줍니다. 보드레이트, 데이터 사이즈, STOP 비트 갯수, 패리티 비트 여부를 설정해줍니다. 시리얼 통신을 설정할 때 기본이 되는 설정들입니다.
DCB
구조체에 값을 등록한 후, SetCommState
함수로 시리얼 핸들에 적용합니다.
dcbSerialParams.BaudRate = CBR_9600; // 보드레이트 설정
dcbSerialParams.ByteSize = 8; // 데이터 사이즈 설정
dcbSerialParams.StopBits = ONESTOPBIT; // 스탑비트 설정
dcbSerialParams.Parity = NOPARITY; // 패리티 설정
if (!SetCommState(hSerial, &dcbSerialParams)) { // 설정 적용
// Error setting COM port state
printf("Error : Setting COM port state\n");
}
다음으로 연결의 Timeout을 설정해줍니다. 통신에서 Timeout을 설정하는 이유는, 둘 중 어디선가 통신에 문제가 생겼을 경우를 대비하기 위해서입니다. Read 동작의 Timeout과 Write 동작의 Timeout을 모두 설정해줍니다. 설정에는 COMMTIMEOUTS
구조체가 사용됩니다. 모든 값의 단위는 ms(밀리세컨드)입니다.
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (!SetCommTimeouts(hSerial, &timeouts)) {
// Error set timeouts
printf("Error : Setting timeouts\n");
}
Read와 Write 작업의 Timeout을 50ms로 설정해주었습니다.
여기까지 해서 초기화 작업이 종료되었습니다. 이제 열린 Serial 포트 파일을 통해 데이터를 읽고 써주면 됩니다. 먼저 Read입니다.
DWORD dwBytesRead = 0;
int readSize = 10;
unsigned char buff[10];
if (ReadFile(hSerial, buff, readSize, &dwBytesRead, NULL)) {
printf("%X ", buff[0]); // Print data sample
}
읽는 작업은 단순합니다. 10바이트를 시리얼 포트에서 읽어온 후, buff
배열에 저장하고 있습니다.
Write 작업도 비슷합니다.
DWORD dwBytesWrite = 0;
int writeSize = 10;
unsigned char str[10] = "MagmaTart";
if (WriteFile(hSerial, str, writeSize, &dwBytesWrite, NULL)) {
// Write Complete
}
통신이 끝난 시리얼 포트는, CloseHandle
함수를 통해 닫을 수 있습니다.
CloseHandle(hSerial);