시리얼 터미널 접속

Juni and ING·2020년 8월 10일
0

INITIAL

필요 스킬

  • 시리얼 장치를 Open한 후 R/W
  • 시리얼 장치로부터 데이터를 수신하고 그 데이터를 처리할 로직
  • 키보드로부터 키 입력을 받아 시리얼 장치로 데이터를 송신하는 로직

시리얼 함수

#define TRANSFER_SPECIAL_CHARACTER

static int fd = -1;
static struct termios oldTio, newTio;
static bool initialized = false;

#define isInitialized() (initialized);
#define set_initialized(x) (initialized = x);

static int get_serial_fd(void)
{
    return fd;
}

static int open_serial(const char * const dev)
{
    fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
    return (fd >= 0);
}

static void term_serial(void)
{
    if(isInitialized() == true)	tcsetattr(fd, TCSANOW, &oldTio);
    if(fd >= 0)	close(fd);
}

static void init_serial(void)
{
    tcgetattr(fd, &oldTio);
    
    newTio.c_flag = B115200 | CS8 | CLOCAL | CREAD;
    newTio.c_iflag = 0;
    newTio.c_oflag = 0;
    newTio.c_lflag = 0;
    newTio.c_cc[VTIME] = 0;
    newTio.c_cc[VMIN] = 0;
    
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newTio);
    
    set_initialized(true);
}

static void write_serial(const char* const pBuf, const int len)
{
    int cnt;
    cnt = write(fd, pBuf, len);
}

static int read_serial(char* const pBuf, const int len)
{
    int cnt, totalCnt, freeSize;
    char buf[len];
    
    freeSize = len;
    pBuf[0];
    
    while(freeSize > 0){
    	cnt = read(fd, buf, freeSize);
        if(cnt < 0)	break;
        
        buf[cnt] = 0;
        strcat(pBuf, buf);
        freeSize -= cnt;
    }
    
    totalCnt = len - freeSize;
    
    return totalCnt;
}

POLL 이벤트 함수

static int pollState;
static struct pollfd pollEvents;
static pthread_t thread;

static void init_poll(const int fd)
{
    pollEvents.fd = fd;
    pollEvents.events = POLLIN | POLLERR;
    pollEvents.revents = 0;
}

void* start_poll_routine(void *arg)
{
    int cnt, len;
    char rBuf[MAX_BUF_SIZE] = {0,};
    
    while(true){
    	pollState = poll(&pollEvents, 1, -1);
        if(pollState > 0){
            if(pollEvents.revents & POLLIN){
                cnt = read_serial(rBuf, MAX_STR_LEN);
                fputs(rBuf, stdout);
                fflush(stdout);
            }
        
            if(pollEvents.revents & POLLERR){
            	eprint("ERROR on COM Line");
            }
            
            pollEvents.revents = 0;
        }
    }
}

static void start_poll(void)
{
    pthread_create(&thread, NULL, start_poll_routine, NULL);
}

static void stop_poll(void)
{
    pthread_cancel(thread);
    pthread_join(thread, NULL);
}

키 입력 함수

static incline char getch(void)
{
    static char c;
    static struct termios save, now;
    
    tcgetattr(0, &save);
    memcpy(&now, &save, sizeof(struct termios));
    #ifdef TRANSFER_SPECIAL_CHARACTER
    now.c_iflag |= IGNBRK;
    now.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF);
    now.c_lflag &= ~(ICANON | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN);
    now.c_cc[VMIN] = 1;
    now.c_cc[VTIME] = 0;
    
    #else /*TRANSFER_SPECIAL_CHARACTER*/
    now.c_lflag &= ~(ICANON | ECHO);
    now.c_cc[VMIN] = 1;
    now.c_cc[VTIME] = 0;
    #endif /*TRANSFER_SPECIAL_CHARACTER*/
    
    tcsetattr(0, TCSAFLUSH, &now);
    c = getchar();
    tcsetattr(0, TCSAFLUSH, &save);
    
    return c;
}
  • 키 하나하나씩 입력될 때마다 값을 받아오려면, l_flag에서 ICANON, ECHO 비트만 Clear하면 된다.
  • 하지만, Ctrl+C 같이 커널의 특정 기능을 수행하는 특수한 문자들까지 직접 키 값으로 받아오려면 추가 설정을 해줘야 한다.
    • Ctrl+C 같이 시그널을 발생시키는 키 값을 가져오려면, ISIG 플래그를 Clear한다.
    • Ctrl+Q, S의 키 값을 가져오려면, IXON, IXOFF 플래그를 Clear한다.
    • 기타 특수키 값을 가져오려면, IEXTEN 플래그를 Clear한다.

절차

  1. 시리얼 장치 Open
  2. 시리얼 설정
  3. 수신 데이터 처리 설정
  4. 수신 데이터 처리 로직 시작
  5. 키 입력 및 데이터 송신 시작
  6. 수신 데이터 처리 로직 종료
  7. 시리얼 장치 Term

시리얼 장치 OPEN

static int open_serial(const char * const dev)
{
    fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
    return (fd >= 0);
}

시리얼 설정

static void init_serial(void)
{
    tcgetattr(fd, &oldTio);
    
    newTio.c_flag = B115200 | CS8 | CLOCAL | CREAD;
    newTio.c_iflag = 0;
    newTio.c_oflag = 0;
    newTio.c_lflag = 0;
    newTio.c_cc[VTIME] = 0;
    newTio.c_cc[VMIN] = 0;
    
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newTio);
    
    set_initialized(true);
}

수신 데이터 처리 설정

static void init_poll(const int fd)
{
    pollEvents.fd = fd;
    pollEvents.events = POLLIN | POLLERR;
    pollEvents.revents = 0;
}

수신 데이터 처리 로직 시작

static void start_poll(void)
{
    pthread_create(&thread, NULL, start_poll_routine, NULL);
}

키 입력 및 데이터 송신 시작

static void start_keyin(void)
{
    char c;

    while(true){
        c = getchar();
        write_serial(&c, 1);
    }
}

수신 데이터 처리 로직 종료

static void stop_poll(void)
{
    pthread_cancel(thread);
    pthread_join(thread, NULL);
}

시리얼 TERMINATION

static void term_serial(void)
{
    if(isInitialized() == true)	tcsetattr(fd, TCSANOW, &oldTio);
    if(fd >= 0)	close(fd);
}
profile
인기는 없지만 그래도 임베디드를 사랑하는 한 개발자

0개의 댓글