3.17. זרמי ביטים ומדידת פולסים¶
חלק מההתקנים זקוקים לתבניות פולסים מתוזמנות בדיוק ולא לאות PWM בתדר קבוע. נורית LED מסוג WS2812 RGB מקודדת כל ביט כפולס ברוחב מסוים; מד-טווח אולטרסוני HC-SR04 עונה בפולס הד שרוחבו הוא זמן הטיסה הלוך-ושוב; שלט רחוק IR שולח כותרת ואחריה ביטי נתונים כרצפים של דלוק-כבוי.
למודול 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 מ“ש, 1000 בתים ל-10 מ“ש, ו-10000 בתים ל-100 מ“ש מלאות. כל דבר מעבר לכמה מאות בתים לקריאה מסכן תקיעות מורגשות – פצלו עדכונים ארוכים למקטעים קטנים יותר, כשהקריאה חוזרת בין כל מקטע כדי ששאר הסקריפט יוכל לרוץ.
הערה
להנעת רצועות WS2812 / NeoPixel בפרט, מודול neopixel עוטף את bitstream() בממשק ברמה גבוהה יותר שמטפל בערבוב סדר-הצבעים ובעדכוני רצועה בכמות. פנו ל-bitstream() ישירות כשהפרוטוקול אינו WS2812 אך עדיין מתאים לצורת PDM גבוה-נמוך.
3.17.2. מדידת פולס נכנס¶
machine.time_pulse_us() מודדת את רוחב הפולס היחיד על פין. היא ממתינה שהקו יגיע לרמה שצוינה (תחילת הפולס), ואז מודדת כמה זמן הקו נשאר באותה רמה (סוף הפולס), ומחזירה את המשך במיקרו-שניות.
השימוש הקלאסי הוא חיישן מרחק אולטרסוני HC-SR04. המצלמה שולחת פולס trigger של 10 µs, ואז ממתינה שפין ה-echo יחזיר פולס שרוחבו הוא זמן ההלוך-ושוב של הקול:
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)
הארגומנט השלישי הוא ה-timeout במיקרו-שניות, מוחל בנפרד על ”המתן שהפולס יתחיל“ ו“המתן שהפולס יסתיים“. ב-timeout הפונקציה מחזירה ערך שלילי המזהה איזו פאזה נכשלה: -2 אם הפולס מעולם לא התחיל, -1 אם התחיל אך מעולם לא הסתיים בתוך החלון.
שני חצאי ה-timeout חשובים עבור חיישנים אמיתיים. HC-SR04 יכול לקחת מילי-שנייה עד שתיים כדי להתחיל את ההד שלו, וההד עצמו יכול להיות באורך עשרות מילי-שניות עבור עצמים רחוקים. התאמת timeout_us לטווח המרבי הנדרש שומרת על המדידה תחומה.