תכנות מיקרו-בקרים בשפת MicroPython · פרק 8 — תקשורת טורית UART⌂📚🏠
פרק 8

תקשורת טורית UART

Serial Communication (UART)
3 שעות לימוד

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

בפרק הזה:
  • נסביר את המסגרת: סיבית התחלה, סיביות נתונים, סיבית זוגיות וסיבית עצירה, ונחשב את זמן הבית מקצב השידור.
  • נחבר שני לוחות נכון, ונבין למה צריך אדמה משותפת.
  • נשתמש ב-write, ב-read וב-any, ונרכיב שורות שמגיעות בחלקים.
  • נבנה פרוטוקול טקסט פשוט, עם בדיקת שגיאות ופסק זמן.
  • נסביר למה אי אפשר לחבר יציאת RS-232 ישירות ללוח, ולמה מודול HC-05 הוא UART מבחינת הקוד.

8.1 פרוטוקול וסנכרון (Serial and Asynchronous)

כדי להעביר בית אחד במקביל דרושים שמונה חוטים. בתקשורת טורית (Serial) שולחים את הסיביות זו אחר זו על חוט אחד. זה לוקח קצת יותר זמן, אבל החוטים מעטים בהרבה והכבל יכול להיות ארוך.

UART: מעגל בתוך הבקר שהופך בית לרצף סיביות בהדק השידור TX, ואוסף את הסיביות שמגיעות להדק הקליטה RX חזרה לבית. השם בעברית: משדר-מקלט אסינכרוני אוניברסלי.

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

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

8.2 מסגרת וזמן בית (The Frame)

בזמן שקט הקו גבוה. כדי שהמקלט ידע שבית מתחיל, המשדר מוריד את הקו למשך סיבית אחת: זו סיבית ההתחלה. אחריה באות סיביות הנתונים, הקלה ביותר ראשונה, ואז סיבית העצירה הגבוהה. כל זה יחד הוא המסגרת. הסרטוט מראה את הבית 0x41, כלומר את התו A.

TXidlestartD0D1D2D3D4D5D6D7stopidle
מסגרת התו A בתבנית 8N1: סיבית התחלה, שמונה סיביות שמתחילות בקלה ביותר, סיבית עצירה.

התבנית הנפוצה נכתבת 8N1: שמונה סיביות נתונים, בלי סיבית זוגיות, וסיבית עצירה אחד. לכן בית עולה על הקו 10 סיביות ולא 8.

זמן בית
tbyte = nframeB
סמלפירושיחידה
tbyteזמן השידור של בית אחד s
nframeמספר הסיביות במסגרת, 10 ב-8N1—
Bקצב השידור bit/s
דוגמה 8.1
כמה זמן לוקח לשדר בית אחד בקצב של 9600 bit/s? וכמה בתים משדרים בשנייה?
פתרון:
tbyte = nframeB
tbyte = 109600
tbyte = 1.042 ms

בכל שנייה משדרים לכל היותר 960 בתים, ולא 1200.

tbyte = 1.042 ms
קצב bit/sזמן סיבית µsזמן בית msבתים בשנייה
9600104.171.042960
1920052.080.5211920
3840026.040.2603840
5760017.360.1745760
1152008.680.08711520

איך החיבור שורד בלי שעון משותף? המקלט מתחיל לספור ברגע שסיבית ההתחלה יורדת, וקורא כל סיבית מאמצעו. הקריאה האחרונה במסגרת נופלת 9.5 סיביות אחרי ההתחלה, ומותר לתזמון לסטות בחצי סיבית לפני שהקריאה נופלת על הסיבית הלא נכונה. לכן ההפרש התאורטי הגדול ביותר בין שני השעונים הוא 5.3 אחוזים. זה גבול תאורטי, והמרווח בפועל קטן ממנו, אבל הוא מסביר למה מספיק שעון לא מדויק במיוחד, ולמה מסגרת ארוכה בלי סיבית התחלה חדשה לא תעבוד.

סיבית הזוגיות היא אופציונלית. מוסיפים אותו אחרי הנתונים כדי לגלות שגיאה בסיבית אחת. בזוגיות זוגית סכום האחדות, יחד עם סיבית הזוגיות, הוא מספר זוגי: בבית 0x41 יש שתי אחדות, ולכן סיבית הזוגיות היא 0. בבית 0x43 יש שלוש אחדות, ולכן סיבית הזוגיות היא 1. במיקרו-פייתון מבקשים זוגיות זוגית כך: uart.init(9600, parity=0), ואי-זוגית כך: parity=1.

שאלה 8.1: בקצב של 9600 bit/s ובתבנית 8E1, כלומר עם סיבית זוגיות: כמה זמן לוקח בית? וכמה בתים בשנייה לכל היותר?

8.3 חיבור בין שני בקרים (Wiring)

הדק השידור של צד אחד נכנס להדק הקליטה של הצד השני. החוט השלישי הוא האדמה: מתח נמדד ביחס לייחוס, ובלי ייחוס משותף 3.3 V לא אומר דבר לצד השני.

ESP32Pico WTXTXRXRXGNDGND
חיבור שני לוחות: TX אל RX בשני הכיוונים, ואדמה משותפת.
טעות נפוצה: התקלה הנפוצה ביותר: חיבור TX אל TX. בדרך כלל שום דבר לא נשרף, אבל שום דבר גם לא מגיע. התקלה השנייה: שכחנו את האדמה, ומופיעים תווים אקראיים. קצב שונה בין שני הצדדים נותן את אותם תסמינים.
אזהרה: שני הלוחות עובדים ב-3.3 V. אין לחבר התקן שהמוצא שלו 5 V ישירות להדק RX: המגבלה המוחלטת להדק של ESP32 היא 3.6 V לפי תיעוד מיקרו-פייתון. צריך מחלק מתח או ממיר רמות.

מה לגבי המרחק? מתחי הבקר חלשים מול רעש, ולכן נשארים בגבולות השולחן עם חוטים קצרים, וככל שהקצב עולה כך המרחק האפשרי קטן. למרחקים ארוכים יותר ממירים את המתחים לתקן אחר, כפי שנראה בסעיף 8.7.

8.4 פקודות UART

ESP32Pico W / Pico 2 W
מספר יחידות UART3: UART0, UART1 ו-UART22: UART0 ו-UART1
שורת הפקודות REPLעל UART0, ולכן לא נשתמש בהעל USB
מה ש-board.uart() נותןUART2, TX 17, RX 16UART1, TX GP8, RX GP9
חופש בחירת ההדקכל הדק ל-TX, ול-RX גם 34–39קבוצה קבועה לכל יחידה
מן הקובץ board.py ב-ESP32
# board.py - ESP32 DevKit
from machine import ADC, I2C, PWM, SPI, UART, Pin, Timer

TX = 17
RX = 16


def uart(baudrate=9600):
    return UART(2, baudrate, tx=Pin(TX), rx=Pin(RX))

תיעוד מיקרו-פייתון מציין שהדקים 17 ו-16, ברירת המחדל, עלולים להיות תפוסים בלוחות מסוימים, כי הם מתנגשים עם זיכרון ה-PSRAM שמורכב עליהם. בלוח כזה משנים את שני מספרי ההדקים ב-board.py בלבד, ואף שורה בתוכניות לא משתנה. זו בדיוק הסיבה שהקובץ הזה קיים. לקליטה בוחרים הדק כמו 36, שהוא כניסה בלבד, ולשידור הדק שלא נמצא בשימוש בפרויקט. הקטע הבא אינו מורץ בבניית הספר:

השינוי ב-board.py בלוח עם PSRAM
TX = 25
RX = 36

לפני שמחברים התקן כלשהו, בודקים את היחידה לבדה: מחברים את TX אל RX באותו לוח בחוט אחד. כל מה ששולחים חוזר אלינו. הבדיקה הזאת מפרידה בין בעיה בקוד לבין בעיה בהתקן השני.

בדיקת לולאה: חוט מ-TX אל RX.
import board
import time

uart = board.uart(9600)
print("sent:", uart.write(b"hello\n"))
time.sleep(0.1)
print("waiting:", uart.any())
print("received:", uart.read())
print("empty:", uart.read())
ESP32 + Pico W · MicroPython 1.29 · simulated board
sent: 6
waiting: 6
received: b'hello\n'
empty: None
פקודהמה היא עושה
uart = board.uart(9600)יוצר את היחידה בקצב הזה, והתבנית ברירת המחדל היא 8N1.
uart.write(b"hello\n")שולח בתים ומחזיר את מספרם. לא מקבל מספר: שולחים טקסט או bytes.
uart.any()האם הגיע משהו? אפס פירושו שלא.
uart.read()לוקח את כל מה שהגיע, ומחזיר None אם לא הגיע דבר.
הערה: לפי התיעוד, ב-any() ייתכן שיוחזר 1 גם כשיש יותר מבית אחד, בלוחות מסוימים. לכן מתייחסים אליו כאל תשובה של כן או לא ולא בונים על המספר עצמו: קוראים ב-read() את כל מה שהגיע.

8.5 קליטת שורות (Receiving Lines)

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

שתי השורות מגיעות בשלושה חלקים, ובחלק השני יש סוף שורה והתחלה של שורה. ההתקן המשדר כאן מדומה.
import board
import time

uart = board.uart(9600)
buf = b""

while True:
    data = uart.read()
    if data:
        buf += data
        print("buffer:", buf)
        while b"\n" in buf:
            line, buf = buf.split(b"\n", 1)
            print("line:", line.decode())
    time.sleep(0.05)
ESP32 + Pico W · MicroPython 1.29 · simulated board
buffer: b'TEM'
buffer: b'TEMP 24\nHU'
line: TEMP 24
buffer: b'HUM 60\n'
line: HUM 60
...
טעות נפוצה: הפקודה uart.readline() נראית פשוטה יותר. אבל לפי התיעוד היא עלולה לחזור לפני סוף השורה אם פסק הזמן פג, ובפסק הזמן ברירת המחדל, אפס, אפשר לקבל חצי שורה. לכן כותבים את החוצץ בעצמנו: הוא לא תוקע את התוכנית ולא חותך משפט. בספר ההדמיה אינה מראה את ההתנהגות הזאת.
שאלה 8.2: בתוכנית הקודמת, למה השתמשנו ב-while בתוך if, ולא ב-if שני?

8.6 פרוטוקול טקסט פשוט (A Text Protocol)

פרוטוקול הוא הסכמה על צורת ההודעות. ההסכמה הפשוטה ביותר: כל הודעה היא שורת טקסט. קל לקרוא אותה בעין וקל לנסות אותה מן המחשב. הנה לוח שמבצע שתי פקודות ועונה על כל שורה:

הלוח המקבל. השורות שמגיעות מדומות, ומה שמופיע תחת [uart_tx] הוא מה שיצא על החוט.
from machine import Pin
import board
import time

led = Pin(board.LED, Pin.OUT)
uart = board.uart(9600)
buf = b""


def handle(line):
    if line == b"ON":
        led.on()
        return b"OK ON\n"
    if line == b"OFF":
        led.off()
        return b"OK OFF\n"
    return b"ERR\n"


while True:
    data = uart.read()
    if data:
        buf += data
        while b"\n" in buf:
            line, buf = buf.split(b"\n", 1)
            answer = handle(line.strip())
            uart.write(answer)
            print(line, "->", answer, "LED =", led.value())
    time.sleep(0.01)
ESP32 + Pico W · MicroPython 1.29 · simulated board
b'ON' -> b'OK ON\n' LED = 1
b'BLINK' -> b'ERR\n' LED = 1
b'OFF\r' -> b'OK OFF\n' LED = 0
[uart_tx]
b'OK ON\n'
b'ERR\n'
b'OK OFF\n'
...

שימו לב ל-line.strip(): תוכנות מסוימות במחשב מסיימות את השורה בשני הבתים \r\n, ובלי הניקוי b"OFF" לא יתאים.

מה אם סיבית נפגעת בדרך? ON הופך למשהו אחר, ואולי נקבל במקרה פקודה תקינה אחרת. לכן מוסיפים לכל שורה סכום ביקורת: מספר שהמשדר מחשב מן הטקסט, והמקלט מחשב מחדש ומשווה. ניקח את החישוב הפשוט ביותר, XOR של כל התווים. זה מספיק כדי לגלות כל שגיאה בסיבית אחת, ונכתוב אותו בספרייה קטנה ששני הצדדים משתמשים בה:

הספרייה textlink.py, בשני הלוחות
# textlink.py - one line of text with a check, for example "LED,1*5A\n".
# The two hex digits are the XOR of all the characters before the star.
# (c) 2026 Fahed Ghanem - MIT License - fahed-lab.com


def checksum(text):
    total = 0
    for ch in text:
        total ^= ord(ch)
    return total


def pack(text):
    return ("%s*%02X\n" % (text, checksum(text))).encode()


def unpack(line):
    try:
        text, check = line.decode().strip().split("*")
        if int(check, 16) == checksum(text):
            return text
    except ValueError:
        pass
    return None
דוגמה 8.2
איזו שורה שולח המשדר עבור הטקסט LED,1? וכמה זמן זה לוקח בקצב של 9600 bit/s?
פתרון:

מחשבים XOR של חמשת התווים ומקבלים 0x50, ולכן השורה היא LED,1*50 ועוד סימן סוף שורה, כלומר 9 בתים.

t = N · tbyte
t = 9 · 1.042
t = 9.38 ms
t = 9.38 ms
המקלט: בשורה השנייה סכום הביקורת שגוי, ובשלישית אין סכום, ולכן הוא לא מבצע אף אחת מהן.
from machine import Pin
from textlink import pack, unpack
import board
import time

led = Pin(board.LED, Pin.OUT)
uart = board.uart(9600)
buf = b""

while True:
    data = uart.read()
    if data:
        buf += data
        while b"\n" in buf:
            line, buf = buf.split(b"\n", 1)
            text = unpack(line)
            if text == "LED,1":
                led.on()
            elif text == "LED,0":
                led.off()
            uart.write(pack("ACK" if text else "NAK"))
            print(line, "->", text, "LED =", led.value())
    time.sleep(0.01)
ESP32 + Pico W · MicroPython 1.29 · simulated board
b'LED,1*50' -> LED,1 LED = 1
b'LED,0*00' -> None LED = 1
b'garbage' -> None LED = 1
b'LED,0*51' -> LED,0 LED = 0
[uart_tx]
b'ACK*49\n'
b'NAK*44\n'
b'NAK*44\n'
b'ACK*49\n'
...

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

המשדר. התשובה הראשונה תקינה, השנייה פגומה, והשלישית לא מגיעה. ask מחזיר None בשני המקרים האחרונים.
from textlink import pack, unpack
import board
import time

uart = board.uart(9600)


def ask(text, timeout_ms=500):
    uart.write(pack(text))
    start = time.ticks_ms()
    buf = b""
    while time.ticks_diff(time.ticks_ms(), start) < timeout_ms:
        data = uart.read()
        if data:
            buf += data
            if b"\n" in buf:
                return unpack(buf.split(b"\n", 1)[0])
        time.sleep_ms(10)
    return None


for command in ("LED,1", "LED,0", "LED,1"):
    print(command, "->", ask(command))
    time.sleep(1)
ESP32 + Pico W · MicroPython 1.29 · simulated board
LED,1 -> ACK
LED,0 -> None
LED,1 -> None
הערה: על השולחן טוענים את שתי התוכניות לשני לוחות מחוברים, כמו בסעיף 8.3. בספר הרצנו כל תוכנית לבדה מול צד שני מדומה.

8.7 תקן RS-232 (RS-232)

היציאה הטורית בהתקנים תעשייתיים רבים פועלת לפי תקן RS-232. המסגרת זהה לחלוטין, אבל המתחים שונים: תחום רחב, חיובי ושלילי, והלוגיקה הפוכה. מתח שלילי הוא 1 ומתח חיובי הוא 0. המתח הגדול עמיד בפני רעש ומאפשר כבל באורך של כמה מטרים.

+15 V+3 V0 V-3 V-15 VUART 3.3 V1 = 3.3 V0 = 0 VRS-23201לא מוגדר
מפלסי הקלט ב-RS-232 בהשוואה להדק הלוח.
אזהרה: לעולם לא מחברים יציאת RS-232 להדק של הלוח: המתח יכול להגיע לעשרה וולט ויותר, וגם שלילי, וזה הורס את ההדק מיד. ביניהם מציבים שבב המרת רמות כמו MAX3232, שעובד ב-3.3 V, מייצר את המתחים החיוביים והשליליים והופך את הלוגיקה. הקוד לא משתנה באות אחת.

8.8 מודול Bluetooth קלאסי (Classic Bluetooth Modules)

המודולים HC-05 ו-HC-06 מממשים בתוכם Bluetooth קלאסי, ומופיעים לבקר כהתקן UART רגיל: מה שמגיע מהטלפון יוצא בהדק TX של המודול. לכן התוכנית uart_cmd.py עצמה הופכת לשליטה מהטלפון בלי לשנות שורה, וכל ההבדל הוא בחוט. אנחנו זקוקים למודול הזה כי המודול bluetooth במיקרו-פייתון תומך ב-Bluetooth בצריכה נמוכה, נושא פרק 17, ולא בקלאסי.

ESP32 / Pico WHC-05TXTXRXRXGNDGND
המודול מחובר כמו כל התקן UART. הדק ההזנה לפי הכתוב על המודול.

קצב ברירת המחדל ברוב המודולים האלה הוא 9600, וקוד הצימוד הנפוץ הוא 1234. אלה ערכי יצרן אופייניים, ובודקים אותם בגיליון הנתונים של המודול שבידינו. ההבדל ביניהם: HC-06 הוא עבד בלבד, ואילו את HC-05 אפשר להגדיר כך שיפתח את החיבור בעצמו.

פעילות: חברו מודול HC-05 וטענו את uart_cmd.py. אחר כך שלחו מהטלפון, בכל אפליקציית טרמינל ל-Bluetooth, את שתי השורות ON ו-OFF. זכרו להגדיר באפליקציה שתשלח סוף שורה.

8.9 ב-Raspberry Pi 4/5

ב-Raspberry Pi אין machine, והיציאה היא קובץ במערכת. פותחים אותה בספרייה pyserial, שמתקינים בפקודה pip install pyserial. קודם מפעילים את היציאה מ-sudo raspi-config (בחלק Interface Options). שם היציאה עשוי להשתנות בין הדגמים, ולכן בודקים בתיעוד של Raspberry Pi. גם מבחינת החוטים, המתחים ב-Pi הם 3.3 V. הקטע הבא לא הורץ בבניית הספר:

ה-Pi שואל את הלוח uart_cmd.py ומדפיס את תשובתו
import serial

port = serial.Serial("/dev/serial0", 9600, timeout=1)
port.write(b"ON\n")
print(port.readline())
port.close()

עיקרי הפרק:

סיכום הכללים בפרק:

הכללהנוסחה
זמן ביתtbyte = nframeB

פקודות הפרק:

פקודהמה היא עושה
board.uart(baud)היחידה וההדקים המתאימים ללוח.
uart.write(data)שולח, ומחזיר את מספר הבתים.
uart.any()האם יש משהו בחוצץ?
uart.read()כל מה שהגיע, או None.
uart.readline()שורה, ועלול להחזיר שורה חסרה כשפסק הזמן פג.
uart.init(9600, parity=0)משנה הגדרות, וכאן זוגיות זוגית.
buf.split(b"\n", 1)מפצל את החוצץ בסוף השורה הראשון.
serial.Serial(port, 9600, timeout=1)פתיחת היציאה ב-Pi.

מילון מונחים:

עבריתEnglishערבית
תקשורת טוריתSerial Communicationالاتصال التسلسلي
אסינכרוניAsynchronousغير متزامن
קצב שידורBaud Rateمعدّل الإرسال
מסגרתFrameالإطار
סיבית התחלהStart Bitبتّ البداية
סיבית עצירהStop Bitبتّ التوقف
סיבית זוגיותParity Bitبتّ التماثل
דו-כיווני מלאFull Duplexثنائي الاتجاه الكامل
ממיר רמותLevel Shifterمحوّل المستويات
פרוטוקולProtocolالبروتوكول
חוצץBufferالمخزن
סכום ביקורתChecksumمجموع الفحص
פסק זמןTimeoutالمهلة
התשובות הסופיות לשאלות הפרק
8.1 t = 119600 = 1.146 ms, וכ-872 בתים בשנייה.
8.2 כי חלק אחד יכול להכיל יותר משורה אחת. עם if לוקחים שורה אחת, והאחרת נשארת בחוצץ עד שמגיע משהו חדש.

שאלות לתרגול:

רמה ראשונה — בסיס
1.
כמה זמן לוקח בית אחד בתבנית 8N1 בקצב של 115200 bit/s?
תשובות: t = 10115200 = 86.8 µs.
2.
כתבו את סדר הסיביות על הקו, משמאל לימין בציר הזמן, עבור הבית 0x35 בתבנית 8N1.
תשובות: התחלה 0, אחר כך 1 0 1 0 1 1 0 0, ואז עצירה 1.
3.
לוח משדר ולא מגיע דבר. מנו שלושה דברים שבודקים לפי הסדר.
תשובות: בדיקת לולאה באותו לוח; אחר כך חיבור TX ו-RX והאדמה; ואז שהקצב והמסגרת זהים בשני הצדדים.
רמה שנייה — יישום
4.
רוצים להעביר 2000 בתים בשנייה בתבנית 8N1. מהו הקצב הקטן ביותר מן הטבלה שבסעיף 8.2 שמספיק?
תשובות: דרושים 2000 · 10 = 20000 bit/s, ולכן הקצב הקטן ביותר הוא 38400 bit/s.
5.
כתבו תוכנית שמקבלת מספרים, כל מספר בשורה, ומדפיסה את סכומם אחרי כל שורה. מה היא מדפיסה אם הגיעו 3 ואחריו 4?
תשובות:
import board
import time

uart = board.uart(9600)
buf = b""
total = 0

while True:
    data = uart.read()
    if data:
        buf += data
        while b"\n" in buf:
            line, buf = buf.split(b"\n", 1)
            total += int(line)
            print("sum =", total)
    time.sleep(0.01)
מודפס: sum = 3 , sum = 7
6.
חשבו את סכום הביקורת של הטקסט OK, וכתבו את השורה המלאה.
תשובות: התוצאה 0x04, והשורה OK*04.
רמה שלישית — העמקה
7.
(א) תנו דוגמה לשתי שגיאות בשורה ש-XOR לא מגלה.
(ב) מה הפתרון אם הקו רועש מאוד?
תשובות: (א) שתי סיביות באותו מקום שמתהפכות בשני תווים שונים, כמו הסיבית 0 בשני תווים: כל אחת מבטלת את השנייה ב-XOR. (ב) סכום ביקורת ארוך יותר כמו CRC, או שידור חוזר ב-NAK.
8.
כתבו את הפונקציה ask_retry שמשתמשת ב-ask ומנסה עד שלוש פעמים.
תשובות:
def ask_retry(text, tries=3):
    for i in range(tries):
        answer = ask(text)
        if answer == "ACK":
            return True
        print("try", i + 1, "failed")
    return False
9.
חבר לכיתה חיבר את TX של התקן RS-232 ישירות להדק RX של הלוח, וההדק הפסיק לעבוד. הסבירו, ותארו במילים את החיבור הנכון.
תשובות: המתח על הקו חורג מהמגבלה המוחלטת של ההדק בהרבה, והוא גם שלילי. נכון: יציאת RS-232 אל צד ה-RS-232 של MAX3232, וצד ה-TTL של השבב אל הלוח, עם אדמה משותפת.
נוסחאון הספר: כל נוסחאות הפרק הזה מרוכזות, עם סרטוטיהן וטבלאות הסמלים שלהן, בפרק 25 — נוסחאון הספר.