3.19. UART בקוד

machine.UART עוטף ערוץ UART חומרתי אחד. בנה אחד עם מזהה האפיק ו-baud rate; לכל השאר יש ברירות מחדל סבירות:

from machine import UART

uart = UART(3, baudrate=115200)

ה-id בוחר באיזה UART חומרתי להשתמש; הערך תלוי בלוח (ראה את לוחות OpenMV עבור מספרי האפיק הזמינים והקצאות הפינים במצלמה נתונה). פורמט הפריים של ברירת המחדל הוא 8 ביטי נתונים, ללא זוגיות, ביט עצירה אחד – ה-”8N1“ שכולם מצפים לו.

3.19.1. כתיבה וקריאה

פלט UART עובר דרך write():

uart.write("hello\n")
uart.write(b"\x01\x02\x03")

write מקבל או str (מקודד כ-UTF-8) או bytes / bytearray. הוא חוזר מיד ברגע שהבתים נכנסו לתור בחוצץ ה-TX; החומרה מסיימת להוציא אותם ברקע.

קריאות מתרחשות דרך שלוש מתודות, בהתאם למה שנדרש:

n = uart.any()              # bytes available to read right now
data = uart.read(8)         # up to 8 bytes, or None on timeout
line = uart.readline()      # bytes ending in '\n', or None on timeout

any() בודק את חוצץ ה-RX ללא חסימה. read() קורא מספר קבוע של בתים, ומחזיר None אם פג הזמן הקצוב (ניתן להגדרה דרך הארגומנט timeout בבנאי) תחילה. readline() קורא עד וכולל השורה החדשה הבאה, שימושי עבור פרוטוקולים מבוססי-שורות.

לולאה פשוטה שמהדהדת כל מה שהיא מקבלת:

uart = UART(3, baudrate=115200, timeout=100)

while True:
    if uart.any():
        data = uart.read()
        uart.write(data)

read() ללא ארגומנט אורך ממשיך לקרוא בתים עד שקו הקליטה נשאר שקט למשך ה-timeout המוגדר, ואז מחזיר את כל מה שנצבר. עם timeout=100, כל קריאה כאן מחזירה מקבץ אחד של בתים – כל מה שהשולח הוציא ללא רווח של 100 ms בין בתים. ללא timeout לא היה לקריאה שום סימן שהשולח סיים, והיא עלולה להיתקע ללא הגבלת זמן.

3.19.2. נתונים בינאריים עם struct

שליחת מספרים שלמים ומספרים עשרוניים על הקו היא בדיוק מה שהמודול struct נועד לו. הוא אורז ערכים ברוחב קבוע לאובייקט bytes באמצעות מחרוזת פורמט שמציינת את סדר הבתים ואת סוג כל שדה:

import struct

uart.write(struct.pack("<lhb", count, temperature_x100, status))

ה-"<" המוביל בוחר סדר בתים little-endian; "l" הוא מספר שלם חתום של 32 ביט, "h" הוא מספר שלם חתום של 16 ביט, "b" הוא מספר שלם חתום של 8 ביט. הצד השני מפרק עם אותה מחרוזת פורמט:

payload = uart.read(7)        # 4 + 2 + 1 = 7 bytes
count, temperature_x100, status = struct.unpack("<lhb", payload)

מחרוזת הפורמט היא החוזה בין שני הקצוות. כל אי-התאמה – סדר בתים שגוי, גדלי סוגים שגויים, סדר שדות שגוי – מייצרת ערכים חסרי משמעות.

3.19.3. חציצה וקריאות בקצב גבוה

לולאת תשאול שקוראת ל-any() ול-read() עומדת בקצב של רוב התעבורה בכוחות עצמה, כל עוד הלולאה הראשית חוזרת מהר מספיק כדי לרוקן את חוצץ הקליטה לפני שהוא מתמלא. שתי אפשרויות בנאי חשובות כשהקצב עולה או כשהלולאה עסוקה.

rxbuf קובע את גודל חוצץ ה-RX התוכנתי. ברירת המחדל היא כמה מאות בתים; עבור חיישנים הפולטים מאות בתים במקבץ, או כשהלולאה הראשית מבצעת עבודה ארוכה בין תשאולים, הגדלת החוצץ מונעת מבתים נכנסים מליפול בזמן שהלולאה עסוקה במקום אחר:

uart = UART(3, baudrate=115200, timeout=100, rxbuf=4096)

כל מה שמגיע בזמן שהחוצץ מלא אובד; קבע את rxbuf כדי לכסות את הרווח הארוך ביותר בין ריקונים.

עבור קצבים גבוהים מתמשכים, readinto() קורא לתוך חוצץ שהוקצה מראש במקום להחזיר אובייקט bytes חדש בכל קריאה:

buf = bytearray(256)

while True:
    n = uart.readinto(buf)
    if n:
        process(buf, n)

לא מתרחשת הקצאה במסלול הקריאה, מה שחשוב כשה-heap מקוטע או כשהשהיית ההקצאה הייתה אחרת דוחפת מעבר לתקציב התזמון בין-הבתים.