Easy ways

[C 언어] 시리얼 통신 Open 함수 (struct termios) 본문

프로그래밍 언어/C언어

[C 언어] 시리얼 통신 Open 함수 (struct termios)

softColors 2021. 2. 23. 11:55
반응형

안녕하세요! 

오늘은 센서에서 많이 사용하는 시리얼 통신의 옵션을 설정하는 법을 알아보겠습니다.

옵션을 모두 설명하기엔 너무 많아서 많이 쓴다고 생각되는 것들을 위주로 설명드리겠습니다.

 

실행 환경은 linux 입니다.

 

먼저 일반적인 시리얼 통신 Open 함수는 아래와 같습니다.

// C library headers
#include <stdio.h>
#include <string.h>

// Linux headers
#include <fcntl.h> // Contains file controls like O_RDWR
#include <termios.h> // Contains POSIX terminal control definitions
#include <unistd.h> // write(), read(), close()


int main(void)
{
	struct termios newtio;
	int ttyfd;
 	char *ttyname = "/dev/ttyS0";
	ttyfd = open(ttyname, O_RDWR | O_NOCTTY);
	
	if(ttyfd < 0)
	{
		printf( ">> tty Open Fail [%s]\r\n ", ttyname);
		return -1;
	}
	memset( &newtio, 0, sizeof(newtio) );
	
	newtio.c_cflag = B115200 | CS8 | CLOCAL | CREAD | CRTSCTS;
	newtio.c_iflag = IGNPAR;
	newtio.c_oflag = 0;

	//set input mode (non-canonical, no echo,.....)
	newtio.c_lflag     = 0;     // LF recive filter unused
	newtio.c_cc[VTIME] = 0;     // inter charater timer unused
	newtio.c_cc[VMIN]  = 0;     // blocking read until 1 character arrives

	tcflush( ttyfd, TCIFLUSH ); // inital serial port
	tcsetattr( ttyfd, TCSANOW, &newtio ); // setting serial communication
	printf( "## ttyo1 Opened [%s]\r\n", ttyname);
	
    
	// 아래 부분은 테스트 코드 전용으로 실제로 사용하실때는 삭제바랍니다
	close(ttyfd); //close serial port
	return 0;
}

 

 

open은 대충 포트를 연다는 소리인 것 같은데.. 도대체 c_cflag, c_iflag 가 무슨 뜻일까요?

 

기본적으로 flag는 깃발이란 의미이죠? 

코드에서의 flag는 스위치라고 생각하시는 것이 좋습니다. 

 

위 코드로 예를 들면 /dev/ttyS0라는 포트를 열건대 이 포트는 어떤 식으로 통신할지를 

스위치를 달칵 달칵하면서 설정해주는 겁니다. 

 

예를 들면 

newtio.c_cflag = B115200 | CS7;

위 코드를 문장으로 바꾸면 아래와 같이 표시할 수 있습니다!

 

시리얼 통신 flag 예시

 

한마디로, flag는 각각 시리얼 통신의 입력, 출력 , 제어 등을 설정해주는 스위치의 모음인  셈입니다.

[ 이 변수를 제공하는 헤더 파일은 termios.h입니다. ]

 

변수 선언


자 이제 코드를 처음부터 풀어보겠습니다.

int ttyfd;
struct termios newtio;
char *ttyname = "/dev/ttyS0";

먼저 int로 선언된 변수에는 시리얼 포트의 파일 디스크립터가 들어갈 겁니다. 

파일 디스크립터는 어떠한 통신 포트를 열었을 때 컴퓨터가 찾아가기 쉽게

정수로 숫자를 매겨주는 거라고 생각하시면 됩니다.

 

문자열 포인터 변수에 선언되어있는 경로  (/dev/ttyS0)는  제가 열고 싶은 시리얼 포트의 이름입니다.

linux가 익숙하지 않은 분들은 Window로 치자면 "COM1" 느낌이라고 생각하시면 됩니다

 

익숙한 정수형 (int), 문자열 포인터(char *) 다음 생소한 struct termios라는 구조체가 있습니다. 

이 구조체는 아래와 같이 정의되어있습니다.

 

struct termios {
	tcflag_t c_iflag;
	tcflag_t c_oflag;
	tcflag_t c_cflag;
	tcflag_t c_lflag;
	cc_t c_cc[NCCS];
	speed_t c_ispeed;
	speed_t c_ospeed;
};

자세히 보니 위에서 설명했던 Flag의 집합이네요!

 

 

struct termios 예시

struct termios 구조체는 시리얼 통신을 제어할 수 있는 스위치들이 모여있는 박스인 셈입니다.

단순히 serial 통신만 제어할 수 있는 건 아니긴 하지만 이 글에선 다루지 않겠습니다.

 

Open


이제 시리얼 포트를 열기 위한 open 함수를 사용해 보겠습니다.

	ttyfd = open(ttyname, O_RDWR | O_NOCTTY);
	
	if(ttyfd < 0)
	{
		printf( ">> tty Open Fail [%s]\r\n ", ttyname);
		return -1;
	}

open  함수는 문자열로 선언된 경로(ttyname)를 받아서 파일 디스크립터(ttyfd)로 반환하는 함수입니다.

open에 실패하면 반환된 파일 디스크립터가 0보다 작습니다.

open 함수 아래의 if 문은 잘못 open에 실패한 경우 처리해주는 역할을 합니다.

실패의 예를 들면 선언된 경로에 아무것도 없는 경우 실패합니다

 

open 함수의 원형은 다음과 같습니다. 

 

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

 

보시면 open 함수에도 flag가 있는 걸 볼 수 있습니다.

open 함수의 flag는 정말 다양한데요 자주 쓰이는 것들만 살펴보겠습니다.

 

우선적으로 액세스 모드는 꼭 설정해줘야 합니다.

액세스 모드는 읽기(수신)만 할 건지 쓰기만 할 건지(송신) 모두 할 건지를 결정하는 것입니다. 

Flag Description
O_RDONLY 읽기만
O_WRONLY 쓰기만
O_RDWR 읽기/쓰기 모두


위 코드에선 0_RDWR을 사용하여 읽기/쓰기 모두를 선택해 주었네요.

 

다음으로 시리얼 통신에서 자주 사용하는 Flag 들음과 그에 대한 설명은 다음과 같습니다.

Flag Description Flag Description
O_NOCTTY 제어 제한 
이를 지정하지않으면 키보드 중단 신호등의 영향을 받습니다. 
O_NDELAY 수신 대기 차단
read() 시 수신된 데이터 없으면 바로 반환

O_NDELAY 플래그는 차단이 가능한 경우만 적용되기 때문에 명확한 느낌이 아닙니다.

저는 개인적으로 c_cc [VTIME]이나 select를 쓰는 편입니다. 

 

 

Flag 설정


이제부터 각 flag 가 무엇을 의미하는지 알아보겠습니다.

 

코드를 보시면 여러 가지 종류의 flag 가 있는 것을 확인할 수 있습니다.

newtio.c_cflag = B115200 | CS8 | CLOCAL | CREAD | CRTSCTS;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;

//set input mode (non-canonical, no echo,.....)
newtio.c_lflag     = 0;     // LF recive filter unused
newtio.c_cc[VTIME] = 0;     // inter charater timer unused
newtio.c_cc[VMIN]  = 0;     // blocking read until 1 character arrives

 

먼저 flag 이름에 대한 정의부터 알아보겠습니다.

tcflag_t c_iflag 입력 모드 (input)
tcflag_t c_oflag 출력 모드 (output)
tcflag_t c_cflag 제어 모드 (control)
tcflag_t c_lflag 로컬 모드 (local)
cc_t c_cc [VTIME] Time out 설정
cc_t c_cc [VMIN] 최소 반환 Character 수 

 

이 중에서 많이 쓰는 입력 모드와 제어 모드 시간 설정 등에 대하여 알아보겠습니다.

 

c_cflag : 제어 모드


제어 모드는 시리얼통신에 가장 기본적인 Baudrate, Data Bit, Stop bit, Parity bit 등을 설정합니다.

 

BaudRate

    Baudrate는 시리얼 통신 속도를 뜻하며, 단위는 "bps(bit per second)"를 사용합니다.

    1초에 얼마의 데이터를 보낼 수 있느냐를 뜻합니다

    종류는 다음과 같습니다. (B0은 연결 종료를 의미합니다.)

 

 B0  B50  B75  B110  B134
 B150  B200  B300  B600  B1200
 B1800  B2400  B4800  B9600  B19200
 B38400  B57600   B115200   B230400   

 

DataBits

    CSIZE :  문자 크기 마스크. CS5 , CS6 , CS7, CS8가 있습니다.

 

 

StopBits

    CSTOPB    : Stop bit 수 2개 [newtio.c_cflag|= CSTOPB]

    ~CSTOPB : Stop bit 수 1개 [newtio.c_cflag&= ~CSTOPB]

 

패리티 비트(Parity Bit)

    IGNPAR : 프레임 오류 및 패리티 오류를 무시합니다.

    PARENB : 출력 시 패리티 생성을 활성화하고 입력. 

    PARODD : 설정되면 입력 및 출력의 패리티가 홀수입니다. 반전되면 패리티가 짝수 [~PARODD]

 

Others

 

    CRTSCTS : RTS / CTS (하드웨어) 흐름 제어를 활성화합니다

    CLOCAL : 모뎀 제어 라인 무시(Carrier Detect 신호)

    CREAD : 수신 활성화.

 

 

c_iflag : 입력 모드


입력 모드는 시리얼 포트에 수신되는 데이터에 대한 처리를 의미합니다. 

 

    INPCK   : 입력 패리티 검사가 활성화
    IGNPAR :  패리티 오류가 있는 모든 바이트가 무시
    PARMRK : 패리티 오류가있는 입력 바이트가 표시

    ISTRIP     : 유효한 입력 바이트가 7 비트로 제거

c_cc [VTIME] /  c_cc [VMIN] 

 


VTIME : 데이터 수신 대기 시간. [read() 얼마나 기다리고 있을지]

VMIN  : 최소 얼마의 데이터가 들어왔을 때 읽어올지  

 

Flag Description 입력이 없는 경우 반환 가능 여부
VMIN = 0 , VTIME = 0 들어온데이터가 없어도 즉시반환 [수신된 데이터 길이 0 반환가능] O
VMIN = 0 and VTIME > 0 VTIME에 대한 시간이 지나면 입력데이터가 없어도 반환[수신된 데이터 길이 0 반환 가능] O
VMIN > 0 and VTIME = 0 수신된 문자 수가 VMIN 이상일때 반화됩니다. [수신된 데이터 길이 0을 반환하지 않습니다.] X
VMIN > 0 and VTIME > 0 VTIME에 대한 시간이 지났거나 (or) 수신된 문자 수가 VMIN 이상일때 반화됩니다. [수신된 데이터 길이 0을 반환하지 않습니다.] X

 

위에 설명한 것 이외에도 정말 여러 가지 종류의 flag가 있습니다.

혹시나 다른 flag 도 알고 싶으시다면 참조 2번에서 확인하실 수 있습니다.

 

 

 

마지막으로 아래 코드를 사용해서 시리얼 포트에 설정을 입혀줍니다. 

	tcflush( ttyfd, TCIFLUSH ); // 시리얼 포트 초기화
	tcsetattr( ttyfd, TCSANOW, &newtio ); // 시리얼 포트에 설정 입력

 

이제 시리얼 통신을 할 준비가 되었습니다!

 

 

참조 :

1. www.cmrr.umn.edu/~strupp/serial.html

2. man7.org/linux/man-pages/man3/termios.3.html

3. www.gnu.org/software/libc/manual/html_node/Input-Modes.html

반응형
Comments