3.26. אפיק CAN בקוד

machine.CAN עוטף בקר CAN חומרתי. הפעל אותו עם מזהה האפיק וקצב הביטים:

from machine import CAN

can = CAN(1, 500_000)
can.set_filters(None)        # accept all incoming IDs

קצב הביטים חייב להתאים בדיוק לכל צומת אחר על האפיק – 125_000, 250_000, 500_000 ו-1_000_000 הם הערכים הנפוצים. set_filters() מגדיר אילו מזהים הבקר יעביר; None משמעו לקבל הכול (שימושי בעת הפעלת קישור, פחות שימושי כשהאפיק עמוס).

3.26.1. בתוך הבקר

שלושה רכיבי חומרה יושבים בין קוד ה-Python של המצלמה לבין האפיק – תיבות הדואר של TX, מסנן קבלה, וה-RX FIFO. כל אחד ממופה לחלק מה-API של CAN שבשימוש להלן.

Block diagram of the CAN controller. On the TX side (top), can.send() drops frames into one of three TX mailboxes, and an arbitrate block picks which mailbox goes to the bus next. On the RX side (bottom), incoming frames pass through an acceptance filter; accepted frames land in the RX FIFO and can.recv() reads the FIFO from its oldest end.

תיבות הדואר של TX, מסנן הקבלה וה-RX FIFO של הבקר בין התוכנה לבין האפיק.

  • תיבות דואר של TX. קבוצה קטנה של חריצי חומרה (בדרך כלל שלושה) המחזיקים פריימים יוצאים שהמצלמה מסרה דרך send() אך שעדיין לא הגיעו לאפיק. כאשר האפיק פנוי, הבקר בוחר את תיבת הדואר עם המזהה בעל המספר הנמוך ביותר (העדיפות הגבוהה ביותר) ומבצע בוררות עבור האפיק בשמה. המצלמה אינה בוחרת את תיבת הדואר; הבקר מקצה אחת ומחזיר את האינדקס שלה מ-send().

  • מסנן קבלה. חומרה הניתנת להגדרה המשווה את המזהה של כל פריים נכנס לרשימת תבניות ומשליכה כל דבר שאינו תואם. פריימים שעוברים ממשיכים אל ה-RX FIFO; פריימים שנדחו מושלכים על ידי הבקר מבלי שיגיעו אי פעם ל-Python. set_filters() מגדיר את התבניות הללו.

  • RX FIFO. תור ראשון-נכנס-ראשון-יוצא (first-in, first-out) – הפריים הראשון שנכנס הוא גם הפריים הראשון שיוצא, כמו תור בדלפק כרטיסים. הבקר מצרף פריימים שהתקבלו לסוף התור עם הגעתם, ו-recv() מושך אותם מהחזית באותו סדר. התור חשוב מכיוון שהאפיק תופס פריימים ברקע בזמן ש-Python עסוק במקום אחר; המצלמה אז מרוקנת אותם אחד בכל פעם מבלי לאבד אף אחד, כל עוד ה-FIFO לא עבר את הקיבולת.

3.26.2. שליחת פריים

send() מציב פריים בתור לשידור:

can.send(0x123, b"\x01\x02\x03\x04")

הארגומנט הראשון הוא המזהה (מספר שלם בן 11 ביטים עבור פריים סטנדרטי); השני הוא המטען (0 עד 8 בייטים עבור CAN Classic). הקריאה מחזירה אינדקס מספרי שלם קטן המזהה את תיבת הדואר החומרתית שאליה נכנס הפריים. הבקר מבצע בוררות עם כל משדר אחר על האפיק ומשדר מחדש לפי הצורך ללא עזרה נוספת מהתוכנה.

עבור מזהים מורחבים (29 ביטים), בצע OR של הדגל CAN.FLAG_EXT_ID לתוך הארגומנט השלישי:

can.send(0x18FF1234, b"hello", CAN.FLAG_EXT_ID)

3.26.3. קבלת פריימים

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

can.set_filters(None)   # accept every frame

recv() מחזיר אז את הפריים הבא ב-FIFO של הקבלה, או None אם דבר אינו ממתין:

msg = can.recv()
if msg is not None:
    can_id, data, flags, errs = msg
    print("got", hex(can_id), bytes(data))

האפיק ממלא את ה-RX FIFO ברקע, כך שהלולאה הראשית פשוט מרוקנת אותו במהירות שבה היא מבצעת איטרציות. כל עוד ה-FIFO עמוק יותר מהמרווח הארוך ביותר בין ריקונים, אף פריים אינו אובד.

3.26.4. מסננים

אפיק אמיתי בדרך כלל עמוס בפריימים שהמצלמה אינה מתעניינת בהם. מסנני חומרה מאפשרים לבקר להשליך מזהים לא רצויים לפני שהם מגיעים ל-FIFO. set_filters() מקבל רשימה של טאפלים (id, mask, flags); פריים עובר את המסנן אם המזהה שלו, ממוסך על ידי mask, תואם את ה-id המוגדר:

# Accept only IDs 0x100 - 0x10F (mask off the bottom 4 bits)
can.set_filters(((0x100, 0x7F0, 0),))

# Accept IDs 0x300 and 0x700 exactly
can.set_filters(((0x300, 0x7FF, 0),
                 (0x700, 0x7FF, 0)))

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

3.26.5. מצבי שגיאה והתאוששות

אפיק CAN אמיתי קולט שגיאות שידור – קצרים להארקה, צמתים חסרים, רעש חשמלי המשבש ביטים. הבקר מחזיק שני מונים העוקבים אחר התנהגות זו: מונה שגיאות שידור (TEC) ומונה שגיאות קבלה (REC), שכל אחד מהם עולה כאשר הבקר מזהה שגיאה ויורד לאחר העברה מוצלחת. ערכי המונים מכניסים את הבקר לאחד מארבעה מצבים:

  • Error Active (גם TEC וגם REC מתחת ל-96). פעולה תקינה. כאשר הצומת מזהה שגיאת אפיק הוא משדר פריים שגיאה פעיל דומיננטי, אשר מאלץ כל צומת אחר להשליך את הפריים שבתהליך כך שהשולח יכול לנסות שוב.

  • Error Warning (אחד המונים מגיע ל-96). עדיין פעיל לחלוטין על האפיק – מצב האזהרה הוא איתות תוכנה שהשגיאות מצטברות, לא שינוי התנהגות.

  • Error Passive (אחד המונים מגיע ל-128). הצומת עדיין על האפיק אך מפסיק לשלוח פריימי שגיאה דומיננטיים; שגיאות מסומנות כעת באמצעות פריימי שגיאה פסיביים (רצסיביים) כך שצומת תקול אינו יכול להמשיך לקרוע את האפיק לכל היתר.

  • Bus Off (TEC מגיע ל-256). הבקר החליט שצומת זה אינו אמין מספיק כדי להשתתף. הוא מתנתק מהאפיק, מפסיק לשדר ולאשר, ונשאר בחוץ עד שהתוכנה מאתחלת אותו מחדש במפורש.

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

Bus Off הוא המצב היחיד הדורש פעולת תוכנה. restart() מאפס את הבקר ומחזיר אותו ל-Error Active. דפוס טיפוסי הוא לבדוק את המצב מהלולאה הראשית ולאתחל מחדש לאחר השהיה קצרה כדי לתת לאפיק זמן להתייצב:

import time
from machine import CAN

can = CAN(1, 500_000)
can.set_filters(None)

while True:
    if can.state() == CAN.STATE_BUS_OFF:
        time.sleep_ms(100)
        can.restart()
    # ... rest of the loop

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