QT

리눅스 가상 시리얼 포트 사용

크게웃기 2019. 6. 21. 11:23
반응형

리눅스에서 시리얼 포트를 사용해야 할 일이 생겨 정리해 놓았다.

 

리눅스는 기본으로 Serail port의 경우 

/dev/ttyS* 로 시작한다. (ubuntu 기준)

 

cd /dev/

ls ttyS* 해보니 ttyS0 ~ ttyS30 까지 있네?? ㅎㅎㅎ

 

 

 

물리적으로 선을 연결 하였을때 ttyS0, ttyS1 차례로 활성화가 된다.

활성화 유/무 확인 방법

$ dmesg | grep ttyS*

[    0.004000] console [tty0] enabled
[    2.219799] 00:05: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A
[    2.245086] 00:06: ttyS1 at I/O 0x2f8 (irq = 3, base_baud = 115200) is a 16550A

 

연결 되면 위 같이 출력되며 물리적으로 연결이 되어 있지 않을때는 아무런 log가 발생이 되지 않는다.

 

직접적으로 선을 연결하지 않고, 

리눅스 내부에서 프로그램끼리 Serial 통신을 하기 위해서는 아래의 pacakge 가 필요하다.

$sudo apt install socat

 

설치 후 아래 명력어를 입력하면 가상에 시리얼포트를 활성화하여 테스트 해볼수 있다.

$socat -d -d pty,raw,echo=0 pty,raw,echo=0

2019/06/18 17:44:02 socat[103459] N PTY is /dev/pts/0
2019/06/18 17:44:02 socat[103459] N PTY is /dev/pts/1
2019/06/18 17:44:02 socat[103459] N starting data transfer loop with FDs [5,5] and [7,7]

 

대신 위에 명시한 ttyS 가 아니라 /pts/0 ~ /pts/ 숫자 로 순차적으로 생성된다.

 

사용 방법은 일반 serial 사용을 위해 fd를 open 할때 path 를 "/dev/pts/0" 으로 생성하면 된다.

 

Serial open source

    QString path = "/dev/pts/0";
//    QString path = "/dev/ttyS0";

	unsigned long _baudrate = 115200;

    QByteArray ba = path.toLocal8Bit();
    const char* c_str2 = ba.data();
    fd = open(c_str2, O_RDWR | O_NOCTTY);
    if(fd < 0)
    {
        qDebug() << QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss.zzz") << Q_FUNC_INFO << "open fail";
    }
    else
    {
        memset(&newtio, 0, sizeof(newtio));
        newtio.c_iflag = IGNPAR;  // non-parity    newtio.c_oflag = 0;
        newtio.c_cflag = CS8 | CLOCAL | CREAD; // NO-rts/cts

        switch( _baudrate )
        {
            case 115200 :
                newtio.c_cflag |= B115200;
                break;
            case 57600  :
                newtio.c_cflag |= B57600;
                break;
            case 38400  :
                newtio.c_cflag |= B38400;
                break;
            case 19200  :
                newtio.c_cflag |= B19200;
                break;
            case 9600   :
                newtio.c_cflag |= B9600;
                break;
            default     :
                newtio.c_cflag |= B115200;
                break;
        }

        //set input mode (non-canonical, no echo,.....)
        newtio.c_lflag = 0;
        newtio.c_cc[VTIME] = 50;

//        newtio.c_cc[VMIN]  = vmin;   // 최소 n 문자 받을 때까진 대기

        tcflush  ( fd, TCIFLUSH );
        tcsetattr( fd, TCSANOW, &newtio );
    }
    

 

 

 

 

시리얼을 통하여 수신 받은 data read.

일반 리눅스 프로그래밍 을 진행 할 때 아래와 같이 read 하여 처리를 하면된다.

    애드블럭 해제시 코드가 보여요.
char buf[1024] = {0x00,};
    
    buf[] = "abcd";

	// buf 데이터 전송
    write( fd, buf, sizeof(buf));
    
    // 데이타를 읽어온다.    
    rdcnt = read( fd, buf, sizeof(buf) );   
    if ( rdcnt > 0 )    
    {    
        buf[rdcnt] = '\0';    
        printf( "<rd=%2d> %s\n", rdcnt, buf );   
    }      
    

 

QT 프로그램의 경우 socket_notifier라는 사용하기 편한 class가 존재하므로 아래와 같이 사용하면된다.

socket notifier 선언.

socket_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
  connect(socket_notifier, SIGNAL(activated(int)), this, SLOT(read(int)));

read(int) 에 noti 발생한 fd 값을 전달해 주므로

위의 일반 리눅스 프로그래밍 코드 부분을 재 사용하면된다.

 

socket notifier 의 장점은 비동기식으로 언제 수신 받을지 모르는 serial 데이타를 read 함수에서 waiting 하는 것이 아니라 해당 fd에 이벤트가 발생 했을 때 activated signal 이 발생되어 resource 부분에서 좋다.

 

2019/06/16 - [QT] - 리눅스에서 USB 정보 가져오기

2019/02/18 - [QT] - QTcpSocket tcp socket client 프로그래밍 예제.

2019/01/31 - [QT] - [QT] QT Application에 SQLite 연결하기

2019/01/31 - [QT] - [QT] Ubuntu 18.04LTS 환경설정에 QT 프레임워크 설치하기(X11 platform 용)

반응형