3.17. Bitströmmar och pulsmätning

Vissa enheter behöver precist tidsstyrda pulsmönster snarare än en PWM-signal med konstant frekvens. En WS2812 RGB-LED kodar varje bit som en puls av en specifik bredd; en HC-SR04 ultraljudsmätare svarar med en ekopuls vars bredd är tur-och-retur-flygtiden; en IR-fjärrkontroll skickar ett huvud följt av databitar som på-av-sekvenser.

machine-modulen har två funktioner för denna sorts tidsprecisa GPIO:

  • bitstream() skickar ett pulståg med separat tidssättning för 0- och 1-bitar.

  • time_pulse_us() mäter bredden på en inkommande puls i mikrosekunder.

3.17.1. Skicka en bitström

machine.bitstream() tar ett stift, en kodning, en tidsspecifikation och de byte som ska skickas. Den vanligaste kodningen (0) är hög-låg-pulsduration-modulation: varje bit är en hög puls av en bredd följd av en låg puls av en annan, där 0- och 1-bitar har olika tidssättningar.

Två staplade vågformer. Den övre visar en 0-bit: en kort hög puls (high_0) följd av en längre låg period (low_0). Den nedre visar en 1-bit: en längre hög puls (high_1) följd av en kortare låg period (low_1).

Hög-låg-pulsduration-kodningen: en 0 och en 1 består var och en av en hög fas följd av en låg fas, med olika bredder.

Det kanoniska exemplet är WS2812 (NeoPixel) RGB-LED, som förväntar sig bitar vid 800 kHz med dessa tidssättningar:

  • 0: 400 ns hög, sedan 850 ns låg.

  • 1: 800 ns hög, sedan 450 ns låg.

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:n bit-bangar pulserna vid de begärda bredderna; på kameror som är tillräckligt snabba för det är tidssättningen exakt inom tiotals nanosekunder.

Varning

bitstream() inaktiverar alla avbrott under hela överföringen så att den kan behålla precis kontroll över pulstidssättningen. Anropets varaktighet skalar linjärt med antalet byte – vid WS2812-tidssättning (cirka 10 µs per byte) pausar 100 byte processorn i ungefär 1 ms, 1000 byte i 10 ms och 10000 byte i hela 100 ms. Allt utöver några hundra byte per anrop riskerar märkbara låsningar – dela upp långa uppdateringar i mindre delar, där anropet returnerar mellan varje del så att resten av skriptet kan köra.

Anteckning

För att driva specifikt WS2812-/NeoPixel-remsor lindar neopixel-modulen bitstream() i ett gränssnitt på högre nivå som hanterar omkastning av färgordning och bulk-uppdateringar av remsor. Ta till bitstream() direkt när protokollet inte är WS2812 men ändå passar en hög-låg-PDM-form.

3.17.2. Mäta en inkommande puls

machine.time_pulse_us() mäter bredden på en enskild puls på ett stift. Den väntar på att ledningen ska nå den angivna nivån (pulsens början), mäter sedan hur länge ledningen stannar på den nivån (pulsens slut) och returnerar varaktigheten i mikrosekunder.

Den klassiska användningen är en HC-SR04 ultraljudsavståndssensor. Kameran skickar en 10 µs triggerpuls och väntar sedan på att eko-stiftet returnerar en puls vars bredd är ljudets tur-och-retur-tid:

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)

Det tredje argumentet är timeouten i mikrosekunder, tillämpad separat på ”vänta på att pulsen börjar” och ”vänta på att pulsen slutar”. Vid timeout returnerar funktionen ett negativt värde som identifierar vilken fas som misslyckades: -2 om pulsen aldrig började, -1 om den började men aldrig slutade inom fönstret.

Båda halvorna av timeouten spelar roll för verkliga sensorer. En HC-SR04 kan ta en till två millisekunder att starta sitt eko, och själva ekot kan vara tiotals millisekunder långt för avlägsna objekt. Att dimensionera timeout_us efter det maximala avstånd som behövs håller mätningen begränsad.