micropython — גישה ושליטה ברכיבים הפנימיים של MicroPython

פונקציות

micropython.const(expr: int) int

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

from micropython import const

CONST_X = const(123)
CONST_Y = const(2 * CONST_X + 1)

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

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

micropython.opt_level(level: int | None = None) int | None

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

רמת האופטימיזציה שולטת על תכונות ההידור הבאות:

  • טענות (Assertions): ברמה 0 משפטי טענה מאופשרים ומהודרים אל הbytecode; ברמות 1 ומעלה טענות אינן מהודרות.

  • המשתנה המובנה __debug__: ברמה 0 משתנה זה מתרחב ל-True; ברמות 1 ומעלה הוא מתרחב ל-False.

  • מספרי שורות בקוד המקור: ברמות 0, 1 ו-2 מספרי שורות הקוד נשמרים יחד עם הbytecode כך שחריגות יכולות לדווח על מספר השורה שבה הן התרחשו; ברמות 3 ומעלה מספרי שורות אינם נשמרים.

רמת האופטימיזציה המוגדרת כברירת מחדל היא בדרך כלל רמה 0.

micropython.alloc_emergency_exception_buf(size: int) None

הקצו size בתים של RAM עבור חוצץ חריגות החירום (גודל טוב הוא בסביבות 100 בתים). החוצץ משמש ליצירת חריגות במקרים שבהם הקצאת RAM רגילה הייתה נכשלת (למשל בתוך מטפל פסיקה) ובכך לספק מידע traceback שימושי במצבים אלה.

דרך טובה להשתמש בפונקציה זו היא למקם אותה בתחילת הסקריפט הראשי שלכם (למשל boot.py או main.py) ואז חוצץ חריגות החירום יהיה פעיל עבור כל הקוד שאחריו.

micropython.mem_info(verbose: Any | None = None) None

הדפיסו מידע על הזיכרון שבשימוש כעת. אם הארגומנט verbose ניתן אז מידע נוסף מודפס.

המידע שמודפס תלוי במימוש, אך כעת כולל את כמות המחסנית (stack) והערמה (heap) שבשימוש. במצב verbose הוא מדפיס את כל הערמה תוך ציון אילו בלוקים בשימוש ואילו פנויים.

micropython.qstr_info(verbose: Any | None = None) None

הדפיסו מידע על מחרוזות מועצמות (interned) שבשימוש כעת. אם הארגומנט verbose ניתן אז מידע נוסף מודפס.

המידע שמודפס תלוי במימוש, אך כעת כולל את מספר המחרוזות המועצמות וכמות ה-RAM שהן משתמשות בה. במצב verbose הוא מדפיס את שמות כל המחרוזות המועצמות ב-RAM.

micropython.stack_use() int

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

micropython.heap_lock() None

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

נעילות מקוננות: קריאה ל-heap_lock() מספר פעמים מגדילה את עומק הנעילה. הערמה נשארת נעולה עד ש-heap_unlock() נקראה אותו מספר פעמים.

אם ה-REPL הופך לפעיל בעוד שהערמה נעולה אז היא תיפתח בכוח.

micropython.heap_unlock() int

הפחיתו את עומק נעילת הערמה באחד והחזירו את העומק החדש כמספר שלם אי-שלילי. ערך החזרה של 0 משמעו שהערמה אינה נעולה עוד והקצאות מותרות שוב.

micropython.heap_locked() int

החזירו את עומק נעילת הערמה הנוכחי כמספר שלם אי-שלילי; 0 משמעו שהערמה אינה נעולה.

הערה: פונקציה זו אינה זמינה ב-OpenMV Cam.

micropython.kbd_intr(chr: int) None

הגדירו את התו שיעלה חריגת KeyboardInterrupt. כברירת מחדל זה מוגדר ל-3 במהלך ביצוע הסקריפט, התואם ל-Ctrl-C. העברת -1 לפונקציה זו תשבית את לכידת Ctrl-C, והעברת 3 תשחזר אותה.

פונקציה זו יכולה לשמש למניעת לכידת Ctrl-C על הזרם הנכנס של תווים שמשמש בדרך כלל עבור ה-REPL, במקרה שהזרם הזה משמש למטרות אחרות.

micropython.schedule(func: Callable[[Any], Any], arg: Any) None

תזמנו את הפונקציה func להתבצע ”בקרוב מאוד“. לפונקציה מועבר הערך arg כארגומנט היחיד שלה. ”בקרוב מאוד“ משמעו ש-MicroPython runtime יעשה כמיטב יכולתו לבצע את הפונקציה בזמן המוקדם ביותר האפשרי, בהינתן שהוא גם מנסה להיות יעיל, ושהתנאים הבאים מתקיימים:

  • פונקציה מתוזמנת לעולם לא תקדים (preempt) פונקציה מתוזמנת אחרת.

  • פונקציות מתוזמנות מתבצעות תמיד ”בין opcodes“ מה שאומר שכל פעולות Python הבסיסיות (כגון הוספה לרשימה) מובטחות להיות אטומיות.

  • port נתון עשוי להגדיר ”אזורים קריטיים“ שבתוכם פונקציות מתוזמנות לעולם לא יתבצעו. ניתן לתזמן פונקציות בתוך אזור קריטי אך הן לא יתבצעו עד שיוצאים מאזור זה. דוגמה לאזור קריטי היא מטפל פסיקה מקדים (IRQ).

  • בתוך פונקציות קוד מקורי (native), פונקציות מתוזמנות אינן נקראות אלא אם הקוד המקורי קורא לפונקציה שעושה זאת באופן ספציפי.

  • פונקציות מסוימות כולל poll.poll, poll.ipoll, time.sleep ו-time.sleep_ms (כולל שינה באורך אפס) יקראו לפונקציות מתוזמנות.

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

ב-ports מרובי-תהליכונים, התנהגות הפונקציה המתוזמנת תלויה בשאלה האם ה-Global Interpreter Lock (GIL) מאופשר עבור ה-port הספציפי:

  • אם GIL מאופשר, הפונקציה יכולה להקדים כל תהליכון ולרוץ בהקשר שלו.

  • אם GIL מושבת, הפונקציה תקדים רק את התהליכון הראשי ותרוץ בהקשר שלו.

הערה: אם schedule() נקראת מ-IRQ מקדים, כאשר הקצאת זיכרון אינה מותרת וה-callback שיועבר ל-schedule() הוא מתודה כבולה (bound method), העברתו ישירות תיכשל. הסיבה לכך היא שיצירת הפניה למתודה כבולה גורמת להקצאת זיכרון. פתרון הוא ליצור הפניה למתודה בבנאי המחלקה ולהעביר את ההפניה הזו ל-schedule(). הדבר נדון בפירוט כאן reference documentation תחת ”Creation of Python objects“.

קיימת תור סופי להחזקת הפונקציות המתוזמנות ו-schedule() תעלה RuntimeError אם התור מלא.

מחלקות

class micropython.RingIO(size: int)
class micropython.RingIO(buffer: bytes | bytearray | memoryview)

מספק ringbuffer בגודל קבוע עבור בתים עם ממשק זרם (stream). ניתן להתייחס אליו כווריאנט תור-FIFO של io.BytesIO. שתי צורות הבנאי נבדלות רק באופן שבו החוצץ המגבה מסופק:

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

  • RingIO(buffer) משתמש ב-buffer שסופק במקומו במקום להקצות אחד. מכיוון שבית אחד שמור למעקב, המופע יכול להחזיק len(buffer) - 1 בתים של נתונים. לדוגמה, RingIO(bytearray(16)) מחזיק 15 בתים של נתונים.

מופע RingIO הוא בטוח לשימוש מ-IRQ/תהליכונים כאשר משמש להעברת נתונים בכיוון יחיד (למשל נכתב אליו מ-IRQ ונקרא מפונקציה שאינה IRQ, או להפך). זה אינו תקף אם מופע יחיד נכתב אליו הן מהקשרי IRQ והן מהקשרים שאינם IRQ, מה שלעתים קרובות יגרום לשחיתות נתונים.

any() int

מחזיר מספר שלם הסופר את מספר התווים שניתן לקרוא.

read(nbytes: int | None = None) bytes

קראו את התווים הזמינים. זו פונקציה לא-חוסמת. אם nbytes מצוין אז קראו לכל היותר מספר זה של בתים, אחרת קראו כמה שיותר נתונים.

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

readline(nbytes: int | None = None) bytes

קראו שורה, המסתיימת בתו שורה חדשה או return אם קיים כזה בחוצץ, אחרת החזירו את הבתים הזמינים בחוצץ. אם nbytes מצוין אז קראו לכל היותר מספר זה של בתים.

ערך החזרה: אובייקט bytes המכיל את השורה שנקראה.

readinto(buf: bytearray | memoryview, nbytes: int | None = None) int

קראו את הבתים הזמינים אל ה-buf שסופק. אם nbytes מצוין אז קראו לכל היותר מספר זה של בתים. אחרת, קראו לכל היותר len(buf) בתים.

ערך החזרה: ספירת מספר שלם של מספר הבתים שנקראו אל buf.

write(buf: bytes | bytearray | memoryview) int

כתיבה לא-חוסמת של בתים מ-buf אל ה-ringbuffer, מוגבלת על ידי המקום הזמין ב-ringbuffer.

ערך החזרה: ספירת מספר שלם של בתים שנכתבו.

close() None

פעולת ריק (No-op) המסופקת כחלק מממשק stream הסטנדרטי. אין לה השפעה על נתונים ב-ringbuffer.