'RS-232'에 해당되는 글 2건



A Linux serial port test program

Below is a Linux serial test program which requires the command parameters to be entered on the command line when the program is started. This program will send output typed on the computer keyboard after the program is started, through the serial port specified. This program can be downloaded using this link: com.c. I recommend that you right click on the link to download it rather than viewing it in your browser and saving it so you do not get carriage returns and line feeds in the text which may cause the compilation to fail.

#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/types.h>

#define BAUDRATE B38400
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1    //POSIX compliant source
#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;

void signal_handler_IO (int status);    //definition of signal handler
int wait_flag=TRUE;                     //TRUE while no signal received
char devicename[80];
long Baud_Rate = 38400;         // default Baud Rate (110 through 38400)
long BAUD;                      // derived baud rate from command line
long PARITY;
int Data_Bits = 8;              // Number of data bits
int Stop_Bits = 1;              // Number of stop bits
int Parity = 0;                 // Parity as follows:
                  // 00 = NONE, 01 = Odd, 02 = Even, 03 = Mark, 04 = Space
int Format = 4;
FILE *input;
FILE *output;
int status;

main(int Parm_Count, char *Parms[])
   char version[80] = "       POSIX compliant Communications test program version 1.00 4-25-1999\r\n";
   char version1[80] = "          Copyright(C) Mark Zehner/Peter Baumann 1999\r\n";
   char version2[80] = " This code is based on a DOS based test program by Mark Zehner and a Serial\r\n";
   char version3[80] = " Programming POSIX howto by Peter Baumann, integrated by Mark Zehner\r\n";  
   char version4[80] = " This program allows you to send characters out the specified port by typing\r\n";
   char version5[80] = " on the keyboard.  Characters typed will be echoed to the console, and \r\n";
   char version6[80] = " characters received will be echoed to the console.\r\n";
   char version7[80] = " The setup parameters for the device name, receive data format, baud rate\r\n";
   char version8[80] = " and other serial port parameters must be entered on the command line \r\n";
   char version9[80] = " To see how to do this, just type the name of this program. \r\n";
   char version10[80] = " This program is free software; you can redistribute it and/or modify it\r\n";
   char version11[80] = " under the terms of the GNU General Public License as published by the \r\n";
   char version12[80] = " Free Software Foundation, version 2.\r\n";
   char version13[80] = " This program comes with ABSOLUTELY NO WARRANTY.\r\n";
   char instr[100] ="\r\nOn the command you must include six items in the following order, they are:\r\n";
   char instr1[80] ="   1.  The device name      Ex: ttyS0 for com1, ttyS1 for com2, etc\r\n";
   char instr2[80] ="   2.  Baud Rate            Ex: 38400 \r\n";
   char instr3[80] ="   3.  Number of Data Bits  Ex: 8 \r\n";
   char instr4[80] ="   4.  Number of Stop Bits  Ex: 0 or 1\r\n";
   char instr5[80] ="   5.  Parity               Ex: 0=none, 1=odd, 2=even\r\n";
   char instr6[80] ="   6.  Format of data received:  1=hex, 2=dec, 3=hex/asc, 4=dec/asc, 5=asc\r\n";
   char instr7[80] =" Example command line:  com ttyS0 38400 8 0 0 4 \r\n";
   char Param_strings[7][80];
   char message[90];

   int fd, tty, c, res, i, error;
   char In1, Key;
   struct termios oldtio, newtio;       //place for old and new port settings for serial port
   struct termios oldkey, newkey;       //place tor old and new port settings for keyboard teletype
   struct sigaction saio;               //definition of signal action
   char buf[255];                       //buffer for where data is put
   input = fopen("/dev/tty", "r");      //open the terminal keyboard
   output = fopen("/dev/tty", "w");     //open the terminal screen

   if (!input || !output)
      fprintf(stderr, "Unable to open /dev/tty\n");

   fputs(version,output);               //display the program introduction
   //read the parameters from the command line
//if there are the right number of parameters on the command line

if (Parm_Count==7)
{ for (i=1; i&#60Parm_Count; i++) // for all wild search parameters { strcpy(Param_strings[i-1],Parms[i]); } i=sscanf(Param_strings[0],"%s",devicename); if (i != 1) error=1; i=sscanf(Param_strings[1],"%li",&Baud_Rate); if (i != 1) error=1; i=sscanf(Param_strings[2],"%i",&Data_Bits); if (i != 1) error=1; i=sscanf(Param_strings[3],"%i",&Stop_Bits); if (i != 1) error=1; i=sscanf(Param_strings[4],"%i",&Parity); if (i != 1) error=1; i=sscanf(Param_strings[5],"%i",&Format); if (i != 1) error=1; sprintf(message,"Device=%s, Baud=%li\r\n",devicename, Baud_Rate); //output the received setup parameters fputs(message,output); sprintf(message,"Data Bits=%i Stop Bits=%i Parity=%i Format=%i\r\n",Data_Bits, Stop_Bits, Parity, Format); fputs(message,output); } //end of if param_count==7 if ((Parm_Count==7) && (error==0)) //if the command line entries were correct { //run the program tty = open("/dev/tty", O_RDWR | O_NOCTTY | O_NONBLOCK); //set the user console port up tcgetattr(tty,&oldkey); // save current port settings //so commands are interpreted right for this program // set new port settings for non-canonical input processing //must be NOCTTY newkey.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newkey.c_iflag = IGNPAR; newkey.c_oflag = 0; newkey.c_lflag = 0; //ICANON; newkey.c_cc[VMIN]=1; newkey.c_cc[VTIME]=0; tcflush(tty, TCIFLUSH); tcsetattr(tty,TCSANOW,&newkey); switch (Baud_Rate) { case 38400: default: BAUD = B38400; break; case 19200: BAUD = B19200; break; case 9600: BAUD = B9600; break; case 4800: BAUD = B4800; break; case 2400: BAUD = B2400; break; case 1800: BAUD = B1800; break; case 1200: BAUD = B1200; break; case 600: BAUD = B600; break; case 300: BAUD = B300; break; case 200: BAUD = B200; break; case 150: BAUD = B150; break; case 134: BAUD = B134; break; case 110: BAUD = B110; break; case 75: BAUD = B75; break; case 50: BAUD = B50; break; } //end of switch baud_rate switch (Data_Bits) { case 8: default: DATABITS = CS8; break; case 7: DATABITS = CS7; break; case 6: DATABITS = CS6; break; case 5: DATABITS = CS5; break; } //end of switch data_bits switch (Stop_Bits) { case 1: default: STOPBITS = 0; break; case 2: STOPBITS = CSTOPB; break; } //end of switch stop bits switch (Parity) { case 0: default: //none PARITYON = 0; PARITY = 0; break; case 1: //odd PARITYON = PARENB; PARITY = PARODD; break; case 2: //even PARITYON = PARENB; PARITY = 0; break; } //end of switch parity //open the device(com port) to be non-blocking (read will return immediately) fd = open(devicename, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd < 0) { perror(devicename); exit(-1); } //install the serial handler before making the device asynchronous saio.sa_handler = signal_handler_IO; sigemptyset(&saio.sa_mask); //saio.sa_mask = 0; saio.sa_flags = 0; saio.sa_restorer = NULL; sigaction(SIGIO,&saio,NULL); // allow the process to receive SIGIO fcntl(fd, F_SETOWN, getpid()); // Make the file descriptor asynchronous (the manual page says only // O_APPEND and O_NONBLOCK, will work with F_SETFL...) fcntl(fd, F_SETFL, FASYNC); tcgetattr(fd,&oldtio); // save current port settings // set new port settings for canonical input processing newtio.c_cflag = BAUD | CRTSCTS | DATABITS | STOPBITS | PARITYON | PARITY | CLOCAL | CREAD; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; newtio.c_lflag = 0; //ICANON; newtio.c_cc[VMIN]=1; newtio.c_cc[VTIME]=0; tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); // loop while waiting for input. normally we would do something useful here while (STOP==FALSE) { status = fread(&Key,1,1,input); if (status==1) //if a key was hit { switch (Key) { /* branch to appropiate key handler */ case 0x1b: /* Esc */ STOP=TRUE; break; default: fputc((int) Key,output); // sprintf(message,"%x ",Key); //debug // fputs(message,output); write(fd,&Key,1); //write 1 byte to the port break; } //end of switch key } //end if a key was hit // after receiving SIGIO, wait_flag = FALSE, input is available and can be read if (wait_flag==FALSE) //if input is available { res = read(fd,buf,255); if (resɬ) { for (i=0; i<res; i++) //for all chars in string { In1 = buf[i]; switch (Format) { case 1: //hex sprintf(message,"%x ",In1); fputs(message,output); break; case 2: //decimal sprintf(message,"%d ",In1); fputs(message,output); break; case 3: //hex and asc if ((In1ថ) || (In1)) { sprintf(message,"%x",In1); fputs(message,output); } else fputc ((int) In1, output); break; case 4: //decimal and asc default: if ((In1ថ) || (In1)) { sprintf(message,"%d",In1); fputs(message,output); } else fputc ((int) In1, output); break; case 5: //asc fputc ((int) In1, output); break; } //end of switch format } //end of for all chars in string } //end if resɘ // buf[res]=0; // printf(":%s:%d\n", buf, res); // if (res==1) STOP=TRUE; /* stop loop if only a CR was input */ wait_flag = TRUE; /* wait for new input */ } //end if wait flag == FALSE } //while stop==FALSE // restore old port settings tcsetattr(fd,TCSANOW,&oldtio); tcsetattr(tty,TCSANOW,&oldkey); close(tty); close(fd); //close the com port } //end if command line entrys were correct else //give instructions on how to use the command line { fputs(instr,output); fputs(instr1,output); fputs(instr2,output); fputs(instr3,output); fputs(instr4,output); fputs(instr5,output); fputs(instr6,output); fputs(instr7,output); } fclose(input); fclose(output); } //end of main /*************************************************************************** * signal handler. sets wait_flag to FALSE, to indicate above loop that * * characters have been received. * ***************************************************************************/ void signal_handler_IO (int status) { // printf("received SIGIO signal.\n"); wait_flag = FALSE; }

블로그 이미지


불만있으면 떠나라...



시리얼포트의 개요


흔히 시리얼통신에 대해 많은 사람들이 오해하고 있는 것 중의 하나가【시리얼통신 = RS-232C】관계라고 생각하는 것이고, 또 다른 오해는 시리얼통신이 터미널 디바이스간의 통신규약으로 생각한다는 점이다. (예컨대 원격에 떨어져 있는 PC() PC()간의 연결을 위한 회선규약이 RS-232C라고 생각하는 것)

여기서 데이터를 주고 받는 최 종단의 장비인 PC(甲과 乙) "데이터 터미널 장비"(줄여서 DTE)라고 부른다.

그런데, 시리얼통신은 이들 DTE사이의 통신선로에 대한 규약이 아니다. DTE 사이에는 이들을 연결시켜주기 위한 통신장비가 존재하는데(예컨대 모뎀 따위 등) 이들을 "데이터 통신 장비"(줄여서 DCE)라고 한다. 따라서, 양단의 통신에 대한 관계는 다음과 같이 표현할 수 있다.

(DTE) ━━━━ 모뎀(DCE) 〓〓〓〓 (원격) 〓〓〓〓 모뎀(DCE) ━━━━ (DTE)

 우리가 말하는 시리얼통신의 정확한 표현은 DTE DCE사이의 통신을 말한다. (그림에서 굵은 선에 해당하는 부분) 따라서, 모뎀과 모뎀 사이의 통신선로는 관여치 않는다. 우리가 과거 모뎀을 통해서 PC통신을 했을 때에는 전화망을 이용했고, 요즘의 초고속통신은 ADSL이나 케이블방송 또는 전력선 등의 회선을 이용하기도 한다.

RS-232C DTE DCE사이의 연결을 위한 인터페이스 방식 중의 하나에 불과하다. PC에서는 자주 볼 수 없지만, RS-422, RS-485와 같은 시리얼통신 인터페이스도 존재한다. 다만, PC에 사용되는 시리얼통신 인터페이스가 대부분 RS-232C를 사용하는 것이고, 그 중에서도 DB9(9핀으로 구성된 커넥터) 인터페이스를 주로 사용하고 있을 뿐이다(참고로, RS-232C에는 DB25라는 25핀 커넥터도 있음). DTE에 해당하는 우리의 PC를 기준으로 봤을 때 모뎀, 마이컴 보드, 기타 하드웨어는 대개 DCE로 취급한다.

DTE 장비의 인터페이스는 수컷(Male) 커넥터 구성되어 있다(지금 여러분의 PC의 뒷면을 확인하기 바람). 그리고, DCE에 해당하는 장비는 대체로 암컷(Female) 인터페이스로 구성되어 있다. 간혹 수컷 인터페이스로 구성되어 있는 경우도 있기는 한데, 이 때문에 시리얼 인터페이스를 이용하여 디바이스를 연결할 때 혼동이 오는 경우가 있다. (이것은 극히 예외에 해당됨)

사용자 삽입 이미지

위의 그림은 DTE DCE 인터페이스의 핀 아웃을 설명한 것이다. DTE를 컴퓨터, DCE를 모뎀 또는 마이컴 보드로 생각하면 기억하기 쉽다.(, 위의 그림이 마이컴, 아래그림이 PC라고 생각할 것)

 DTE(컴퓨터)는 수컷커넥터가 원칙이며, DCE(모뎀, 마이컴보드)는 암컷커넥터가 원칙이다.

 DTE DCE 디바이스를 연결하는 경우를 생각해보자. PC에서 데이터를 3번핀을 이용하여 송신(Tx)하면, 마이컴은 3번핀으로 수신(Rx)할 것이다. 따라서, DTE DCE가 각각 수컷, 암컷 커넥터로 구성되어 있다면 그냥 커넥터끼리 연결하면 된다. 다만, 디바이스의 크기로 인해 마이컴을 PC에 직접 연결하지 못할 경우에는 확장케이블(Extention Cable)을 이용하여 연결할 수 있다. 확장케이블은 한쪽은 수컷, 다른 한쪽은 암컷 커넥터로 연결되어 있다.

또 다른 상황으로서, DTE DTE DCE들을 거치지 않고 직접 연결되어 있다고 생각해보자(, 모뎀 없이 PC PC를 연결한 경우). 이 경우 DTE는 서로 수컷 커넥터로 구성되어 있으므로 직접 연결할 수 없다. 게다가, 두 디바이스가 송신(Tx) 3번핀으로 동일하고 수신(Rx) 2번핀으로 동일하다. 데이터가 재대로 송수신이 되기 위해서는 두 디바이스를 연결하는 케이블이 교차되어 연결되어야 할 필요가 있다. 그래야, 송수신에 아무런 문제가 없게 된다.

 따라서, 이러한 경우에 사용되는 케이블을 크로스케이블(또는 널 모뎀)이라고 부른다. 널 모뎀이라 불리는 이유는 모뎀 없이 두 PC가 연결되어 있어서 겉보기에는 마치 모뎀이 중간에 있는 것처럼 보이기 때문이다. 널 모뎀 케이블은 양쪽이 모두 암컷 커넥터로 이루어져 있다. 간혹, 양쪽 모두 수컷 커넥터로 이루어진 경우도 있는데, 이럴 경우에는 젠더(Gender)라는 것을 이용하면 암수 핀변환이 가능하다.

사용자 삽입 이미지

사용자 삽입 이미지

 위쪽사진은 연장케이블과 널모뎀케이블의 실물의 모습이고, 아래쪽 사진이 바로 젠더라고 하는 것이다. 보다시피 한쪽은 암컷, 반대편은 수컷으로 되어 있어 핀 변환이 자유롭다.

 간혹 변칙적으로 DCE(마이컴보드) 측의 인터페이스가 수컷커넥터로 되어 있거나, Tx신호라인이 3번핀에 위치한 경우가 종종 있다. 본좌도 간혹 이렇게 변칙적으로 구성된 디바이스로 인해 많은 혼동을 일으키기도 했는데, 이 경우는 예외에 해당하므로 원칙을 정확히 알아두면 케이블이나 젠더를 바꾸어 줌으로써 문제점을 충분히 해결할 수 있다.

항상 마이컴 보드는 암컷커넥터에 2번핀이 송신(Tx)이라는 것을 원칙으로 정하고 보드를 설계하는 것이 편리할 것이다. 그렇지 않으면 다양한 종류의 케이블과 변환 젠더 등을 항시 실험실에 보유하고 있어야 하므로 매우 불편해지기 때문이다.



RS-232C 커넥터의 핀배치 정리

RS-232C DTE(예컨대, PC 또는 터미널) 기준으로 아웃을 정의하고 있다. 또한, DCE(예컨대, 모뎀 또는 주변기기) DTE 직선케이블(Straight Cable 또는 Extension Cable) 연결하도록 되어 있다. 따라서, DCE측의 커넥터 배치도 DTE 동일할 밖에 없다.

 그런데, 이러한 배치의 표기는 대다수의 사람들뿐만 아니라 엔지니어에게조차 혼동을 유발하곤 한다. 본인도 예외는 아니어서 수첩에 적어놓고 사용하기도 . -,.;;

 예를 들어, DB9 커넥터의 경우, DTE TxD 라인은 3 핀인데 DCE 3 핀도 TxD라고 부르게 되면 이름상으로는 라인이 서로 충돌을 일으키는 셈이다. 또한 RTS CTS 같은 이유로 인해 혼동이 생긴다.

 따라서, 커넥터의 배치는 디바이스 자신을 기준으로 명기하는 것이 혼동을 피할 있는 가장 쉬운 방법이 아닐까 생각한다. 그래서, 여기저기 자료를 찾아 다니며 정리한 끝에 RS-232C 인터페이스 커넥터의 배치에 대한 명확한 정리를 해봤다.

사용자 삽입 이미지

그림이 다소 지저분하고 안보이므로 DB9 커넥터만 다시 정리해보면 다음과 같다.

사용자 삽입 이미지

위의 그림은, 통신이 성공적으로 이루어져야 한다는 전제하에 DTE DCE 다이렉트로 연결할 경우 디바이스를 기준으로 배치를 나타낸 것이다. 따라서, 아까 설명했던 Tx, Rx  디바이스의 관점에서 위와 같이 표기하면, 엇갈려 표기되어 있으므로 혼동의 여지가 없다.

 그런데, 한가지 여기서 짚고 넘어갈 것은 RTS CTS 또한 엇갈려 표기되어 있다는 점이다.

 RTS CTS 데이터 송수신을 위한 통신채널의 흐름제어에 사용되는 신호들인데, 원래 RS-232C 표준에는 DB9커넥터의 7 핀의 경우 RTS 할당되어 있고, 8 핀의 경우 CTS 할당되어 있다. , DCE 경우에도 RTS CTS 위치가 바뀌어야 정상이다. 하지만, 표준에서 설명하는 것과 같이 RTS 오직 DTE DCE 전송하는 신호이고, CTS DTE DCE로부터 수신하는 신호이다.

 다시 말해서 DTE 관점에서 보면, RTS 송신으로만 사용되며 CTS 수신으로만 사용된다. 이와 반대로 DCE 입장이라면 뒤바뀌어야 것이다. 이것 또한 혼동을 유발하기 일쑤다. (개인적으로 RS-232C 커넥터를 저런 식으로 DTE 관점으로만 정의했는지 불만이다. 인터넷 도처에 있는 커넥터의 그림을 보면 각각이다. 그리고, 그것이 마치 표준인 것처럼 설명하고 있다. DTE측에서 그림인지, DCE측에서 그림인지조차 설명 안되어 있는 그림도 많고, 심지어는 PC측의 DB9 커넥터를 암컷으로 명기하고 있는 그림도 있다. 결국, RS-232C 배치는 핀들이 방향성을 가지고 있기 때문에 디바이스를 기준으로 상대적으로 해석해야 올바르지 않나 싶다.)

그러므로 디바이스의 관점에서 RTS Tx 같이 나가는 신호, CTS Rx 같이 들어오는 신호로 파악하는 것이 나중에 핸드쉐이킹 부분을 이해하는데 도움이 것이다.



TTL RS-232C의 관계

마이컴이나 디지털회로에 사용되는 전압(보통 TTL전압이라고도 부른다) 대개 +5V 내외로서 DTE에서 DCE로의 연결에는 적합하지 못하다. 따라서, RS-232C는 이를 증폭하여 보다 높은 전압의 상태에서 DTE DCE의 디바이스를 연결할 필요가 있다.

사용자 삽입 이미지

그렇다면, TTL 레벨의 디지털회로에서 어떻게 RS-232C레벨의 전압을 얻을 수 있을까? 통상 저 전압의 디바이스에서 고전압을 얻는 방법 중에 차지 펌프(Charge Pump)라는 방식이 있는데 이 방법을 이용하여 디지털보드는 RS-232C와 인터페이싱이 가능하다. 그 때 사용되는 부품이 바로 그 유명한 MAX232라는 칩이다.

데이터송수신에 사용되는 칩을 흔히 라인 드라이버라고도 하는데, MAX232 TTL레벨과 RS-232C레벨을 변환해주는 라인 드라이버의 한 예이다. 아래는 MAX232의 구성을 나타내는 그림이다.

사용자 삽입 이미지

의 구성도를 보면 차지펌핑을 위해 커패시터 4개가 사용되고 있음을 알 수 있다. MAX233이라는 칩은 차지펌핑에 사용되는 커패시터가 칩에 내장되어 있어 별도로 붙일 필요가 없다. 대신 가격은.... ,. 5천원정도.. ㅋㅋ MAX232 600~800원정도. ㅋㅋ 

한편 3.3V의 디지털제품에 사용되는 MAX3232라는 칩도 있으며 그 외에 다양한 파생품들이 있으니 홈페이지를 방문하여 참고하도록 하도록 하자.



RS-232C의 전기적 특성

RS-232C의 전기적 특성을 설명하기 전에 다시 한번 기억해야 할 점은 RS-232C DTE DCE사이의 인터페이싱하기 위한 전기적 특성을 표준화한 것이라는 점이다. 따라서, DCE DCE사이의 전기적 특성은 고려하지 않는다. (, 전화로 연결을 하건, 광케이블로 연결을 하건 상관없다는 것)

먼저, 새롭게 등장하는 용어에 대해서 정리하도록 하자.

1. 로직레벨(신호레벨) 

시리얼케이블의 각 핀이 가지는 논리적 의미를 말한다. 쉽게 말해 이진수 1 0을 생각하면 되겠다. , 데이터신호의 경우 로직레벨은 '1' '0'이며 제어신호의 로직레벨(신호레벨이라고도 함) 'On' 'Off'이다.

로직레벨 '1'의 경우를 마크(MARK), 로직레벨 '0'을 스페이스(SPACE)라고 부르기도 한다. , 마크는 음의 전압레벨 상태를 말하며, 스페이스는 양(+)의 전압레벨 상태를 말한다. 주로 데이터 신호의 로직레벨을 설명할 때 사용한다.  

2. 전압레벨 

로직레벨이 의미를 가질 수 있는 전압의 허용범위를 말하는데, 통상 양(+)의 전압과 음(-)의 전압의 범위를 가질 수 있다.

전압레벨을 말할 때는 두가지 주의해야 할 점이 있다.

첫째, 전압레벨이 송신단과 수신단에서 그 허용범위가 다르다는 점이다. 먼저 양(+)의 전압레벨의 경우 송신단에서는 +5~+15V 인데 반해 수신단에서는 +3~25V이다. (-)의 전압레벨의 경우 송신단에서는 -5~-15V인데 반해 수신단에서는 -3~-25V이다. 이처럼 송수신단의 전압레벨이 다르다는 점은 매우 중요한 사실이다.

둘때, 데이터신호의 경우 로직레벨과 전압레벨의 역의 관계에 있다는 점이다. , 데이터신호의 로직레벨 '1'(마크)은 음(-)의 전압레벨을 가진다. 반대로 로직레벨 '0'(스페이스)은 양(+)의 전압레벨을 가진다. 이와 반대로 제어신호는 로직레벨과 전압레벨이 일치한다. 로직레벨 'On'은 스페이스(+)의 전압레벨을 가지며 로직레벨 'Off'는 마크(-)의 전압레벨을 가진다.

아래의 그림은 로직레벨과 전압레벨에 대한 관계를 그림으로 나타낸 것이다.

사용자 삽입 이미지

위의 그림에서 전압레벨이 의미가 없는 곳(빗금친 영역)이 있다. 송신단에서는 -5~+5V의 영역이고, 수신단에서는 -3~+3V의 영역이다. 이 대역의 전압은 의미가 없게 되므로 주의할 필요가 있다. 다시 말해 어떠한 이유로 빗금 친 대역의 전압레벨을 보일 경우 데이터 오류의 원인이 되는 것이다.



시리얼통신에서 자동으로 보레이트 설정하기

마이컴보드를 사용할 때 정확한 보레이트값을 모르면 시리얼통신이 재대로 되지 않아 답답할 때가 있다. 게다가, 매번 호스트의 터미널에서 보레이트를 설정하는 게 귀찮기까지 할 때도 있다. 사용자가 어떠한 보레이트의 속도로 접속하더라도 마이컴이 이를 감지하여 자동으로 보레이트를 일치시켜 통신이 가능하도록 하는 방법이 있는데... 이를 Automatic Baudrate Detection 기법이라고 한다.

 ABD 기법은 uC의 종류에 따라 다양한 방법으로 구현이 가능한데, 그 중 한 방법을 소개하려고 한다. 인식되는 보레이트는 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200bps이며 호스트의 터미널 프로그램 사양에 따라 다른 보레이트 또한 얼마든지 지원할 수 있다. (또한, 이 원리를 조금만 이용하면 ABD 기능을 터미널 프로그램에도 적용할 수 있다.)

ABD는 마이컴이 ON(또는 리셋)되고 나서 사용자가 특정문자를 맨 처음 보내면서 시작된다. 간단하게 엔터키를 사용자가 입력했다고 가정하고 ABD기능을 구현해보자.

우선, 마이컴을 부팅할 때 보드가 지원할 수 있는 최대 baudrate 속도로 부팅시키자. 통상 115200bps을 최대속도로 사용하므로 이 값으로 부팅되었다면... 호스트에서 입력한 엔터키는 호스트측의 보레이트에 따라 다음과 같은 값으로 수신될 것이다.

사용자 삽입 이미지

 엔터키의 아스키코드는 0x0D이며 시리얼통신에서는 LSB(최하위비트)부터 수신된다. 비동기 통신이므로 시작비트와 정지비트가 데이터비트를 감싸는 형태로 수신될 것이다. 시리얼포트의 수신핀은 맨처음 HIGH상태를 유지하면서 데이터가 들어오기를 기다린다. 이윽고, 시작비트(LOW)가 들어오면서 수신이 시작되는데 115200bps의 경우 1비트의 시간은 1/115200 초이므로 이 시간 간격으로 차례로 D0, D1, ... , D7 를 수신해서 데이터(바이트)값을 검사한다.


이 값이 0x0D라면 호스트는 115200bps로 설정되었음을 알 수 있다. 따라서, 마이컴은 115200bps로 포트를 초기화하면 된다.

만약, 115200bps보다 느린 속도로 접속했다면... 패킷의 수신 시간은 보레이트에 반비례하므로 위의 그림과 같은 형태로 수신될 것이다. 따라서, 이 값을 위의 57600, 38400bps등에서의 값과 비교해서 일치하는 보레이트로 초기화시켜주면 된다.


그런데, 9600bps이하의 저속의 보레이트로 접속했다면... 수신된 데이터는 어느 경우라도 0x00이므로 문제가 생긴다. 따라서, 일치하는 데이터가 없을 경우 마이컴을 9600bps로 재설정하여 호스트로부터 엔터키를 한번 더 입력받는다.

사용자 삽입 이미지

 호스트가 9600bps로 접속한 경우라면 2번째로 입력받은 엔터키는 0x0D로 수신될 것이며 그 이하의 속도로 접속했다면 위와 같은 형태의 값으로 수신하였을 것이다. 위의 값도 아닌 경우라면... 에러가 발생한 경우이거나... 지원되지 않는 속도로 접속한 경우이므로 예외처리를 하면 된다.

 이상의 알고리즘을 순서도로 구현하면 다음과 같다.

사용자 삽입 이미지

위의 방법은 uC의 종류에 구애받지 않고 구현할 수 있는 알고리즘으로 1 ~ 2번의 엔터키 입력으로 어떠한 속도의 보레이트라도 검출이 가능하다. 관심 있는 분들은 한번 도전해보시길...


< ABD 구현을 위한 >


1. 엔터키를 입력 받는 루틴(단계1 : 115200bps, 단계2 : 9600bps)은 날카로운 독자 분들이라면 통상의 시리얼 통신으로는 구현할 수 없다는 걸 짐작했을 것이다. 9600bps 2400bps의 경우 정지비트가 HIGH가 아니기 때문에 엔터키가 정상적으로 수신될 수 없기 때문이다. 따라서, ABD 루틴은 RxD핀을 시리얼포트가 아닌 일반 I/O포트로 가정하고 타이머나 다른 방법을 이용하여 bit-by-bit로 수신해야 한다. 다시 말해 일정시간 간격(1/115200 또는 1/9600초의 시간간격)으로 RxD핀의 I/O 레벨을 검출해야 한다는 것!


2. 해당 비트의 수신은 각 비트의 주기에서 중앙값에서 측정하는 것이 정확성이 높다. 따라서, 에지(edge)단에서 검출하지 말도록 하자.


3. 8개의 비트를 수신하여 1바이트 데이터를 만든 다음, 어느 정도 딜레이를 주어야 한다. 왜냐하면, 저속으로 접속한 경우... 마이컴은 1바이트를 검출하였더라도.. 호스트 쪽에서는 여전히 데이터를 보내고 있기 때문이다.

블로그 이미지


불만있으면 떠나라...

티스토리 툴바