3.17. Luồng bit và đo xung¶
Một số thiết bị cần các mẫu xung được định thời chính xác thay vì tín hiệu PWM với tần số cố định. LED RGB WS2812 mã hóa mỗi bit dưới dạng xung có độ rộng cụ thể; bộ đo khoảng cách siêu âm HC-SR04 trả lời bằng xung echo có độ rộng là thời gian bay khứ hồi; điều khiển từ xa IR gửi một tiêu đề theo sau là các bit dữ liệu dưới dạng chuỗi bật-tắt.
Module machine có hai hàm cho loại GPIO định thời chính xác này:
bitstream()gửi chuỗi xung với thời gian riêng biệt cho bit0và1.time_pulse_us()đo độ rộng của xung đến bằng micro giây.
3.17.1. Gửi luồng bit¶
machine.bitstream() nhận một chân, một mã hóa, một đặc tả thời gian, và các byte để gửi. Mã hóa phổ biến nhất (0) là điều chế thời lượng xung cao-thấp: mỗi bit là một xung cao có độ rộng nhất định theo sau là xung thấp có độ rộng khác, với bit 0 và 1 có thời gian khác nhau.
Mã hóa thời lượng xung cao-thấp: bit 0 và 1 mỗi bit bao gồm pha cao theo sau là pha thấp, với độ rộng khác nhau.¶
Ví dụ điển hình là LED RGB WS2812 (NeoPixel), mong đợi các bit ở 800 kHz với các thời gian sau:
0: 400 ns cao, sau đó 850 ns thấp.1: 800 ns cao, sau đó 450 ns thấp.
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 các xung ở độ rộng được yêu cầu; trên các cam đủ nhanh, thời gian chính xác đến vài chục nanosecond.
Cảnh báo
bitstream() vô hiệu hóa tất cả các ngắt trong toàn bộ quá trình truyền để duy trì kiểm soát chính xác thời gian xung. Thời gian gọi tăng tuyến tính với số byte -- ở thời gian WS2812 (khoảng 10 µs mỗi byte), gửi 100 byte tạm dừng CPU khoảng 1 ms, 1000 byte khoảng 10 ms, và 10000 byte đầy đủ 100 ms. Bất kỳ thứ gì ngoài vài trăm byte mỗi lần gọi có nguy cơ bị treo rõ rệt -- chia các bản cập nhật dài thành các đoạn nhỏ hơn, với lần gọi trả về giữa mỗi đoạn để phần còn lại của tập lệnh có thể chạy.
Ghi chú
Để điều khiển các dải WS2812 / NeoPixel nói riêng, module neopixel bọc bitstream() trong một giao diện cấp cao hơn xử lý việc xáo trộn thứ tự màu và cập nhật dải hàng loạt. Sử dụng bitstream() trực tiếp khi giao thức không phải WS2812 nhưng vẫn phù hợp dạng PDM cao-thấp.
3.17.2. Đo xung đến¶
machine.time_pulse_us() đo độ rộng của một xung đơn trên một chân. Nó chờ đường dây đạt mức chỉ định (bắt đầu xung), sau đó đo thời gian đường dây duy trì ở mức đó (kết thúc xung), và trả về thời lượng bằng micro giây.
Ứng dụng điển hình là cảm biến khoảng cách siêu âm HC-SR04. Camera gửi xung kích hoạt 10 µs, sau đó chờ chân echo trả về xung có độ rộng là thời gian bay khứ hồi của âm thanh:
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)
Đối số thứ ba là timeout tính bằng micro giây, áp dụng riêng cho "chờ xung bắt đầu" và "chờ xung kết thúc". Khi hết timeout, hàm trả về giá trị âm xác định giai đoạn nào thất bại: -2 nếu xung không bao giờ bắt đầu, -1 nếu nó bắt đầu nhưng không kết thúc trong cửa sổ.
Cả hai nửa của timeout đều quan trọng đối với các cảm biến thực tế. HC-SR04 có thể mất một đến hai mili giây để bắt đầu echo, và bản thân echo có thể dài hàng chục mili giây đối với các vật ở xa. Điều chỉnh timeout_us theo phạm vi tối đa cần thiết giúp giới hạn phép đo.