3.2. التوقيت

تجمع وحدة time معاً دوال للنوم (إيقاف البرنامج النصي مؤقتاً لمدة معروفة) ولقياس المدة التي يستغرقها شيء ما. على المتحكم الدقيق ليست هذه أموراً اختيارية للراحة؛ بل هي الطريقة التي يضبط بها البرنامج النصي إيقاع التفاعلات مع العالم الخارجي -- كم من الوقت يبقي دبوساً مرتفعاً، وكم من الوقت ينتظر بين العينات، وكم مضى منذ آخر ضغط على زر.

3.2.1. النوم

تحجب ثلاث دوال نوم البرنامج النصي للمدة المطلوبة:

  • time.sleep(s) -- توقّف لمدة s ثانية. تقبل عدداً عشرياً، لذا تنتظر time.sleep(0.5) نصف ثانية.

  • time.sleep_ms(ms) -- توقّف لمدة ms مللي ثانية. يجب أن يكون الوسيط عدداً صحيحاً.

  • time.sleep_us(us) -- توقّف لمدة us ميكروثانية.

import time

print("now")
time.sleep_ms(500)
print("half a second later")

استخدم time.sleep_ms() للاحتياجات المعتادة من نوع "انتظر قليلاً"، واستخدم time.sleep_us() فقط عندما يجب أن يكون التوقيت محكماً. الدالة العادية time.sleep() جيدة أيضاً، لكن المتغيرات ذات الوسيط الصحيح تتجنب تحويل الفاصلة العائمة وتُقرأ بشكل أكثر طبيعية للفترات القصيرة.

النوم استدعاء حاجب. أثناء نوم البرنامج النصي، لا يفعل أي شيء آخر -- لا يقرأ دبوساً، ولا يخدم UART، ولا يحدّث LED. اللجوء إلى sleep عندما يكون الحجب هو ما تريده؛ واستخدم النمط غير الحاجب أدناه عندما لا يكون كذلك.

3.2.2. قراءة الساعة

لقياس المدة التي تستغرقها قطعة من الشيفرة، اقرأ الساعة قبلها وبعدها:

import time

start = time.ticks_ms()
do_work()
elapsed = time.ticks_ms() - start
print("took", elapsed, "ms")

يعمل هذا في معظم الأوقات. يلتف عدّاد النبضات (يعود إلى الصفر) بعد عدد كبير لكنه محدود من النبضات، والطرح الساذج عبر ذلك الالتفاف يُنتج رقماً سالباً أو موجباً خاطئاً بشكل جامح.

Number line from 0 to MAX with a start tick near MAX and an end tick near 0; a dashed wrap-around arrow shows that after MAX the counter returns to 0.

يلتف عدّاد النبضات عائداً إلى الصفر عندما يبلغ الحد الأقصى للعدد الصحيح. الطرح البسيط عبر ذلك الالتفاف خاطئ.

3.2.3. ticks_diff

للحصول على النبضات المنقضية بشكل صحيح، حتى عبر الالتفاف، استخدم time.ticks_diff():

import time

start = time.ticks_ms()
do_work()
elapsed = time.ticks_diff(time.ticks_ms(), start)
print("took", elapsed, "ms")

ترتيب الوسائط هو ticks_diff(later, earlier) -- ويُقرأ التعبير "كم يبعد later بعد earlier". النتيجة عدد صحيح بإشارة؛ الموجب يعني أن later هو فعلاً لاحق، والسالب يعني أنه في الماضي. تعالج الدالة الالتفاف داخلياً.

نصيحة

اقرن دائماً time.ticks_ms() / time.ticks_us() مع time.ticks_diff(). الطرح الخام صحيح في معظم الأوقات؛ والوقت الذي يكون فيه خاطئاً هو عندما يكون البرنامج النصي قيد التشغيل لمدة طويلة -- وهو عادةً أسوأ وقت لتصحيح خلل في التوقيت.

3.2.4. التوقيت غير الحاجب

عادةً ما يكون لدى برنامج المتحكم الدقيق أكثر من شيء يقوم به "في الوقت نفسه": قراءة زر، وإيماض LED، واستطلاع مستشعر، وخدمة UART. الدالة sleep غير صالحة لذلك -- فبينما تعمل إحدى دوال sleep، تتوقف المهام الأخرى.

النمط القياسي هو الاحتفاظ بـ موعد نهائي لكل مهمة، والتحقق في كل حلقة مما إذا كان الموعد النهائي قد انقضى. ويُبنى قرار التصرف على time.ticks_diff()، وليس أبداً على sleep:

import time

next_blink = time.ticks_ms()
next_poll  = time.ticks_ms()
state = False

while True:
    now = time.ticks_ms()

    if time.ticks_diff(now, next_blink) >= 0:
        state = not state
        # update LED here
        next_blink = time.ticks_add(next_blink, 500)

    if time.ticks_diff(now, next_poll) >= 0:
        # read sensor here
        next_poll = time.ticks_add(next_poll, 100)

يتبدّل LED كل 500 مللي ثانية، ويُستطلع المستشعر كل 100 مللي ثانية، ولا تحجب أي من المهمتين الأخرى. تقدّم time.ticks_add() موعداً نهائياً بمقدار معلوم دون الوقوع في فخ الالتفاف.

هذا الشكل -- حلقة واحدة، وعدة مهام موقّتة، ودون sleep في المتن -- يظهر في كل مكان تذهب إليه شيفرة الأجهزة: إزالة الارتداد البرمجية للمفاتيح، وتتابع المحركات، ومهلات قراءة UART. تغطي الدوال الثلاث نفسها (time.ticks_ms()، time.ticks_diff()، time.ticks_add()) كل حالة.