개발자/라즈베리파이4

RPi Python 프로그래밍 19: RPi의 TTL 포트에서 직렬 UART 통신

지구빵집 2021. 11. 30. 10:25
반응형

 

 

RPi Python 프로그래밍 19: RPi의 TTL 포트에서 직렬 UART 통신 

 

이전 포스팅에서 직렬 USB 보드 및 다른 직렬 어댑터를 포함해, 유니버설 비동기 송수신(UART) 프로토콜 및 전압 레벨에 대해 논의했습니다. 또한 Linux, Windows, macOS 및 Raspberry Pi(RPi)에서 직렬 포트 이름을 찾는 방법도 배웠습니다. 

 

이 자습서에서는 Python 직렬 라이브러리를 사용하여 컴퓨터와 양방향 통신을 설정하는 방법을 배웁니다.

 

RPi에서 UART 구성하기

 

Raspberry Pi의 직렬 통신 튜토리얼에서 논의한 바와 같이, UART 통신을 위해 RPi의 직렬 TTL 포트를 사용하려면 Raspberry Pi OS의 기본 직렬 Linux 콘솔을 비활성화해야 합니다.

 

RPi의 직렬 Linux 콘솔을 비활성화하려면 다음 단계를 따르십시오.

 

1. Raspberry Pi에서 터미널 창(Bash 셸)을 열고 다음 명령을 실행합니다. 만약에 x-window로 부팅한 상태라면 아래 그림의 LXTerminal 창을 열고

터미널에서 아래 명령을 실행하세요.

 

$sudo raspi-config

 

2. "인터페이싱 옵션"으로 이동합니다.

 

 

3. 다음으로 "직렬" 옵션을 선택합니다.

 

 

4. 구성 창이 직렬을 통해 로그인 셸에 액세스할 수 있는지 묻습니다. "아니오"를 선택합니다.

 

 

5. 그런 다음 구성 창에서 직렬 포트 하드웨어를 활성화할지 묻습니다. "예"를 선택하십시오.

 

 

6. 이제 Raspberry Pi에서 직렬 통신을 위해 직렬 TTL 포트(GPIO14 및 GPIO15)가 활성화됩니다.

 

 

7. 설정을 마치고 메인 화면으로 돌아와 빠져나오면 재부팅을 하라고 나온다. 변경 사항을 적용하려면 Raspberry Pi를 재부팅하십시오.

 

 

RPi를 재부팅한 후 직렬 TTL 포트를 UART 프로토콜을 통한 직렬 통신에 사용할 수 있습니다. 포트는 /dev/serial0이라는 이름으로 주소를 지정할 수 있습니다.

 

또는 PL011 또는 mini-UART가 기본 UART인지 여부에 따라 /dev/ttyS0 또는 /dev/ttyAMA0을 포트 이름으로 사용할 수 있습니다( 각 Raspberry Pi 모델에서 ). /boot/config.txt 파일을 검사하여 어떤 UART 유형이 기본 유형인지 알 수 있습니다.

 

참고: 라즈베리파이4 모든 UART 활성화하기 

 

/boot/config.txt 파일에서 enable_uart 플래그가 기본적으로 0으로 설정되어 있으면 mini-UART가 해당 RPi 모델의 기본 UART입니다. 그러나 enable_uart 플래그가 기본적으로 1로 설정되어 있으면 PL011이 해당 RPi 모델의 기본 UART입니다.

 

USB 직렬 보드 선택

 

여러 USB 직렬 보드를 사용할 수 있습니다. FTDI는 가장 오래되고 가장 일반적인 것입니다. Raspberry Pi와 함께 사용하는 것은 3.3V UART 전압을 가져야 합니다. 5V USB 직렬 보드를 사용하는 경우 RPi의 TTL 포트 채널이 영구적으로 손상될 수 있습니다.

 

Converter USB-UART FTDI FT232RL miniUSB + USB wire

 

일부 USB 직렬 보드는 5~3.3V UART 전압으로 설정할 수 있는 점퍼를 제공합니다. Raspberry Pi에 연결하기 전에 3.3V 옵션을 선택해야 합니다.

 

USB 직렬 보드에는 Raspberry Pi에서 직접 공급 전압을 수신하는 옵션도 있습니다. 이를 위해서는 RPi의 3.3V 핀을 보드의 VCC 단자에 연결해야 합니다.

 

다음을 확인하는 것이 중요합니다.

 

  • RPi의 Rx(GPIO15)는 USB 직렬 보드의 Rx에 연결됩니다.
  • RPi의 Tx(GPIO14)는 USB 직렬 보드의 Tx에 연결됩니다.
  • USB 직렬 보드의 접지 핀은 RPI의 접지에 연결됩니다(UART 전압의 기준 접지이기 때문에).
  • USB 직렬 보드의 USB 포트는 USB-to-USB 케이블을 통해 컴퓨터(데스크탑)의 USB 포트에 연결됩니다.

 

직렬 TTL 포트가 동일한 3.3V UART 전압 레벨로 다른 장치의 직렬 TTL 포트와 직접 연결되어야 하는 경우:

 

  • RPi의 Rx(GPIO15)는 다른 장치의 Tx에 연결됩니다.
  • RPi의 Tx(GPIO14)는 다른 장치의 Rx에 연결됩니다.

 

직렬에 대한 하드웨어 권한

 

RPi의 직렬 TTL 포트를 사용하려면 Raspberry Pi 사용자가 다이얼아웃 그룹의 구성원이어야 합니다. 기본 사용자는 pi이지만 Raspberry Pi 시스템 에서 생성된 다른 사용자가 있을 수 있습니다 .

 

현재 사용자가 다이얼아웃 그룹의 구성원이 아닌 경우 TTL 직렬 포트에 액세스하려고 하면 다음 경고가 표시됩니다.

 

serial.serialutil.SerialException: [Errno 13]이 /dev/ttyS0 포트를 열 수 없습니다: [Errno 13] 권한이 거부되었습니다: '/dev/ttyS0'

 

기본 사용자(pi)가 아닌 경우 터미널 창(Bash Shell)에서 다음 명령을 입력하여 전화 걸기 그룹에 자신을 추가할 수 있습니다.

 

sudo adduser your_username dialout

 

명령을 성공적으로 실행한 후 변경 사항을 적용하려면 Raspberry Pi를 재부팅하십시오.

 

Python 직렬 라이브러리

 

Python 스크립트가 Raspberry Pi의 직렬 포트를 사용하려면 인터페이스에 라이브러리가 필요합니다. PySerial은 Linux, BSD, OSX, Win32, 심지어 Jython 및 IronPython 용 Python 직렬 포트 확장으로 사용되는 라이브러리입니다 .

 

GitHub 의 무료 소프트웨어 라이선스 에 따라 사용할 수 있습니다 . 모듈은 다양한 인터페이스(예: TTL 포트, RS232, RS485 및 RFC 2217) 및 다양한 URL 핸들러를 통한 액세스를 포함하여 직렬 포트에 대한 액세스를 캡슐화합니다.

 

이 라이브러리는 모든 주요 플랫폼을 지원하는 클래스 기반 인터페이스입니다. 순수 Python 라이브러리이며 Python의 io 라이브러리와 호환됩니다.

 

PySerial 설치

 

기본적으로 PySerial은 RPi에 설치됩니다. 제거된 경우 터미널에서 다음 명령을 실행하여 설치할 수 있습니다.

 

$python3 -m pip install pyserial

 

또는

 

$easy_install -U pyserial

 

단, pip가 설치되어 있지 않다면 먼저 아래 명령어를 입력하여 설치합니다. 그런 다음 PySerial을 설치합니다.

 

$sudo apt install python3-pip

 

PySerial API 라이브러리에는 직렬(UART) 포트에 액세스하는 데 사용되는 직렬 클래스가 포함되어 있습니다. 클래스는 다음과 같은 읽기 전용 속성을 지원합니다.

 

Attribute Type Description
name str Device name.
cts bool Get the state of the CTS line
dsr bool Get the state of the DSR line 
ri bool Get the state of the RI line 
cd bool Get the state of the CD line 
is_open bool Get the state of the serial port, whether it’s open 

 

 

클래스는 다음과 같은 구성 가능한 속성을 지원합니다. 참고 문서로 이동.

클래스는 다음 상수를 지원합니다. 참고 문서로 이동

 

직렬 클래스는 다음 메서드를 지원합니다.

 

serial.Serial() — 이 메서드는 직렬 포트를 가리켜야 하는 Python 스크립트에 직렬 객체를 만듭니다. 동시에 생성자이며 다음 매개변수의 구성을 지원합니다.

 

1. 포트 – 장치 이름 또는 없음./p>

2. baudrate – 초당 비트(bps) 단위의 전송 속도입니다. 표준은 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 19200, 38400, 515020과 같은 표준 값 중 하나일 수 있습니다. 많은 플랫폼과 장치에서는 가능하지만 Raspberry Pi에서는 그렇지 않습니다./p>

3. 바이트 크기 - 데이터 비트 수입니다. 가능한 값은 FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS일 수 있습니다./p>

4. 패리티 - 패리티 검사를 활성화합니다. 가능한 값은 PARITY_NONE, PARITY_EVEN, PARITY_ODD PARITY_MARK, PARITY_SPACE일 수 있습니다./p>

5. stopbits – 정지 비트의 수입니다. 가능한 값은 STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO입니다./p>

6. timeout – 읽기 타임아웃 값을 설정합니다./p>

7. xonxoff – 소프트웨어 흐름 제어를 활성화합니다./p>

8. rtscts – 하드웨어(RTS/CTS 모뎀 라인) 흐름 제어를 활성화합니다./p>

9. dsrdtr - 하드웨어(DSR/DTR 모뎀 라인) 흐름 제어를 활성화합니다./p>

10. write_timeout - 쓰기 타임아웃 값을 설정합니다./p>

11. inner_byte_timeout - 문자간 타임아웃을 설정합니다. 없음으로 설정하여 비활성화할 수 있습니다. 기본적으로 비활성화되어 있습니다.

 

주어진 포트 이름으로 직렬 객체가 생성되면 포트가 즉시 열립니다. 포트가 None으로 설정된 경우, open() 메서드에 대한 연속적인 호출을 사용하여 포트를 열어야 합니다.

 

serial.open() — 이 메서드는 포트를 엽니다. 모뎀의 RTS와 DTR 라인을 사용한다면 이 방법으로 rts와 dtr의 상태를 적용할 수 있다.

 

serial.close() — 이 메서드는 포트를 즉시 닫습니다.

 

serial.__del__() — 이 메서드는 포트를 닫고 직렬 객체를 파괴합니다.

 

serial.read() — 이 메서드는 직렬 포트에서 size 바이트를 읽습니다. 시간 초과가 적용되면 요청한 것보다 적은 수의 문자를 반환할 수 있습니다. 시간 초과가 없으면 요청된 바이트 수를 읽을 때까지 프로그램 실행을 차단합니다. 버전 2.5 이상에서 메서드는 사용 가능한 경우 바이트를 반환합니다. 그렇지 않으면 완전한 문자열을 반환합니다.

 

serial.read_until(expected=LF, size=None) — 이 메서드는 예상 시퀀스(예: 줄 바꿈 '\n')가 발견되거나 크기가 초과되거나 시간 초과가 발생할 때까지 직렬 데이터를 읽습니다.

 

serial.write() — 이 메서드는 바이트 데이터를 직렬 포트에 씁니다(데이터는 바이트 유형이어야 함). 이는 유니코드 문자열이 str 라이브러리의 encode() 메서드를 사용하여 인코딩되어야 함을 의미합니다. 버전 2.5 이상에서는 사용 가능한 경우 바이트 또는 바이트 배열을 씁니다. 그렇지 않으면 str 유형을 씁니다.

 

serial.readline(size) — 이 메서드는 io.IOBase.readline()을 통해 제공되었습니다. 직렬 포트에서 한 줄을 읽고 반환합니다. 매개변수 크기가 지정되면 해당 크기 바이트는 행에서 읽힙니다.

 

serial.readlines(hint=-1) — 이 메서드는 io.IOBase.readlines()를 통해 제공되었습니다. 이 메서드는 직렬 포트에서 라인 목록을 읽고 반환합니다. 매개변수 힌트를 지정하여 읽는 행 수를 제어할 수 있습니다.

 

serial.writelines(lines) — 이 메서드는 io.IOBase.writelines()를 통해 제공되었습니다. 직렬 포트에 라인 목록을 씁니다. 그러나 각 줄의 끝에 줄 구분 기호가 있어야 합니다(예: 줄 바꿈 문자 '\n').

 

serial.flush() — 이 메서드는 파일류 객체를 플러시합니다. 또한 모든 데이터가 기록될 때까지 포트가 대기하도록 합니다.

 

serial.in_waiting() — 이 메서드는 수신된 버퍼의 바이트 수를 반환합니다.

 

serial.out_waiting() — 이 메서드는 출력 버퍼의 바이트 수를 반환합니다.

 

serial.reset_input_buffer() — 이 메서드는 모든 내용을 버리고 입력 버퍼를 플러시합니다. 3.0 이전 버전에서는 flushinput() 메서드를 대체했습니다.

 

serial.reset_output_buffer() — 이 메서드는 출력 버퍼를 지우고 출력을 중단하고 모든 내용을 버립니다 . 일부 USB 직렬 어댑터의 경우 이것은 OS의 버퍼만 플러시할 수 있으며 USB 포트에 있을 수 있는 모든 데이터는 플러시하지 않습니다. 3.0 이전 버전에서는 flushoutput() 메서드를 대체했습니다.

 

serial.send_break(duration=0.25) — 이 메서드는 BREAK 조건을 보냅니다. 지속 시간(초)을 지정하면 해당 시간이 지나면 포트가 유휴 상태로 돌아갑니다. 하드웨어 흐름 제어 모뎀 라인을 사용할 때만 중단 조건을 적용할 수 있습니다.

 

serial.get_settings() — 이 메서드는 포트 설정이 포함 된 사전 을 반환합니다 . 설정은 변수에 저장하고 나중에 apply_settings() 메서드를 사용하여 복원할 수 있습니다. 3.0 이전 버전에서는 getSettingsDict() 메서드를 대체했습니다.

 

serial.apply_settings(d) — 이 메서드는 get_settings()에 의해 생성된 사전을 적용합니다. 키가 없고 변경 사항이 적용되면 설정이 변경되지 않은 상태로 유지됩니다. 3.0 이전 버전에서는 applySettingsDict() 메서드를 대체했습니다.

 

Python 스크립트에서 사용되는 메서드는 시스템에 설치된 PySerial 버전에 따라 다릅니다. 버전이 2.5 이전인 경우 이전 방법을 사용할 수 있습니다. 그러나 PySerial 버전 2.5 이상이 설치된 경우에는 새로운 방법을 사용해야 합니다.

 

다음 코드를 사용하여 Python 스크립트에서 PySerial 라이브러리를 가져올 수 있습니다.

 

import serial

 

스크립트에서 라이브러리를 가져온 후 다양한 방법과 속성을 사용하여 UART 프로토콜을 통한 직렬 통신을 구성하고 실행할 수 있습니다.

 

레시피: RPi에서 PC로 직렬 데이터 보내기

 

이 레시피에서는 Raspberry Pi에서 데스크톱 컴퓨터로 데이터를 보냅니다.

 

필요한 구성 요소

1. Raspberry Pi 3/4 Model B x1

2. FTDI USB-직렬 변환기 x1

3. USB-to-USB 케이블 x1

4. 암-암 점퍼 와이어

 

회로도

이미지 출처 https://www.engineersgarage.com/articles-raspberry-pi-serial-communication-uart-protocol-ttl-port-usb-serial-boards/

 

회로 연결

 

  • GPIO14(UART_TXD0, 보드 핀 번호 8)를 USB 직렬 보드의 Tx에 연결합니다.
  • GPIO15(UART_RXD0, 보드 핀 번호 10)를 점퍼 와이어를 사용하여 USB 직렬 보드의 Rx에 연결합니다.
  • RPi의 GND 핀(예: 보드 핀 번호 6) 중 하나를 USB 직렬 보드의 GND에 연결합니다.
  • USB-to-USB 케이블을 사용하여 USB 직렬 보드를 데스크탑 컴퓨터의 USB 포트에 연결합니다.

 

파이썬 스크립트 1

 

import time
import serial

ser = serial.Serial("/dev/ttyS0", baudrate=19200, bytesize=8, parity='N', stopbits=1, timeout=None, xonxoff=0, rtscts=0)
counter = 0

while 1:
	ser.write(b'Write counter: %d \n'%(counter))
    print("Printed")
    time.sleep(1)
    counter += 1

 

 

프로젝트 작동 방식

 

Raspberry Pi와 데스크탑 컴퓨터는 USB 직렬 보드를 통해 연결됩니다. RPi에서 python 스크립트는 직렬 데이터를 데스크탑 컴퓨터로 보내는 데 사용됩니다. 스크립트는 카운터 값을 업데이트하고 직렬 포트로 보냅니다. RPi는 UART 통신을 위해 직렬 TTL 포트를 사용하도록 구성됩니다.

 

전송된 데이터는 패리티가 없는 9600bps의 전송 속도, 1개의 정지 비트 및 1초의 타임아웃으로 UART 프로토콜을 통해 8비트 데이터 청크로 전달됩니다. 카운터 값이 직렬 포트를 통해 전송될 때마다 "Printed"라는 메시지가 콘솔(Python IDLE의)에 인쇄되고 카운터 변수는 1씩 증가합니다.

 

데스크탑 측에서 Termite 직렬 응용 프로그램은 직렬 데이터를 수신하기 위해 Win32 시스템에서 사용됩니다. Termite는 USB 직렬 장치가 COM22로 데스크탑 컴퓨터(노트북)에 연결될 때 자동으로 감지합니다.

 

UART 설정은 Termite에서 9600 8N1로 설정되며, 이는 Raspberry Pi의 Python 스크립트에서 사용된 것과 동일한 설정입니다.

 

프로그래밍 가이드

 

스크립트는 직렬 및 시간 라이브러리를 가져오는 것으로 시작합니다. 직렬 객체는 /dev/ttyS0에 지정된 포트와 함께 serial.Serial() 메서드를 사용하여 생성됩니다.

 

카운터 변수 "counter"가 선언되고 0으로 초기화됩니다. 무한 while 루프에서 카운터 값은 serial.write() 메서드를 사용하여 문자열로 전송됩니다. "Printed"라는 메시지는 print() 메서드를 사용하여 Python IDLE의 콘솔에 인쇄됩니다.

 

sleep() 메서드를 사용하여 1초의 지연이 주어지고 카운터 값이 1 증가합니다. 루프는 스크립트가 콘솔에서 강제 종료될 때까지 계속 반복됩니다.

 

import time
import serial
ser = serial.Serial(“/dev/ttyS0”, baudrate = 9600, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1)
counter=0
while 1:
    ser.write(b’Write counter: %d \n’%(counter))
	print(“Printed”)
	time.sleep(1)
	counter += 1

 

레시피: 양방향 직렬 통신

 

이 레시피에서는 데스크톱 컴퓨터와 라즈베리 파이 간의 양방향 통신을 설정합니다. 컴퓨터에서 RPi의 직렬 포트로 메시지를 보내면 동일한 메시지를 컴퓨터로 다시 보냅니다.

 

파이썬 스크립트 2

 

import time
from time import sleep
import serial
ser = serial.Serial(“/dev/ttyS0”, baudrate = 9600, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1)
while 1:
    rx_data =ser.readline()
    print(rx_data)
    ser.write(rx_data)

 

파이썬 스크립트 3

 

while 1:
    rx_data = ser.read()
    sleep(0.03)
    data_left = ser.inWaiting()
    rx_data += ser.read(data_left)
    print(rx_data)
    ser.write(rx_data)

 

프로젝트 작동 방식

 

Raspberry Pi와 데스크탑 컴퓨터는 USB 직렬 보드를 통해 연결됩니다. 데스크탑 측에서 Termite 직렬 응용 프로그램은 직렬 데이터를 보내고 받기 위해 Win32 시스템에서 사용됩니다. Termite는 USB 직렬 장치가 COM22로 데스크탑 컴퓨터(노트북)에 연결될 때 자동으로 감지합니다.

 

UART 설정은 Termite에서 9600 8N1로 설정되며, 이는 RPi의 Python 스크립트에서 사용되는 것과 동일한 설정입니다. Termite에서 "Hello" 또는 "How ru?"와 같은 다른 메시지를 RPi의 직렬 포트로 보냅니다.

 

Raspberry Pi에서 python 스크립트는 다음을 위해 사용됩니다.

 

직렬 하드웨어 포트에서 직렬 데이터 읽기

 

해당 데이터를 콘솔에 인쇄

 

동일한 데이터를 데스크톱 컴퓨터로 다시 보내기

 

RPi는 UART 통신을 위해 직렬 TTL 포트를 사용하도록 구성됩니다. 전송된 데이터는 패리티가 없는 9600bps의 전송 속도, 1개의 정지 비트 및 1초의 타임아웃으로 UART 프로토콜을 통해 8비트 데이터 청크로 전달됩니다.

 

동일한 기능을 위해 작성된 두 개의 스크립트가 있습니다(즉, 직렬 포트에서 데이터를 읽고, 콘솔로 인쇄하고, 직렬 포트를 통해 데스크탑 컴퓨터로 동일한 메시지를 다시 전송). 그러나 두 스크립트 모두 PySerial 라이브러리의 다른 방법을 사용하여 이러한 동일한 기능을 실행합니다.

 

프로그래밍 가이드

 

스크립트 1은 시간, 절전 모드 및 직렬 라이브러리를 가져오는 것으로 시작합니다. 직렬 객체는 /dev/ttyS0에 지정된 포트와 함께 serial.Serial() 메서드를 사용하여 생성됩니다.

 

무한 while 루프에서 TTL 포트의 직렬 데이터는 readline() 메서드를 사용하여 읽습니다. 그런 다음 읽은 데이터는 print() 메서드를 사용하여 콘솔에 인쇄됩니다. 그런 다음 write() 메서드를 사용하여 직렬 포트를 통해 데스크톱 컴퓨터에 동일한 내용을 씁니다.

 

스크립트 2는 스크립트 1과 유사하지만 read() 메서드를 사용하여 직렬 포트 데이터를 읽고 inWaiting() 메서드를 사용하여 남은 데이터를 기다립니다. 이후 다시 read() 메서드를 사용하여 왼쪽 데이터를 읽습니다.

 

두 번째 스크립트는 RPi의 직렬 포트에서 데이터를 읽는 방식만 다릅니다. 이제 Raspberry Pi의 직렬 하드웨어 포트를 통해 데이터를 읽고 쓸 수 있습니다.

 

어렵네. 쉬운 일이 어디있어. 그렇지 않니?

 

 

참고

라즈베리파이4 모든 UART 활성화하기

파이썬(Python) - 시리얼 통신 사용하기 - pyserial 

pySerial API

Serial UART Communication

 

 

 

반응형