아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

라즈베리파이 시리얼 통신(RaspberryPi Uart Communication)

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin
황제곰2020. 7. 23. 13:43

안녕하세요. 베어팹의 황제곰 입니다. 이번포스팅에서는 라즈베리파이를 사용해서 시리얼 통신을 하는 방법에 대해서 알아보도록 하겠습니다.

【라즈베리파이의 UART 핀】

라즈베리파이의 핀 맵을 보시면 아래 사진와 같이 물리적 핀 8, 10(BCM 14, 15)번이 각각 시리얼 통신을 위한 TX핀, Rx핀이라는 것을 확인 할 수 있습니다.

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

하지만 아래 명령어를 통해 라즈베리파이에서 사용가능한 핀 상태를 확인해 보면 이 핀을 통해 두 종류의 UART를 사용할 수 있다는 것을 알 수 있습니다.(혹시 명령어가 안 먹으면"sudo apt install raspi-gpio" 로 해당 패키지를 설치해 주세요.)

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

TX, RX 핀

이중 TXD0, RXD0은 하드웨어적인 UART(PL011)이고 TX1, RX1는 소프트웨어 적으로 구현된 miniUART라고 합니다. 하드웨어적인 UART와 달리 소프트웨어적으로 구현된 miniUART는 패리피 비트 설정이 안되고 CPU 클럭에 따라 통신 속도의 영향을 받는 등 기능적인 제한이 있다고 합니다.

[각 버전의 UART]

라즈베리파이 3버전이 되면서 라즈베리파이에 블루투스 기능이 추가되었는데요. 이에 따라 블루투스 기능을 가진 라즈베리파이 3, 4, zeroW 버전은 하드웨어적인 UART0(PL011)을 블루투스 통신용으로 할당해 놓았습니다. 이 말은 별도의 설정이 없이는 이 하드웨어적인 UART0를 사용할 수 없다는 의미입니다.(블루통신 기능을 사용하지 않도록 설정하면 시리얼통신으로 사용하는 것이 가능하긴 합니다.) 따라서 라즈베리파이 4, 3, zero w는 miniUART(UART1)를 통해서 시리얼 통신을 해야 했습니다. 하지만 miniUART는 단점이 있는데요. 제대로 동작하게 하려면 라즈베리파이의 gpu클럭을 고정주파수로 설정해야 합니다.(miniUART는 소프트웨어적으로 만들어졌기 때문에 VPU의 코어주파수에 다라 UART의 통신속도가 달라질 수 있기 때문이라고 합니다.)

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

라즈베리파이 버전별 UART 할당

위에서 말한 하드웨어적인 UART와 mini UART는 각각 라즈베리파이의 /dev/ttyAMA0, ttyS0와 매핑 되어 있는데요. 아래 명령어를 통해 확인해 보면 매핑된 시리얼 디바이스들을 확인 할 수 있습니다.

[라즈베리파이4의 UART]

라즈베리파이4는 기존의 하드웨어적인 UART0와 miniUART(UART1)외에 시리얼 통신 장치를 4개 추가 했습니다. 따라서 굳이 mini UART를 통해 불편하게 여러가지 세팅 후 시리얼 통신을 할 필요가 없어 졌습니다.

라즈베리파이4에서는 아래와 같이 총 6개의 UART가 있고 위에서 말한 하드웨어적인 UART0와 miniUART인 UART1 말고 UART2-5까지의 UART를 사용하면 되기 때문입니다.

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

라즈베리파이4의 UART

[라즈베리파이 4에서 시리얼 통신 설정하기]

라즈베리파이 4에서 UART 2-5를 추가 할당해서 사용하고자 하면 dt-overlay를 수정해 주어야 하는데요. UART관련 dt-overlay는 아래 명령어를 통해 확인해 볼 수 있습니다.

pi@raspberrypi:~ $ dtoverlay -a | grep uart midi-uart0 midi-uart1 miniuart-bt uart0 uart1 uart2 uart3 uart4 uart5

이 중 uart3이 사용하는 핀에 대한 정보는 아래 명령어를 입력하면 확인 할 수 있습니다.

pi@raspberrypi:~ $ dtoverlay -h uart3

Name: uart3 Info: Enable uart 3 on GPIOs 4-7 Usage: dtoverlay=uart3,<param> Params: ctsrts Enable CTS/RTS on GPIOs 6-7 (default off)

위 나온 결과에서 uart3은 4, 5번 핀은 Tx, Rx핀으로 사용되고 ctsrts파라미터를 추가하면 6, 7번 핀이 CTS 및 RTS핀으로 사용된다는 것을 알 수 있습니다.

그럼 uart3번을 사용하도록 설정해 보겠습니다. 좋아하는 에디터를 사용해서 /boot/config.txt파일을 열어 줍니다.

sudo nano /boot/config.txt

파일의 마지막 줄에 아래 사진과 같이 "dtoverlay=uart3"을 추가해 줍니다.

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

파일을 저장하고 아래 명령어를 통해 재부팅해 줍니다.

재부팅이 완료되면 라즈베리파이의 4, 5번 핀이 uart3을 위한 핀으로 설정되었는지 확인해보기 위해 아래와 같이 입력해 봅니다.

pi@raspberrypi:~ $ raspi-gpio get 4-5 GPIO 4: level=1 fsel=3 alt=4 func=TXD3 pull=NONE GPIO 5: level=1 fsel=3 alt=4 func=RXD3 pull=UP

TXD3, RXD3으로 설정되어 있는것을 확인 할 수 있습니다. 가상 디바이스확인을 위해 아래와 같이 입력해 보면

pi@raspberrypi:~ $ ls -l /dev/ttyAMA* crw-rw---- 1 root dialout 204, 64 Jul 23 07:27 /dev/ttyAMA0 crw-rw---- 1 root dialout 204, 65 Jul 23 07:27 /dev/ttyAMA1

/dev/ttyAMA1 이 새로 생성되는데요. 이 장치를 통해 UART3을 사용할 수 있습니다.

위 방법과 마찬가지로 만약 uart4를 사용하고자 하면 /boot/config.txt 파일에 "dtoverlay=uart4" 를 추가하면 됩니다.

[라즈베리파이 3, zero W에서 시리얼 통신 설정하기]

라즈베리파이3, zeroW는 UART0를 블루투스 통신에 사용하기 때문에 miniUART를 사용해야 합니다. 기본적으로 miniUART는 비활성화 되어 있는데요. 이를 활성화하기 위해서 raspi-config 메뉴를 사용할 수 있습니다. 아래 명령어를 입력해서 우선 raspi-config 메뉴로 들어가 줍니다.

아래 사진과 같이 5. Interface Option => P6 Serial 메뉴를 선택해서 로그인쉘을 시리얼을 통해 접근할 수 있게 하겠냐는 질문에는 No, 시리얼포트 하드웨어를 노출하겠다는 질문에는 Yes를 선택해 줍니다.(MiniUart를 터미널서비스에서 선점하면 우리가 사용할 수 없기 때문에 첫번째 질문에는 No를 하는 것입니다.)

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

이제 다시 터미널에서 아래와 같이 14, 15번 핀의 할당상태를 확인해 보면 miniUART인 TXD1, TXD2가 할당되어 있는 것을 확인 할 수 있습니다.

pi@raspberrypi:~ $ raspi-gpio get 14-15 GPIO 14: level=1 fsel=2 alt=5 func=TXD1 GPIO 15: level=1 fsel=2 alt=5 func=RXD1

아래 명령어로 매핑된 디바이스목록을 확인해 보면 /dev/ttyS0가 새로 매핑된 것을 확인 할 수 있는데요. /dev/ttyS0가 이 miniUART의 경로가 됩니다.

pi@raspberrypi:~ $ ls /dev/ttyS* /dev/ttyS0

그리고 이 miniUART는 부하가 많이 작업을 할 때 다양한 영향으로 주파수가 달라져 통신이 불안정 해 질 수 있기 때문에 VPU 코어의 주파수를 고정된 상태로 유지해 주면 통신의 안정성을 높일 수 있다고 합니다. 이를 위해서는 /boot/config.txt파일에 아래 명령어를 추가해 주면 됩니다.

force_turbo=1 arm_freq=1200 core_freq=250

[라즈베리파이 4, 3, zero W에서 UART0을 사용하기]

하드웨어 UART인 UART0는 블루투스 통신을 위해 할당되어 있다고 위에서 이야기 했는데요. UART0에 할당된 이 블루투스 기능을 비 활성화고 UART0를 시리얼 통신에 사용할 수도 있습니다.이를 위해서는 /boot/config.txt파일을 열고 맨 아래 아래와 같이 내용을 추가해 줍니다.

그 다음 라즈베리파이를 재부팅 시켜 줍니다. 14, 15번 핀이 할당된 기능을 아래 명령어로 확인해 보면 하드웨어적인 UART0에 할당되어 있는 것을 알 수 있습니다.(참고로 이 때 miniUART에 블루투스 기능이 할당된다고 합니다.)

pi@raspberrypi:~ $ raspi-gpio get 14-15 GPIO 14: level=1 fsel=4 alt=0 func=TXD0 GPIO 15: level=1 fsel=4 alt=0 func=RXD0

맵핑된 디바이스는 /dev/ttyAMA0를 사용하면 됩니다.

라즈베리파이와 아두이노를 시리얼 통신으로 연결하고 라즈베리파이에서 입력한 각도대로 아두이노의 서보모터를 제어해 보는 실습을 해 보겠습니다.

【회로 연결】

우선 위에서 언급했던 방법에 따라 UART 통신채널을 하나 할당을 해 줍니다. 저는 라즈베리파이 버전4를 사용하고 있기 때문에 UART3을 사용했습니다. UART3은 BCM4번 핀이 Tx, 5번핀이 Rx3으로 사용 됩니다.

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

UART3의 핀넘버

따라서 아래 사진과 같이 라즈베리파이의 Tx3은 아두이노 시리얼 통신용 핀의 Rx로 라즈베리파이의 Rx3은 아두이노의 시리얼 통신용 핀의 Tx로 연결하면 됩니다. 그리고 라즈베리파이에서 전달받은 값에 따라 서보모터를 돌려주기 위해 아두이노의 8번에 서보모터를 하나 연결해 주었습니다.

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin

주의: 라즈베리파이의 신호레벨은 3V이기 때문에 5V신호를 사용하는 아두이노와 연결시 보드가 망가질 수 있습니다. 반드시 3V레벨을 사용하는 아두이노와 연결해야 하며 5V신호를 사용하는 아두이노를 사용하려면 신호레벨 변환기 등을 통해 서로 신호레벨을 맞춰 줘야 합니다.

【코드】

라즈베리파이 쪽에서는 파이썬 언어를 통해서 코드를 작성했고 시리얼 통신을 위해 serial이라는 모듈을 사용했습니다. 먼저 아래 명령어로 serial 모듈을 설치해 줍니다.

그다음 파일을 하나 열어서 아래와 같이 코드를 입력하고 원하는 이름으로 저장합니다.

import time // 타임모듈 불러오기 import serial //pyserial 모듈 불러오기 ser = serial.Serial( // serial 객체 생성 port='/dev/ttyAMA1', // 시리얼통신에 사용할 포트 baudrate=115200, // 통신속도 지정 parity=serial.PARITY_NONE, // 패리티 비트 설정방식 stopbits=serial.STOPBITS_ONE, // 스톱비트 지정 bytesize=serial.EIGHTBITS, // 데이터 비트수 지정 timeout=1 // 타임아웃 설정 ) while True: // 반복해서 숫자값을 입력받음, 이때 숫자가 아닌 값을 입력하면 다시 입력을 요구함 degree = input('what degree do you want for servo?\n') if degree.isdigit(): ser.write(degree.encode()) else: print("you have to pass a number")

아두이노소스 코드는 아래와 같이 작성후에 보드에 업로드 해 주었습니다. 제가 사용한 레드보드는 기본제공되는 시리얼 포트가 2개(Serial, Serial1) 여서 바로 사용했지만 아두이노 우노 보드와 같이 시리얼포트가 1개인 경우는 SoftwareSerial 라이브러리등을 통해 별도의 시리얼 통신용 포트를 생성해야 할 수 있습니다.

#include <Servo.h> //서보라이브러리 불러오기 Servo myServo; // 서보객체 생성 int servo_angle; // 서보모터의 각도값을 저장할 변수 void setup() { Serial.begin(115200); //serial포트의 시리얼통신속도 지정, 데스크탑 컴퓨터와 시리얼 통신용 Serial1.begin(115200); // Serial1포트의 시리얼통신속도 지정, 라즈베리파이와의 통신용 myServo.attach(8); //digital핀 8번에 서보 제어용 핀 할당 myServo.write(0); // 서보모터 0도로 초기화 } void loop() { if(Serial1.available()>0){ // serial1 포트에 들어온 데이터가 있으면 while(Serial1.available()>0){ servo_angle = Serial1.parseInt(); // 값을 읽어들여 숫자로 변환한 후 변수에 할당 } myServo.write(servo_angle); // 읽어들인 값대로 서보모터를 돌려줌 Serial.print("set to "); // serial 포트로 메시지를 씀(컴퓨터에서 확인용) Serial.println(servo_angle); // serial 포트로 각도값을 씀(컴퓨터에서 확인용) delay(5); } delay(100); }

pyserial에 대한 자세한 함수설명은 pyserial 공식문서를 참고해 주시면 됩니다. 위에서 serial객체를 생성할 때 사용한 주요 매개변수에 대한 설명은 아래와 같습니다.

<Serial 객체 생성시 사용되는 매개변수>

매개변수

설명

port

라즈베리파이에서 사용하는 시리얼포트의 경로를 지정해 줄 때 사용됩니다. 어떤 UART를 사용할지에 따라 지정해주는 포트가 달라집니다.

baudrate

이 값은 통신속도를 지정해 줄 때 사용됩니다. 아두이노에서 지정한 통신속도와 같아야 합니다.

parity

패리티 비트를 통해 통신의 오류 검사를 할 것인지 지정할 때 사용됩니다. 아두이노는 따로 설정하지 않으면 패리티 검사를 하지 않는 것이 기본입니다. 아두이노에서 지정한 패리티 검사 방식과 일치해야 합니다. 아두이노의 패리티 검사를 지정하고 싶으면 여기를 참고해서 원하는 값을 지정하면 됩니다.

stopbits

전송된 데이터의 끝을 알리기 위한 신호를 어떻게 지정할 것인지 결정할 때 사용됩니다. 아두이노는 기본으로 1bit의 스톱비트를 사용합니다. 이 값을 변경하고 싶으면 여기를 참고해서 변경하면 되고 라즈베리파이(파이썬)과 아두이노간의 스톱비트 지정방식이 동일해야 합니다.

bytesize

데이터 비트의 총 숫자를 지정할 때 사용되며 일반적으로 8비트를 많이 씁니다

timeout

시리얼 명령어가 시리얼 통신이 종료될 때까지 기다리는 시간으로 여기서 지정한 시간이 지나면 통신이 종료되었다고 인식합니다.

【실행결과】

이제 라즈베리파이에 파이썬코드를 실행하고 각도를 입력하면 아래 영상과 같이 아두이노에 연결된 서보모터의 각도를 제어할 수 있습니다. 그리고 전달한 각도값은 아두이노의 serial포트와 연결된 시리얼창을 통해 확인이 가능합니다.

아두이노 라즈베리파이 시리얼 통신 - adu-ino lajeubelipai silieol tongsin