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 تكون عبارات التأكيد مُفعَّلة ومُصرَّفة داخل البايت كود؛ وعند المستويات 1 وأعلى لا تُصرَّف التأكيدات.

  • المتغير المُدمَج __debug__: عند المستوى 0 يتوسّع هذا المتغير إلى True؛ وعند المستويات 1 وأعلى يتوسّع إلى False.

  • أرقام أسطر الشِفرة المصدرية: عند المستويات 0 و1 و2 تُخزَّن أرقام أسطر الشِفرة المصدرية مع البايت كود حتى تتمكن الاستثناءات من الإبلاغ عن رقم السطر الذي حدثت عنده؛ وعند المستويات 3 وأعلى لا تُخزَّن أرقام الأسطر.

مستوى التحسين الافتراضي عادةً هو المستوى 0.

micropython.alloc_emergency_exception_buf(size: int) None

يخصص size بايت من RAM لمخزن استثناءات الطوارئ (الحجم الجيد يقارب 100 بايت). يُستخدم المخزن لإنشاء الاستثناءات في الحالات التي يفشل فيها تخصيص RAM المعتاد (مثلًا داخل معالِج مقاطعة)، ومن ثَمَّ توفير معلومات تتبُّع مفيدة في هذه المواقف.

من الطرق الجيدة لاستخدام هذه الدالة وضعها في بداية برنامجك النصي الرئيسي (مثلًا boot.py أو main.py)، وعندئذٍ يكون مخزن استثناءات الطوارئ نشطًا لكل الشِفرة التي تليه.

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

يطبع معلومات عن الذاكرة المُستخدَمة حاليًا. إذا أُعطي الوسيط verbose فتُطبع معلومات إضافية.

المعلومات التي تُطبع تعتمد على التنفيذ، لكنها تتضمن حاليًا مقدار المكدس والكومة (heap) المُستخدَم. وفي الوضع المُفصَّل تطبع الكومة بأكملها مع بيان الكتل المُستخدَمة والكتل الفارغة.

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

يطبع معلومات عن السلاسل النصية المُدمَجة (interned) حاليًا. إذا أُعطي الوسيط verbose فتُطبع معلومات إضافية.

المعلومات التي تُطبع تعتمد على التنفيذ، لكنها تتضمن حاليًا عدد السلاسل النصية المُدمَجة ومقدار RAM التي تستخدمها. وفي الوضع المُفصَّل تطبع أسماء كل السلاسل النصية المُدمَجة في RAM.

micropython.stack_use() int

يعيد عددًا صحيحًا يمثّل المقدار الحالي من المكدس المُستخدَم. القيمة المطلقة لهذا ليست مفيدة بشكل خاص، بل ينبغي استخدامها لحساب الفروق في استخدام المكدس عند نقاط مختلفة.

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 سيبذل قصارى جهده لتنفيذ الدالة في أقرب وقت ممكن، مع مراعاة أنه يحاول أيضًا أن يكون كفؤًا، وأن الشروط التالية متحققة:

  • لن تسبق دالة مجدولة قطّ دالة مجدولة أخرى.

  • تُنفَّذ الدوال المجدولة دائمًا "بين رموز العمليات"، مما يعني أن جميع عمليات Python الأساسية (مثل الإضافة إلى قائمة) مضمونة الذرّية.

  • قد يُعرّف منفذ مُعيّن "مناطق حرجة" لا تُنفَّذ الدوال المجدولة داخلها قطّ. ويمكن جدولة الدوال داخل منطقة حرجة لكنها لن تُنفَّذ حتى الخروج من تلك المنطقة. ومن أمثلة المنطقة الحرجة معالِج مقاطعة سابق (IRQ).

  • داخل دوال الشِفرة الأصلية (native code)، لا تُستدعى الدوال المجدولة ما لم تستدعِ الشِفرة الأصلية دالةً تفعل ذلك تحديدًا.

  • بعض الدوال بما فيها poll.poll وpoll.ipoll وtime.sleep وtime.sleep_ms (بما في ذلك حالات السكون صفرية المدة) تستدعي الدوال المجدولة.

من استخدامات هذه الدالة جدولة دالة رد نداء من IRQ سابق. فمثل هذا الـ IRQ يفرض قيودًا على الشِفرة التي تعمل داخله (مثلًا قد تكون الكومة مقفلة)، وجدولة دالة لتُستدعى لاحقًا يرفع تلك القيود.

في المنافذ متعددة الخيوط، يعتمد سلوك الدالة المجدولة على ما إذا كان قفل المفسّر العام (GIL) مُفعَّلًا للمنفذ المُحدَّد:

  • إذا كان GIL مُفعَّلًا، فيمكن للدالة أن تسبق أي خيط وتعمل في سياقه.

  • إذا كان GIL مُعطَّلًا، فلن تسبق الدالة إلا الخيط الرئيسي وتعمل في سياقه.

ملاحظة: إذا استُدعي schedule() من IRQ سابق، حين لا يكون تخصيص الذاكرة مسموحًا وتكون دالة رد النداء المُراد تمريرها إلى schedule() طريقةً مرتبطة (bound method)، فإن تمريرها مباشرةً سيفشل. والسبب أن إنشاء مرجع لطريقة مرتبطة يسبب تخصيص ذاكرة. والحل هو إنشاء مرجع للطريقة في باني الصنف وتمرير ذلك المرجع إلى schedule(). ويُناقَش هذا بالتفصيل هنا في الوثائق المرجعية تحت عنوان "Creation of Python objects".

توجد طابور محدود الحجم لحفظ الدوال المجدولة، وسيطلق schedule() الاستثناء RuntimeError إذا امتلأ الطابور.

الأصناف

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

يوفر مخزنًا حلقيًا (ringbuffer) ثابت الحجم للبايتات مع واجهة تيار. يمكن اعتباره صيغة طابور FIFO من io.BytesIO. ولا يختلف شكلا الباني إلا في كيفية توفير المخزن الداعم:

  • RingIO(size) يخصص المخزن الداعم داخليًا. تحجز خوارزمية المخزن الحلقي الكلاسيكية بايتًا واحدًا للتتبّع، لذا يكون المخزن المُخصَّص أكبر بمقدار بايت واحد من 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

يقرأ سطرًا ينتهي بمحرف سطر جديد أو رجوع إن وُجد في المخزن، وإلا يعيد البايتات المتاحة في المخزن. إذا حُدِّد nbytes فيقرأ هذا العدد من البايتات على الأكثر.

القيمة المُعادة: كائن bytes يحتوي على السطر المقروء.

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

يقرأ البايتات المتاحة إلى buf المُقدَّم. إذا حُدِّد nbytes فيقرأ هذا العدد من البايتات على الأكثر. وإلا فيقرأ len(buf) بايت على الأكثر.

القيمة المُعادة: عدد صحيح يحصي عدد البايتات المقروءة إلى buf.

write(buf: bytes | bytearray | memoryview) int

كتابة غير حاجبة لبايتات من buf إلى المخزن الحلقي، محدودة بالمساحة المتاحة في المخزن الحلقي.

القيمة المُعادة: عدد صحيح يحصي البايتات المكتوبة.

close() None

عملية بلا أثر مُقدَّمة كجزء من واجهة stream القياسية. ليس لها أي تأثير على البيانات في المخزن الحلقي.