3.17. 位流与脉冲测量¶
某些设备需要精确定时的脉冲模式,而不是恒定频率的 PWM 信号。WS2812 RGB LED 将每个比特编码为特定宽度的脉冲;HC-SR04 超声波测距仪以一个回波脉冲作答,其宽度即往返飞行时间;红外遥控器先发送一个引导头,随后将数据位作为通断序列发送出去。
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 设定为所需的最大量程,可使测量保持有界。