class CAN – אפיק תקשורת רשת בקרים (controller area network)

CAN מממש תמיכה הן ב-CAN קלאסי (bxCAN, בשימוש ב-OpenMV Cam M4 ו-M7) והן ב-CAN FD (FDCAN, בשימוש ב-OpenMV Cam H7, H7 Plus ו-Pure Thermal). ברמה הפיזית אפיק ה-CAN מורכב משני קווים, RX ו-TX. כדי לחבר OpenMV Cam לאפיק CAN עליך להשתמש במשדר-מקלט CAN (transceiver) כדי להמיר את אותות הלוגיקה של ה-CAN מה-MCU לרמות המתח הנכונות באפיק.

CAN קלאסי במצב לולאה חוזרת (loopback, ללא transceiver):

from pyb import CAN

can = CAN(1, CAN.LOOPBACK)

# Accept messages with id 123, 124, 125 or 126.
can.setfilter(0, CAN.LIST16, 0, (123, 124, 125, 126))

can.send("message!", 123)   # send a message with id 123
can.recv(0)                 # receive a message on FIFO 0

CAN FD עם כל המאפיינים האופציונליים מופעלים (FD frame, מיתוג קצב סיביות, מזהי פריים מורחבים; שלב arbitration של 500 kbit/s, שלב נתונים של 1 Mbit/s):

from pyb import CAN

can = CAN(
    1,
    CAN.NORMAL,
    baudrate=500_000,
    brs_baudrate=1_000_000,
    sample_point=80,
)

# Accept any id in the range 0xFFF0 .. 0xFFFF.
can.setfilter(0, CAN.RANGE, 0, (0xFFF0, 0xFFFF))

can.send(b"a" * 64, 0xFFFF, fdf=True, brs=True, extframe=True)
can.recv(0)

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

בנאים (Constructors)

class pyb.CAN(bus: int | str, *args, **kwargs)

בונה אובייקט CAN על ה-bus הנתון (אינדקס התקן היקפי מסוג מספר שלם, למשל 1 עבור CAN1, 2 עבור CAN2). ללא פרמטרים נוספים האובייקט נוצר אך אינו מאותחל (הוא שומר על הגדרות האפיק הקודמות, אם קיימות); אם ניתנים ארגומנטים נוספים האפיק מאותחל. ראה CAN.init() עבור הפרמטרים הזמינים.

CAN(2) מחווט לאותם פיני מחבר (header) בכל OpenMV Cam שחושף את pyb.CAN (M4 / M7 / H7 / H7 Plus / Pure Thermal):

אות

פין מחבר

הערות

RX

P3

TX

P2

ההתקן ההיקפי CAN מספק אותות ברמת לוגיקה בלבד; נדרש משדר-מקלט CAN (transceiver) חיצוני כדי להניע אפיק CAN אמיתי.

pyb.CAN אינו זמין ב-OpenMV Cam N6.

שיטות (Methods)

init(mode: int, prescaler: int = 100, *, sjw: int = 1, bs1: int = 6, bs2: int = 8, auto_restart: bool = False, baudrate: int = 0, sample_point: int = 75, num_filter_banks: int = 14, brs_sjw: int = 1, brs_bs1: int = 8, brs_bs2: int = 3, brs_baudrate: int = 0, brs_sample_point: int = 75) None

מאתחל את אפיק ה-CAN עם הפרמטרים הנתונים:

  • mode הוא אחד מבין: NORMAL, LOOPBACK, SILENT, SILENT_LOOPBACK

  • prescaler הוא הערך שבו מחולק שעון הכניסה של ה-CAN כדי לייצר את קוונטות זמן הסיבית הנומינלי. ה-prescaler יכול להיות ערך בין 1 ל-1024 כולל עבור CAN קלאסי, ובין 1 ל-512 כולל עבור CAN FD.

  • sjw הוא רוחב קפיצת הסנכרון מחדש ביחידות של קוונטות זמן עבור סיביות נומינליות; הוא יכול להיות ערך בין 1 ל-4 כולל עבור CAN קלאסי, ובין 1 ל-128 כולל עבור CAN FD.

  • bs1 מגדיר את מיקום נקודת הדגימה ביחידות של קוונטות הזמן עבור סיביות נומינליות; הוא יכול להיות ערך בין 1 ל-16 כולל עבור CAN קלאסי, ובין 2 ל-256 כולל עבור CAN FD.

  • bs2 מגדיר את מיקום נקודת השידור ביחידות של קוונטות הזמן עבור סיביות נומינליות; הוא יכול להיות ערך בין 1 ל-8 כולל עבור CAN קלאסי, ובין 2 ל-128 כולל עבור CAN FD.

  • auto_restart קובע האם הבקר ינסה אוטומטית להפעיל מחדש את התקשורת לאחר כניסה למצב bus-off; אם זה מושבת אז ניתן להשתמש ב-restart() כדי לצאת ממצב ה-bus-off

  • baudrate אם מסופק baudrate שונה מ-0, פונקציה זו תנסה לחשב אוטומטית את זמן הסיבית הנומינלי של ה-CAN (תוך עקיפה של prescaler, bs1 ו-bs2) שמספק הן את ה-baudrate (בתחום של .1%) והן את ה-sample_point הרצוי (לאחוז ה-1% הקרוב ביותר). לשליטה מדויקת יותר על תזמון ה-CAN, הגדר ישירות את הפרמטרים prescaler, bs1 ו-bs2.

  • sample_point מציין את מיקום דגימת הסיבית ביחס לכל זמן הסיבית הנומינלי, מבוטא כאחוז שלם מזמן הסיבית הנומינלי. ברירת המחדל של sample_point היא 75%. פרמטר זה מתעלם אלא אם baudrate מוגדר.

  • num_filter_banks עבור CAN קלאסי, זהו מספר הבנקים שיוקצו ל-CAN(1), והשאר מתוך ה-28 יוקצו ל-CAN(2).

הפרמטרים הנותרים קיימים רק בלוחות עם תמיכת CAN FD, ומגדירים את המאפיין האופציונלי Bit Rate Switch (BRS) של CAN FD:

  • brs_prescaler הוא הערך שבו מחולק שעון הכניסה של ה-CAN FD כדי לייצר את קוונטות זמן סיבית הנתונים. ה-prescaler יכול להיות ערך בין 1 ל-32 כולל.

  • brs_sjw הוא רוחב קפיצת הסנכרון מחדש ביחידות של קוונטות זמן עבור סיביות נתונים; הוא יכול להיות ערך בין 1 ל-16 כולל

  • brs_bs1 מגדיר את מיקום נקודת הדגימה ביחידות של קוונטות הזמן עבור סיביות נתונים; הוא יכול להיות ערך בין 1 ל-32 כולל

  • brs_bs2 מגדיר את מיקום נקודת השידור ביחידות של קוונטות הזמן עבור סיביות נתונים; הוא יכול להיות ערך בין 1 ל-16 כולל

  • brs_baudrate אם מסופק baudrate שונה מ-0, פונקציה זו תנסה לחשב אוטומטית את זמן סיבית הנתונים של ה-CAN (תוך עקיפה של brs_prescaler, brs_bs1 ו-brs_bs2) שמספק הן את ה-brs_baudrate (בתחום של .1%) והן את ה-brs_sample_point הרצוי (לאחוז ה-1% הקרוב ביותר). לשליטה מדויקת יותר על תזמון ה-BRS, הגדר ישירות את הפרמטרים brs_prescaler, brs_bs1 ו-brs_bs2.

  • brs_sample_point מציין את מיקום דגימת הסיבית ביחס לכל זמן הסיבית הנומינלי, מבוטא כאחוז שלם מזמן הסיבית הנומינלי. ברירת המחדל של brs_sample_point היא 75%. פרמטר זה מתעלם אלא אם brs_baudrate מוגדר.

קוונטת הזמן tq היא יחידת הזמן הבסיסית עבור אפיק ה-CAN. tq היא ערך ה-prescaler של ה-CAN מחולק ב-PCLK1 (התדר של אפיק ההתקנים ההיקפיים הפנימי 1); ראה pyb.freq() כדי לקבוע את PCLK1.

סיבית בודדת מורכבת מקטע הסנכרון, שהוא תמיד 1 tq. אחריו בא קטע סיבית 1, ואז קטע סיבית 2. נקודת הדגימה היא לאחר שקטע סיבית 1 מסתיים. נקודת השידור היא לאחר שקטע סיבית 2 מסתיים. ה-baud rate יהיה 1/bittime, כאשר ה-bittime הוא 1 + BS1 + BS2 כפול קוונטת הזמן tq.

לדוגמה, ב-OpenMV Cam H7 (PCLK1 = 100 MHz), ניתן להגדיר CAN של 250 kbps עם נקודת דגימה של 75% כ-prescaler=25, sjw=1, bs1=11, bs2=4: tq = 25 / 100 MHz = 250 ns, bittime = (1 + 11 + 4) × 250 ns = 4 µs, נקודת דגימה = (1 + 11) / 16 = 75%, וה-baudrate הוא 1 / 4 µs = 250 kHz.

ראה את הסעיף bxCAN / FDCAN במדריך הייחוס של STM32 עבור ה-MCU של ה-OpenMV Cam לפרטים נוספים.

deinit() None

מכבה את אפיק ה-CAN.

restart() None

מאלץ הפעלה מחדש תוכנתית של בקר ה-CAN ללא איפוס התצורה שלו.

אם הבקר נכנס למצב bus-off אז הוא יפסיק להשתתף בפעילות האפיק. אם הבקר אינו מוגדר להפעלה מחדש אוטומטית (ראה init()) אז ניתן להשתמש בשיטה זו כדי להפעיל מחדש, והבקר יפעל לפי פרוטוקול ה-CAN כדי לצאת ממצב ה-bus-off ולעבור למצב error active.

state() int

מחזיר את מצב הבקר. ערך ההחזרה יכול להיות אחד מבין:

  • CAN.STOPPED – הבקר כבוי לחלוטין ומאופס;

  • CAN.ERROR_ACTIVE – הבקר מופעל ובמצב Error Active (גם TEC וגם REC קטנים מ-96);

  • CAN.ERROR_WARNING – הבקר מופעל ובמצב Error Warning (לפחות אחד מבין TEC או REC הוא 96 או יותר);

  • CAN.ERROR_PASSIVE – הבקר מופעל ובמצב Error Passive (לפחות אחד מבין TEC או REC הוא 128 או יותר);

  • CAN.BUS_OFF – הבקר מופעל אך אינו משתתף בפעילות האפיק (TEC עלה על 255).

info(list: list | None = None) list

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

הערכים ברשימה הם:

  • ערך TEC

  • ערך REC

  • מספר הפעמים שהבקר נכנס למצב Error Warning (מתאפס ל-0 לאחר 65535)

  • מספר הפעמים שהבקר נכנס למצב Error Passive (מתאפס ל-0 לאחר 65535)

  • מספר הפעמים שהבקר נכנס למצב Bus Off (מתאפס ל-0 לאחר 65535)

  • מספר הודעות TX ממתינות

  • מספר הודעות RX ממתינות ב-fifo 0

  • מספר הודעות RX ממתינות ב-fifo 1

setfilter(bank: int, mode: int, fifo: int, params: Tuple[int, ...], *, rtr: Tuple[bool, ...] | None = None, extframe: bool = False) None

מגדיר בנק מסננים:

  • bank הוא בנק המסננים של בקר ה-CAN הקלאסי, או אינדקס מסנן ה-CAN FD, שיש להגדיר.

  • mode הוא המצב שבו המסנן צריך לפעול, ראה את הטבלאות שלהלן.

  • fifo הוא איזה fifo (0 או 1) הודעה צריכה להישמר בו, אם היא מתקבלת על ידי מסנן זה.

  • params הוא מערך של ערכים המגדיר את המסנן. תוכן המערך תלוי בארגומנט mode.

תוכן מערך ה-params עבור בקרי CAN קלאסי (OpenMV Cam M4 / M7):

mode

תוכן params

CAN.LIST16

ארבעה מזהים בני 16 סיביות שיתקבלו.

CAN.LIST32

שני מזהים בני 32 סיביות שיתקבלו.

CAN.MASK16

שני זוגות id/mask בני 16 סיביות, למשל (1, 3, 4, 4). הזוג הראשון (1, 3) מקבל את כל המזהים עם סיבית 0 = 1 וסיבית 1 = 0; הזוג השני (4, 4) מקבל את כל המזהים עם סיבית 2 = 1.

CAN.MASK32

זוג id/mask אחד בן 32 סיביות (אחרת זהה ל-CAN.MASK16).

תוכן מערך ה-params עבור בקרי CAN FD (OpenMV Cam H7 / H7 Plus / Pure Thermal):

mode

תוכן params

CAN.RANGE

שני מזהים היוצרים טווח של מזהים מתקבלים.

CAN.DUAL

שני מזהים שיתקבלו (למשל (1, 2)).

CAN.MASK

זוג (id, mask) אחד (למשל (0x111, 0x7FF)).

  • rtr עבור בקרי CAN קלאסי, זהו מערך של ערכים בוליאניים הקובע האם מסנן צריך לקבל הודעת בקשת שידור מרחוק (remote transmission request). אם ארגומנט זה אינו ניתן הוא מקבל כברירת מחדל False עבור כל הרשומות. האורך תלוי ב-mode:

    mode

    len(rtr)

    הערות

    CAN.LIST16

    4

    CAN.LIST32

    2

    CAN.MASK16

    2

    CAN.MASK32

    1

    עבור CAN FD ארגומנט זה מתעלם.

  • extframe אם True לפריים יהיה מזהה מורחב (29 סיביות), אחרת נעשה שימוש במזהה סטנדרטי (11 סיביות).

clearfilter(bank: int, extframe: bool = False) None

מנקה ומשבית בנק מסננים:

  • bank הוא בנק המסננים של בקר ה-CAN הקלאסי, או אינדקס מסנן ה-CAN FD, שיש לנקות.

  • extframe עבור בקרי CAN FD, אם True, מנקה מסנן מורחב (שהוגדר עם extframe=True), אחרת מנקה מזהה סטנדרטי (שהוגדר עם extframe=False).

any(fifo: int) bool

מחזיר True אם יש הודעה כלשהי הממתינה ב-FIFO, אחרת False.

recv(fifo: int, list: list | None = None, *, timeout: int = 5000) list

מקבל נתונים על האפיק:

  • fifo הוא מספר שלם, שהוא ה-FIFO שעליו לקבל

  • list הוא אובייקט רשימה אופציונלי שישמש כערך ההחזרה

  • timeout הוא הזמן הקצוב במילישניות להמתנה לקבלה.

ערך החזרה: רשימה המכילה חמישה ערכים.

  • המזהה של ההודעה.

  • ערך בוליאני המציין אם מזהה ההודעה הוא סטנדרטי או מורחב.

  • ערך בוליאני המציין אם ההודעה היא הודעת RTR.

  • ערך ה-FMI (Filter Match Index).

  • מערך המכיל את הנתונים.

אם list הוא None אז תוקצה רשימה חדשה, וכן אובייקט bytes חדש שיכיל את הנתונים (כאיבר החמישי ברשימה).

אם list אינו None אז הוא צריך להיות אובייקט רשימה עם לפחות חמישה איברים. האיבר החמישי צריך להיות אובייקט memoryview שנוצר מ-bytearray או ממערך מסוג »B« או »b«, ולמערך זה חייב להיות מספיק מקום עבור לפחות 8 בתים. אובייקט הרשימה יאוכלס אז עם ארבעת ערכי ההחזרה הראשונים שלעיל, ואובייקט ה-memoryview ישונה בגודלו במקום לגודל הנתונים וימולא באותם נתונים. ניתן לעשות שימוש חוזר באותם אובייקטי רשימה ו-memoryview בקריאות הבאות לשיטה זו, מה שמספק דרך לקבל נתונים מבלי להשתמש ב-heap. לדוגמה:

buf = bytearray(8)
lst = [0, 0, 0, 0, memoryview(buf)]
# No heap memory is allocated in the following call
can.recv(0, lst)
send(data: int | bytes | bytearray, id: int, *, timeout: int = 0, rtr: bool = False, extframe: bool = False, fdf: bool = False, brs: bool = False) None

שולח הודעה על האפיק:

  • data הוא הנתונים לשליחה (מספר שלם לשליחה, או אובייקט חוצץ).

  • id הוא המזהה של ההודעה שתישלח.

  • timeout הוא הזמן הקצוב במילישניות להמתנה לשליחה.

  • rtr הוא ערך בוליאני המציין אם ההודעה תישלח כבקשת שידור מרחוק (remote transmission request). אם rtr הוא True אז רק האורך של data משמש למילוי משבצת ה-DLC של הפריים; הבתים עצמם ב-data אינם בשימוש.

  • extframe אם True לפריים יהיה מזהה מורחב (29 סיביות), אחרת נעשה שימוש במזהה סטנדרטי (11 סיביות).

  • fdf עבור בקרי CAN FD, אם מוגדר ל-True, לפריים יהיה פורמט פריים FD, התומך במטעני נתונים של עד 64 בתים.

  • brs עבור בקרי CAN FD, אם מוגדר ל-True, מצב מיתוג קצב הסיביות מופעל, שבו שלב הנתונים משודר בקצב סיביות שונה. ראה CAN.init() עבור פרמטרי תצורת תזמון סיבית הנתונים.

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

ערך החזרה: None.

rxcallback(fifo: int, fun: Callable[[CAN, int], None] | None) None

רושם פונקציה שתיקרא כאשר הודעה מתקבלת לתוך FIFO ריק:

  • fifo הוא ה-FIFO הקולט.

  • fun היא הפונקציה שתיקרא כאשר ה-FIFO הופך ללא-ריק.

פונקציית ה-callback מקבלת שני ארגומנטים: הראשון הוא אובייקט ה-CAN עצמו; השני הוא מספר שלם המציין את הסיבה ל-callback:

סיבה

משמעות

0

הודעה התקבלה לתוך FIFO ריק.

1

ה-FIFO מלא.

2

הודעה אבדה עקב FIFO מלא.

דוגמה לשימוש ב-rxcallback:

def cb0(bus, reason):
  print('cb0')
  if reason == 0:
      print('pending')
  if reason == 1:
      print('full')
  if reason == 2:
      print('overflow')

can = CAN(1, CAN.LOOPBACK)
can.rxcallback(0, cb0)

קבועים (Constants)

קבועי מצב-אפיק (ארגומנט mode של init()):

NORMAL: int

הבקר משתתף באופן רגיל באפיק – משדר את הפריימים שלו עצמו ומאשר פריימים תקפים שהתקבלו.

LOOPBACK: int

מצב loopback פנימי: הבקר מנותק מהפינים ומנתב את הפריימים המשודרים ישירות בחזרה לנתיב הקבלה. שימושי לבדיקות עצמיות ללא transceiver.

SILENT: int

מצב האזנה-בלבד: הבקר מקבל פריימים אך לעולם אינו מניע את האפיק (ללא ACK, ללא שידורים). שימושי לצותת לאפיק (sniffing).

SILENT_LOOPBACK: int

משלב SILENT ו-LOOPBACK: ללא פעילות בפינים וללא אישורים, עם loopback פנימי של TX לתוך RX.

קבועי מצב-בקר (מוחזרים על ידי state()):

STOPPED: int

הבקר כבוי לחלוטין ומאופס.

ERROR_ACTIVE: int

הבקר מופעל ובמצב Error Active (גם TEC וגם REC קטנים מ-96).

ERROR_WARNING: int

הבקר מופעל ובמצב Error Warning (לפחות אחד מבין TEC או REC הוא 96 או יותר).

ERROR_PASSIVE: int

הבקר מופעל ובמצב Error Passive (לפחות אחד מבין TEC או REC הוא 128 או יותר).

BUS_OFF: int

הבקר מופעל אך אינו משתתף בפעילות האפיק (TEC עלה על 255).

מצבי מסנן CAN-קלאסי (ארגומנט mode של setfilter() ב-OpenMV Cam M4 / M7):

LIST16: int

מערך ה-params של המסנן מכיל ארבעה מזהים בני 16 סיביות שיתקבלו.

LIST32: int

מערך ה-params של המסנן מכיל שני מזהים בני 32 סיביות שיתקבלו.

MASK16: int

מערך ה-params של המסנן מכיל שני זוגות (id, mask) בני 16 סיביות.

MASK32: int

מערך ה-params של המסנן מכיל זוג (id, mask) אחד בן 32 סיביות.

מצבי מסנן CAN FD (ארגומנט mode של setfilter() ב-OpenMV Cam H7 / H7 Plus / Pure Thermal):

RANGE: int

מערך ה-params של המסנן מכיל שני מזהים היוצרים טווח של מזהים מתקבלים.

DUAL: int

מערך ה-params של המסנן מכיל שני מזהים ספציפיים לקבלה.

MASK: int

מערך ה-params של המסנן מכיל זוג (id, mask) אחד.