본문 바로가기

통신 & 필터

[ UART ] 시리얼 통신 , 패킷

UART통신

UART 통신이란 Serial 통신으로, 데이터 전송 혹은 수신 핀이 하나인 통신이다. 한번에 1byte씩 보내며, 각 bit는 차례(직렬)로 전송된다. MCU (ex. arduino)에서 많이 쓰이는 방식이며, TX (데이터를 보내는 핀)와 RX (데이터를 받는 핀)이 존재한다.

회로적으로 보았을 때, 각 bit의 0과 1은 MCU에선 GND와 VCC로 생각 할수 있으며, 전달받은 신호를 해석하면 다시 bit에서 byte로 전환이 가능하다.

데이터를 주고 받는 것에 있어서 보내는 쪽과 받는 쪽의 데이터 전송 속도가 동일 해야한다. 이러한 통신 속도를 baud rate라고 하며, baud rate에는 115200 , 57600 , 9600 등 다양하게 존재한다.

UART는 Start bit (GND , 0)으로 시작하고, 그 뒤 8개의 Data bit , 그리고 Stop bit (VCC , 1)로 끝난다. 따라서 한번에 10개의 bit를 전송하고 받는게 일반적이다.

UART 통신 구조

Packet (패킷)

UART 통신으로는 한번에 8bit 즉, 64 bit 운영 체제기준 1byte 만 전송 된다. 하지만 실제 데이터를 보낼 때에는 1byte이상의 데이터를 전송해야 한다. 이런 다수의 데이터를 전송 할 때, 보내고 받는 데이터의 형식을 명시해서 주고 받는 것이다. 간단한 패킷의 구조는 아래와 같다.

간단한 packet 구조 

처음 2byte를 데이터가 시작 되었다고 알리는 Header로 고정한다. 그후 앞으로 Data + Check sum 의 수를 알려주는 1 byte를 보낸 뒤 Data를 보내고 Check sum을 보낸다. 여기서 Header란 약속된 0~255 사이의 숫자이지만 Check sum은 앞의 데이터가 중간에 깨지지 않고 잘 갔는지 확인하기 위한 byte이다.

Check sum을 만드는 법은 간단하다. 앞의 데이터들을 가지고 만들 규칙을 정하는거다. 예를 들어 앞의 데이터들을 모드 XOR한다거나, 모두 더한다거나 하는 약속을 가지고 만들면 된다. 그러면 보내는 측에서는 보낸 데이터를 기반으로 Check sum을 만들어서 보내고 받는 측에선 받은 데이터들을 조합해서 Check sum을 만든후 받은 Check sum과 비교해서 데이터가 정상적으로 잘 들어왔는지 확인하면 된다.

UART 통신 예제

UART 통신 , Serial 통신하면 가장 많이 떠오르는게 아두이노이다. 아두이노에는 TX / RX 핀이 UNO에는 1개, Mega에는 3개가 존재하며 SoftwareSerial.h를 통해 만들어 줄 수도 있다. 여기서 아두이노의 0번 TX / RX 핀은 USB - B type으로 연결된 기기와 통신(업로드 , 시리얼 모니터 , 시리얼 플로터 등)을 하는데 사용된다. 

컴퓨터에서 아두이노 IDE를 사용하지 않고 아두이노 TX / RX 0을 통해 통신하는 법은 간단하다. 

다음 github를 참고한다.

https://github.com/ladianchad/communication

Arduino가 linux환경의 컴퓨터와 연결 되면 /dev/ttyACM* 과 같은 파일이 생성된다. 기본적으로 OS는 통신을 하던 뭘하던간에 File Descriptor로 설명이 되기 때문에 아두이노와 통신을 하기 위해선 이 파일을 통해야 한다.  따라서 이 파일에 대한 접근권한이 필요한데 /dev에 들어있는 파일같은 경우는 관리자 권한이기 때문에 다음과 같이 사용자 권한으로 풀어준다. 

sudo chmod 777 /dev/ttyACM0

그럼 이제 통신을 위한 코드를 다음과 같이 작성해 보자.

Arduino

int num;

void setup(){
	Serial.begin(9600);
}

void loop(){
	if(Serial.available()){
    	num = Serial.parseInt();
        Serial.print(num*2);
    }
}

linux

#include "comunnication/Serial.cpp"
#include <iostream>
using namespace std;
int main(int argc,char **argv){
	Serial my_serial("/dev/ttyACM0",9600);    //port & baud rate
    char buf ='\0';
    while(buf !='q'){
    	cout<<"숫자를 입력해 주세요(끝내려면 q) : ";
        cin>>buf;
        if(my_serial.write(&buf))
        	cout<<"I send data"<<endl;
        else
        	cout<<"Send error"<<endl;
        if(my_serial.read(&buf,sizeof(char)))
        	cout<<"I recived : "<<(int)buf<<endl;
        else
        	cout<<"Recieve Error"<<endl;
    }
    return 0;
}

현재는 1byte의 데이터만 주고 받는 것이므로 256이상의 데이터를 주고 받는건 불가능 하다. 패킷을 사용한 예시는 다음과 같다.

Arduino

#define HEADER 0xFF
#define DATA_LEN 6
int num = 0;
char send_buf[10] {};
char receive_buf[10] {};
send_buf[0] = HEADER;
send_buf[1] = HEADER;
send_buf[2] = DATA_LEN;
void setup(){
	Serial.begin(9600);
}

void loop(){
	num++;
    send_packet();
}

void send_packet(){
	send_buf[3] = (num*2 >> 8) & 0x00FF;  // int upper bit
    send_buf[4] = (num*2) & 0x00FF;		  // int lower bit
    for(int i=0;i<DATA_LEN-1;i++)
    	send_buf[5] ^= send_buf[i];
    Serial.write(send_buf,DATA_LEN);
}

linux

#include "Serial.cpp"
#include <iostream>
#define HEADER 0xFF

using namespace std;
int main(int argc,char **argv){
	Serial my_serial("/dev/ttyACM0",9600);    //port & baud rate
    char buf[10];
    bool header = false;
    char *buf_pointer = buf;
    int buf_index = 0;
    while(true){
    	if(my_serial.sread(buf_pointer,sizeof(char)){
            if(!header){					//헤더 확인
                if(buf_index == 0){
                    if(*buf_pointer == HEADER){
                        buf_index++;
                        buf_pointer++;
                    }
                }
                else if(buf_index == 1){
                    if(*buf_pointer == HEADER){
                        buf_index++;
                        buf_pointer++;
                        header = true;
                    }
                    else{
                        buf_pointer = buf;
                        buf_index = 0;
                    }
                }
            }
            else{
                 buf_index++;
                 buf_pointer++;
                 if(buf_index == buf[3]){
                     char check_sum;
                     for(int i=0;i<buf_index-1;i++)			//Check sum 확인
                         check_sum ^=buf[i];
                     if(check_sum == buf[buf_index-1]){		//데이터 재조립
                         int Data = buf[4]<<8 + buf[5];
                         cout<<"Data : "<<Data<<endl;
                     }
                     else{
                     	cout<<"Data loss!!"<<endl;
                     }
                 }
            }
        }
    }
    return 0;
}

위와 같이 아두이노와 TX / RX 통신 하는 법을 하였는데 여러 센서들을 사용하다 보면 센서 Data 출력이 UART통신으로 나오는 경우가 있다. 이때, 컴퓨터와 바로 연결해서 통신 값을 받아 보기위해서는 USB to TTL레벨 컨버터가 필요한데 아래와 같이 생겼다.

USB to TTL 컨버터

이를 사용해서 연결하면 위의 linux코드로 센서와 통신이 가능하다.

 

포스팅 끝

 

틀린거나 궁금한거 댓글 달아주세요

'통신 & 필터' 카테고리의 다른 글

[Filter] Mean, Low Pass , High Pass filter  (0) 2021.03.29