3.17. 비트 스트림과 펄스 측정¶
어떤 장치는 일정한 주파수의 PWM 신호가 아니라 정밀하게 타이밍된 펄스 패턴을 필요로 합니다. WS2812 RGB LED는 각 비트를 특정 폭의 펄스로 인코딩하고, HC-SR04 초음파 거리계는 폭이 왕복 비행 시간인 에코 펄스로 응답하며, IR 리모컨은 헤더에 이어 데이터 비트를 온오프 시퀀스로 전송합니다.
machine 모듈에는 이런 종류의 타이밍 정밀 GPIO를 위한 두 가지 함수가 있습니다:
bitstream()은0비트와1비트에 대해 별도의 타이밍을 적용하여 펄스 열을 전송합니다.time_pulse_us()는 들어오는 펄스의 폭을 마이크로초 단위로 측정합니다.
3.17.1. 비트 스트림 전송하기¶
machine.bitstream()은 핀, 인코딩, 타이밍 명세, 그리고 전송할 바이트를 받습니다. 가장 흔한 인코딩(0)은 하이-로우 펄스 폭 변조입니다. 각 비트는 한 가지 폭의 하이 펄스에 이어 다른 폭의 로우 펄스로 이루어지며, 0 비트와 1 비트는 서로 다른 타이밍을 가집니다.
하이-로우 펄스 폭 인코딩: 0과 1은 각각 하이 단계에 이어 로우 단계로 구성되며, 서로 다른 폭을 가집니다.¶
대표적인 예는 WS2812(NeoPixel) RGB LED로, 800kHz에서 다음과 같은 타이밍의 비트를 기대합니다:
0: 400ns 하이, 그 다음 850ns 로우.1: 800ns 하이, 그 다음 450ns 로우.
from machine import Pin, bitstream
pin = Pin("P7", Pin.OUT)
# (high_0, low_0, high_1, low_1) in nanoseconds
timing = (400, 850, 800, 450)
# one LED: GRB order, three bytes per LED (red shown here)
bitstream(pin, 0, timing, b"\x00\xff\x00")
MCU는 요청된 폭으로 펄스를 비트뱅잉합니다. 충분히 빠른 카메라에서는 타이밍이 수십 나노초 이내로 정확합니다.
경고
bitstream()은 펄스 타이밍을 정밀하게 제어하기 위해 전송 전체 동안 모든 인터럽트를 비활성화합니다. 호출 지속 시간은 바이트 수에 선형으로 비례합니다. WS2812 타이밍(바이트당 약 10µs)에서 100바이트를 보내면 CPU가 약 1ms 동안 멈추고, 1000바이트는 10ms, 10000바이트는 꼬박 100ms 동안 멈춥니다. 호출당 수백 바이트를 넘어서는 것은 눈에 띄는 멈춤을 일으킬 위험이 있습니다. 긴 업데이트는 더 작은 청크로 나누고, 각 청크 사이에 호출이 반환되어 나머지 스크립트가 실행될 수 있도록 하세요.
참고
특히 WS2812 / NeoPixel 스트립을 구동하는 경우, neopixel 모듈이 bitstream()을 더 상위 수준의 인터페이스로 감싸 색상 순서 재배열과 대량 스트립 업데이트를 처리합니다. 프로토콜이 WS2812는 아니지만 여전히 하이-로우 PDM 형태에 맞는 경우에는 bitstream()을 직접 사용하세요.
3.17.2. 들어오는 펄스 측정하기¶
machine.time_pulse_us()는 핀에 들어오는 단일 펄스의 폭을 측정합니다. 라인이 지정된 레벨에 도달할 때까지(펄스의 시작) 기다린 다음, 라인이 그 레벨을 유지하는 시간(펄스의 끝)을 측정하여 그 지속 시간을 마이크로초 단위로 반환합니다.
고전적인 용도는 HC-SR04 초음파 거리 센서입니다. 카메라는 10µs 트리거 펄스를 보낸 다음, 에코 핀이 폭이 소리의 왕복 시간인 펄스를 반환하기를 기다립니다:
import time
from machine import Pin, time_pulse_us
trigger = Pin("P7", Pin.OUT, value=0)
echo = Pin("P8", Pin.IN)
while True:
trigger.value(1)
time.sleep_us(10)
trigger.value(0)
width = time_pulse_us(echo, 1, timeout_us=30_000)
if width > 0:
# sound at ~343 m/s; round trip / 2 / 343 = distance (m)
distance_cm = (width * 343) / 2 / 10_000
print(distance_cm, "cm")
time.sleep_ms(100)
세 번째 인자는 마이크로초 단위 타임아웃으로, “펄스가 시작되기를 기다림”과 “펄스가 끝나기를 기다림”에 각각 따로 적용됩니다. 타임아웃 시 함수는 어느 단계가 실패했는지 식별하는 음수 값을 반환합니다. 펄스가 전혀 시작되지 않으면 -2, 시작되었으나 시간 창 안에 끝나지 않으면 -1입니다.
실제 센서에서는 타임아웃의 양쪽 절반이 모두 중요합니다. HC-SR04는 에코를 시작하는 데 1~2밀리초가 걸릴 수 있고, 에코 자체는 먼 물체의 경우 수십 밀리초에 이를 수 있습니다. timeout_us를 필요한 최대 거리에 맞게 잡으면 측정이 일정 범위 안에 유지됩니다.