برمجة المتحكّمات بلغة مايكروبايثون · الفصل 8 — الاتصال التسلسلي UARTתקשורת טורית UART · תכנות מיקרו-בקרים בשפת MicroPython⌂📚🏠
الفصل 8

الاتصال التسلسلي UART

תקשורת טורית UART – Serial Communication (UART)
3 ساعات تعليمية

حتى الآن كلّم المتحكّم أطرافا تعرف حالتين: مرتفع ومنخفض. لكن بعض الأجهزة تريد جملا كاملة: متحكّم آخر، وحدة بلوتوث، حاسوب. في هذا الفصل نرسل البايتات بتّا بعد بتّ على سلك واحد، بلا ساعة مشتركة، ونرى كيف يفهم الطرف الآخر أين يبدأ كل بايت، وماذا نفعل حين يصل نصف جملة أو تصل جملة تالفة.

في هذا الفصل:
  • نشرح الإطار: بتّ البداية والبيانات والتماثل والتوقف، ونحسب زمن البايت من معدّل الإرسال.
  • نوصل لوحتين توصيلا صحيحا، ونعرف لماذا نحتاج إلى أرضي مشترك.
  • نستعمل write وread وany، ونجمع الأسطر من أجزاء متفرّقة.
  • نبني بروتوكولا نصّيا بسيطا فيه فحص للأخطاء ومهلة انتظار.
  • نشرح لماذا لا يوصل منفذ RS-232 باللوحة مباشرة، ولماذا وحدة HC-05 هي UART من جهة الشيفرة.

8.1 لماذا تسلسلي؟ ولماذا بلا ساعة؟ – פרוטוקול וסנכרון (Serial and Asynchronous)

لنقل بايت واحد بالتوازي نحتاج إلى ثمانية أسلاك. أما الاتصال التسلسلي (תקשורת טורית) فيرسل البتّات واحدا بعد الآخر على سلك واحد. الزمن أطول قليلا، وعدد الأسلاك أقل كثيرا، والكابل يمكن أن يطول.

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 في مايكروبايثون – פקודות 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)

المنفذ التسلسلي في أجهزة صناعية كثيرة يتبع معيار 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 وحدة بلوتوث هي UART من جهة الشيفرة – מודול Bluetooth קלאסי (Classic Bluetooth Modules)

الوحدتان HC-05 وHC-06 تنفّذان بلوتوث الكلاسيكي داخلهما، وتظهران للمتحكّم كجهاز UART عادي: ما يصل من الهاتف يخرج على طرف TX في الوحدة. فالبرنامج uart_cmd.py نفسه يصبح تحكّما من الهاتف دون تغيير سطر، والفرق كله في السلك. ونحتاج إلى هذه الوحدة لأن وحدة bluetooth في مايكروبايثون تدعم البلوتوث منخفض الطاقة، وهو موضوع الفصل 17، وليس الكلاسيكي.

ESP32 / Pico WHC-05TXTXRXRXGNDGND
الوحدة موصولة كأي جهاز UART. وطرف التغذية حسب ما هو مكتوب على الوحدة.

المعدّل الافتراضي في أغلب هذه الوحدات هو 9600، ورمز الاقتران الشائع 1234. هذه قيم مصنع نموذجية، ونتحقّق منها في ورقة بيانات الوحدة التي بأيدينا. والفرق بينهما: HC-06 تابع فقط، أما HC-05 فيمكن ضبطها لتبدأ الاتصال بنفسها.

نشاط: وصّلوا وحدة HC-05 وحمّلوا uart_cmd.py. ثم أرسلوا من الهاتف، عبر أي تطبيق طرفية بلوتوث، السطرين ON وOFF. وتذكّروا أن تضبطوا التطبيق ليرسل نهاية السطر.

8.9 على Raspberry Pi 4/5 – ב-Raspberry Pi

على الباي لا توجد machine، بل المنفذ ملف في النظام، ونفتحه بالمكتبة pyserial التي تثبّت بالأمر pip install pyserial. ونفعّل المنفذ أولا من sudo raspi-config (قسم Interface Options). اسم المنفذ قد يختلف بين الطرازات، فنراجع توثيق Raspberry Pi. ومن جهة الأسلاك جهود الباي أيضا 3.3 V. والمقطع التالي لم يشغَّل في بناء الكتاب:

الباي يسأل اللوحة 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)فتح المنفذ على الباي.

قاموس المصطلحات – מילון מונחים:

بالعربيةبالعبرية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 — لوحة القوانين الشاملة. (נוסחאון הספר)