3.19. 코드로 보는 UART¶
machine.UART는 하드웨어 UART 채널 하나를 감쌉니다. 버스 id와 보드 레이트로 하나를 생성하며, 나머지는 모두 합리적인 기본값을 가집니다:
from machine import UART
uart = UART(3, baudrate=115200)
id는 어떤 하드웨어 UART를 사용할지 선택합니다. 그 값은 보드에 따라 다릅니다(특정 카메라에서 사용 가능한 버스 번호와 핀 할당은 OpenMV 보드를 참조하십시오). 기본 프레임 형식은 데이터 비트 8개, 패리티 없음, 정지 비트 1개로 – 모두가 기대하는 “8N1”입니다.
3.19.1. 쓰기와 읽기¶
UART 출력은 write()를 통해 나갑니다:
uart.write("hello\n")
uart.write(b"\x01\x02\x03")
write는 str(UTF-8로 인코딩됨) 또는 bytes / bytearray 중 하나를 받습니다. 바이트가 TX 버퍼에 큐잉되면 즉시 반환되며, 하드웨어가 백그라운드에서 그것들을 클럭아웃하는 작업을 마칩니다.
읽기는 필요한 것에 따라 세 가지 메서드를 통해 이루어집니다:
n = uart.any() # bytes available to read right now
data = uart.read(8) # up to 8 bytes, or None on timeout
line = uart.readline() # bytes ending in '\n', or None on timeout
any()는 블로킹하지 않고 RX 버퍼를 확인합니다. read()는 고정된 수의 바이트를 읽으며, 타임아웃(생성자의 timeout 인수로 구성 가능)이 먼저 만료되면 None을 반환합니다. readline()은 다음 줄바꿈까지(포함하여) 읽으며, 줄 기반 프로토콜에 유용합니다.
받은 것을 무엇이든 그대로 되돌려 보내는 간단한 루프:
uart = UART(3, baudrate=115200, timeout=100)
while True:
if uart.any():
data = uart.read()
uart.write(data)
길이 인수 없이 호출한 read()는 수신 선이 설정된 timeout 동안 조용히 유지될 때까지 바이트를 계속 읽은 다음, 누적된 내용을 반환합니다. timeout=100으로 설정하면 여기서 각 호출은 한 번의 버스트(burst) 바이트 – 송신기가 바이트 사이에 100 ms의 간격 없이 내보낸 모든 것 – 를 반환합니다. timeout이 없으면 호출은 송신기가 끝났다는 신호를 받지 못해 무한정 멈춰 있을 수 있습니다.
3.19.2. struct를 이용한 바이너리 데이터¶
정수와 부동소수점을 선상으로 보내는 것이 바로 struct 모듈의 용도입니다. 이 모듈은 바이트 순서와 각 필드의 타입을 명명하는 형식 문자열을 사용하여 고정 폭 값들을 bytes 객체로 패킹합니다:
import struct
uart.write(struct.pack("<lhb", count, temperature_x100, status))
선두의 "<"는 리틀 엔디언 바이트 순서를 선택합니다. "l"은 32비트 부호 있는 정수, "h"는 16비트 부호 있는 정수, "b"는 8비트 부호 있는 정수입니다. 반대편은 동일한 형식 문자열로 언패킹합니다:
payload = uart.read(7) # 4 + 2 + 1 = 7 bytes
count, temperature_x100, status = struct.unpack("<lhb", payload)
형식 문자열은 양쪽 끝 사이의 계약입니다. 어떤 불일치든 – 잘못된 바이트 순서, 잘못된 타입 크기, 잘못된 필드 순서 – 무의미한 값을 만들어 냅니다.
3.19.3. 버퍼링과 고속 읽기¶
any()와 read()를 호출하는 폴링 루프는, 메인 루프가 수신 버퍼가 가득 차기 전에 비울 만큼 충분히 빠르게 반복되는 한 대부분의 트래픽을 스스로 따라잡습니다. 속도가 올라가거나 루프가 바쁠 때 두 가지 생성자 옵션이 중요해집니다.
rxbuf는 소프트웨어 RX 버퍼의 크기를 설정합니다. 기본값은 수백 바이트입니다. 버스트당 수백 바이트를 내보내는 센서의 경우, 또는 메인 루프가 폴링 사이에 긴 작업을 수행할 때, 버퍼를 키우면 루프가 다른 곳에서 바쁜 동안에도 들어오는 바이트가 버려지지 않게 됩니다:
uart = UART(3, baudrate=115200, timeout=100, rxbuf=4096)
버퍼가 가득 찬 동안 도착하는 것은 무엇이든 손실됩니다. 비우는 작업 사이의 가장 긴 간격을 감당하도록 rxbuf 크기를 정하십시오.
지속적인 고속에는 readinto()가 매 호출마다 새 bytes 객체를 반환하는 대신 미리 할당된 버퍼로 읽어 들입니다:
buf = bytearray(256)
while True:
n = uart.readinto(buf)
if n:
process(buf, n)
읽기 경로에서 할당이 일어나지 않는데, 이는 힙이 단편화되어 있거나 할당 지연 시간이 바이트 간 타이밍 예산을 넘기게 될 때 중요합니다.