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 位:一段 短暂的高脉冲(high_0)后接一段较长的低 电平(low_0)。下方的表示一个 1 位:一段较长的 高脉冲(high_1)后接一段较短的低电平(low_1)。

高-低脉冲宽度编码:01 各由一个高电平相位后接一个低电平相位构成,宽度各不相同。

典型示例是 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 设定为所需的最大量程,可使测量保持有界。