3.17. Fluxos de bits e medição de pulsos

Alguns dispositivos precisam de padrões de pulsos com temporização precisa em vez de um sinal PWM de frequência constante. Um LED RGB WS2812 codifica cada bit como um pulso de uma largura específica; um sensor ultrassónico HC-SR04 responde com um pulso de eco cuja largura é o tempo de voo de ida e volta; um controlo remoto por IV envia um cabeçalho seguido de bits de dados como sequências de ligado-desligado.

O módulo machine tem duas funções para este tipo de GPIO com temporização precisa:

  • bitstream() envia um trem de pulsos com uma temporização separada para os bits 0 e 1.

  • time_pulse_us() mede a largura de um pulso de entrada em microssegundos.

3.17.1. Enviar um fluxo de bits

machine.bitstream() recebe um pino, uma codificação, uma especificação de temporização e os bytes a enviar. A codificação mais comum (0) é a modulação por duração de pulso alto-baixo: cada bit é um pulso alto de uma largura seguido de um pulso baixo de outra, sendo os bits 0 e 1 com temporizações distintas.

Two stacked waveforms. The top one shows a 0 bit: a brief high pulse (high_0) followed by a longer low period (low_0). The bottom one shows a 1 bit: a longer high pulse (high_1) followed by a shorter low period (low_1).

A codificação por duração de pulso alto-baixo: um 0 e um 1 consistem cada um numa fase alta seguida de uma fase baixa, com larguras distintas.

O exemplo canónico é o LED RGB WS2812 (NeoPixel), que espera bits a 800 kHz com estas temporizações:

  • 0: 400 ns alto, depois 850 ns baixo.

  • 1: 800 ns alto, depois 450 ns baixo.

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")

O MCU gera os pulsos por bit-banging nas larguras pedidas; nas câmaras suficientemente rápidas, a temporização é precisa a dezenas de nanossegundos.

Aviso

bitstream() desativa todas as interrupções durante toda a transmissão para poder manter um controlo preciso sobre a temporização dos pulsos. A duração da chamada escala linearmente com o número de bytes – à temporização WS2812 (cerca de 10 µs por byte), enviar 100 bytes pausa a CPU cerca de 1 ms, 1000 bytes por 10 ms e 10000 bytes por 100 ms completos. Qualquer coisa além de algumas centenas de bytes por chamada arrisca bloqueios notáveis – divida as atualizações longas em partes menores, com a chamada a retornar entre cada parte para que o resto do script possa ser executado.

Nota

Para controlar fitas WS2812 / NeoPixel em particular, o módulo neopixel envolve bitstream() numa interface de nível superior que trata da reordenação de cores e das atualizações em massa da fita. Recorra a bitstream() diretamente quando o protocolo não é WS2812 mas ainda se enquadra numa forma PDM alto-baixo.

3.17.2. Medir um pulso de entrada

machine.time_pulse_us() mede a largura de um único pulso num pino. Aguarda que a linha atinja o nível especificado (o início do pulso), depois mede quanto tempo a linha permanece nesse nível (o fim do pulso) e devolve a duração em microssegundos.

O uso clássico é um sensor de distância ultrassónico HC-SR04. A câmara envia um pulso de disparo de 10 µs, depois aguarda que o pino de eco devolva um pulso cuja largura é o tempo de ida e volta do som:

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)

O terceiro argumento é o timeout em microssegundos, aplicado separadamente a «aguardar o início do pulso» e «aguardar o fim do pulso». No timeout, a função devolve um valor negativo que identifica qual a fase que falhou: -2 se o pulso nunca começou, -1 se começou mas nunca terminou dentro da janela.

Ambas as metades do timeout são importantes para sensores reais. Um HC-SR04 pode demorar um a dois milissegundos para iniciar o seu eco, e o próprio eco pode ter dezenas de milissegundos para objetos distantes. Dimensionar timeout_us para o alcance máximo necessário mantém a medição limitada.