간단한 장비제어를 위한 시리얼 통신(C++/MFC)

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;

};

 

RSPort_H_Layer.zip
다운로드

 

소스설명

생성자를 통해 생성후에 ReadComport 멤버함수와 WriteComport 멤버함수를 이용하여 데이터를 송수신합니다.

나머지 부분은 타이머나, 쓰레드를 이용하여 이벤트 기반으로 구성하거나 응용하여 구현하면 될듯합니다.

보통은 장비제어시퀀스(바코드리딩) 에서는 쓰레드내에서 응답대기후에 응답에따라 재시도하거나 에러메세지 출력등으로 운영하게 됩니다.

 

 

* 혹시 ReadFile( m_idComDev, message, dwLength, &dwReadLength, &osRead ); 부분에서 바코드 장치에 따라 dwReadLength가 0임에도 불구하고, 데이터가 수신되는 경우가 있습니다. 나름 처리를 임시방편으로 하였는데 혹시 아시는분은 리플부탁드려요.

반응형