2021. 1. 7. 20:29ㆍ장비제어개발관한이야기
안녕하세요 Young입니다.
장비회사에서 시리얼통신이란?
자동화설비 제어 프로그램시에서는 RS232통신을 사용하여 바코드나 OCR리더기, 기타 통신에 많이 이용되고 있습니다.
특히, 바코드부분에서는 거의 RS232 통신을 이용하여 제어하고 있습니다.
저희 회사에서는 주로 쓰는 바코드는 DataLogic 이나 코그넥스(Cognex) 인데, 전부 시리얼통신으로 사용하고 있습니다.
주 통신 시나리오는, 시리얼통신으로 특정 소프트웨어 트리거프로토콜을 보낸뒤에 응답받는 구조받는 시퀀스로 구성됩니다.
시리얼통신에 대해 자세히는 아니더라도 기본적은 통신 프로토콜에 대한 이해+사용법을 숙지해야 장비개발에 어려움이 없을 듯합니다.
간단히 사용하려면, 기본적인 사용법은 MFC에서는 거의 비슷하기 때문에 한번 보시고 실제 장비와 통신을 해보면 빠르게 습득 하실수 있을듯합니다.
밑에 소스는 제가 쓰고 있는 시리얼 통신 랩핑클래스입니다.
RSPort.h
// RsPort.cpp: implementation of the CRsPort class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "RsPort.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CRsPort::CRsPort( CString m_portName )
{
dcb_setup.BaudRate = CBR_19200;
dcb_setup.ByteSize = 8;
dcb_setup.Parity = NOPARITY;
dcb_setup.StopBits = ONESTOPBIT;
initComport( m_portName );
}
CRsPort::CRsPort( CString m_portName, DWORD BaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits )
{
dcb_setup.BaudRate = BaudRate;
dcb_setup.ByteSize = ByteSize;
dcb_setup.Parity = Parity;
dcb_setup.StopBits = StopBits;
initComport( m_portName );
}
CRsPort::~CRsPort()
{
CloseCommPort();
}
void CRsPort::initComport(CString m_portName)
{
COMMTIMEOUTS commTimeOuts;
m_idComDev = CreateFile( (LPCTSTR) m_portName, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );
if( m_idComDev == (HANDLE) -1)
{
CloseHandle( m_idComDev );
m_Connect = FALSE;
//AfxMessageBox(_T("WARNING : 포트를 여는데 실패하였습니다."));
}
else
{
SetCommMask( m_idComDev, EV_RXCHAR );
SetupComm( m_idComDev, 4096,4096);
PurgeComm( m_idComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR );
commTimeOuts.ReadIntervalTimeout = -1;
commTimeOuts.ReadTotalTimeoutMultiplier = 0;
commTimeOuts.ReadTotalTimeoutConstant = 1000;
commTimeOuts.WriteTotalTimeoutMultiplier = 0;
commTimeOuts.WriteTotalTimeoutConstant = 1000;
SetCommTimeouts( m_idComDev, &commTimeOuts );
m_Connect = SetupConnection();
osWrite.Offset = 0;
osWrite.OffsetHigh = 0;
osRead.Offset = 0;
osRead.OffsetHigh = 0;
osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
}
}
BOOL CRsPort::SetupConnection()
{
BOOL fRetVal;
DCB dcb;
dcb.DCBlength = sizeof( DCB );
GetCommState( m_idComDev, &dcb );
dcb.BaudRate = dcb_setup.BaudRate;
dcb.ByteSize = dcb_setup.ByteSize;
dcb.Parity = dcb_setup.Parity;
dcb.StopBits = dcb_setup.StopBits;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fOutxCtsFlow = 0;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fInX = dcb.fOutX = 0; // XON/XOFF
dcb.XonChar = 0x11; // ASCII_XON;
dcb.XoffChar = 0x13; // ASCII_XOFF;
dcb.XonLim = 100;
dcb.XoffLim = 100;
dcb.fBinary = TRUE;
dcb.fParity = TRUE;
fRetVal = SetCommState( m_idComDev, &dcb );
return fRetVal;
}
void CRsPort::CloseCommPort()
{
if( m_Connect == FALSE ) return;
CloseHandle( m_idComDev );
CloseHandle( osRead.hEvent );
CloseHandle( osWrite.hEvent );
}
int CRsPort::WriteCommPort(unsigned char *message, DWORD dwLength)
{
int ret_code;
ret_code = WriteFile( m_idComDev, message, dwLength, &dwLength, &osWrite);
return ret_code;
}
int CRsPort::ReadCommPort(unsigned char *message, DWORD length)
{
COMSTAT ComStat;
DWORD dwErrorFlags;
DWORD dwLength;
DWORD dwReadLength = 0;
CStringA strTemp;
strTemp.Format("%s",message);
if( m_Connect == FALSE ) return 0;
else
{
ClearCommError( m_idComDev, &dwErrorFlags, &ComStat );
dwLength = min((DWORD) length, ComStat.cbInQue);
ReadFile( m_idComDev, message, dwLength, &dwReadLength, &osRead );
}
if(dwReadLength == 0)
{
CStringA str;
str.Format("%s", message);
if(strTemp != str)
{
return str.GetLength();
}
}
return dwReadLength;
}
bool CRsPort::IsCommPortOpen()
{
if(m_Connect)
return true;
return false;
}
RSPort.cpp
#pragma once
#define NUL 0x00
#define SOH 0x01
#define STX 0x02
#define ETX 0x03
#define EOT 0x04
#define ENQ 0x05
#define ACK 0x06
#define NAK 0x15
#define XON 0x11
#define XOFF 0x13
#define ESC 0x1b
#define CR 0x0d
#define LF 0x0a
const int DEF_MAX_SERIAL_PORT = 6; ///< Serial Port 수량
const int ERR_SERIAL_PORT_SUCCESS = 0; // Success
const int ERR_PORT_OPEN_FAIL = 1;
const int ERR_TIME_OUT = 2;
class CRsPort
{
public:
BOOL m_Connect;
HANDLE m_idComDev;
public:
int ReadCommPort( unsigned char *message, DWORD length);
int WriteCommPort( unsigned char* message, DWORD dwLength);
bool IsCommPortOpen();
CRsPort(CString m_portName);
CRsPort( CString m_portName, DWORD BaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits );
virtual ~CRsPort();
protected:
void CloseCommPort(void);
BOOL SetupConnection(void);
void initComport( CString m_portName );
OVERLAPPED osWrite;
OVERLAPPED osRead;
DCB dcb_setup;
};
소스설명
생성자를 통해 생성후에 ReadComport 멤버함수와 WriteComport 멤버함수를 이용하여 데이터를 송수신합니다.
나머지 부분은 타이머나, 쓰레드를 이용하여 이벤트 기반으로 구성하거나 응용하여 구현하면 될듯합니다.
보통은 장비제어시퀀스(바코드리딩) 에서는 쓰레드내에서 응답대기후에 응답에따라 재시도하거나 에러메세지 출력등으로 운영하게 됩니다.
* 혹시 ReadFile( m_idComDev, message, dwLength, &dwReadLength, &osRead ); 부분에서 바코드 장치에 따라 dwReadLength가 0임에도 불구하고, 데이터가 수신되는 경우가 있습니다. 나름 처리를 임시방편으로 하였는데 혹시 아시는분은 리플부탁드려요.
'장비제어개발관한이야기' 카테고리의 다른 글
유용한 시간관련 클래스[MFC/C++] (1) | 2021.01.10 |
---|---|
[UTIL] Beyond Compare 없으면 안됩니다. (1) | 2021.01.07 |
Bayer 변환과 카메라 제어 (0) | 2021.01.06 |
장비제어에서 소켓통신이 필요한 순간... (1) | 2021.01.06 |
심플하고 강력한 MFC,C++ 용 XML 파서(CMarkup) (0) | 2021.01.05 |