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,它預期位元以 800 kHz 並採用以下計時:
0:400 ns 高,接著 850 ns 低。1:800 ns 高,接著 450 ns 低。
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 以位元拍打(bit-bang)方式按要求的寬度產生脈衝;在速度夠快的相機上,計時可精確到數十奈秒之內。
警告
bitstream() 在整個傳輸期間停用所有中斷,以便能精確控制脈衝計時。呼叫時長會隨位元組數線性增長 -- 以 WS2812 計時(每位元組約 10 µs)來說,發送 100 位元組會讓 CPU 暫停約 1 ms、1000 位元組約 10 ms、10000 位元組則為整整 100 ms。每次呼叫超過數百位元組就有可能造成明顯的卡頓 -- 請將長更新拆解為較小的區塊,讓呼叫在每個區塊之間回傳,好讓指令碼的其餘部分能夠執行。
備註
尤其是在驅動 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 可能需要一到兩毫秒才開始它的回波,而對遠處物體而言,回波本身可能長達數十毫秒。將 timeout_us 設定為所需最大量程的大小,可使測量保持有界。