برمجة المتحكّمات بلغة مايكروبايثون · الفصل 4 — الزمن والمؤقّتاتזמן וטיימרים · תכנות מיקרו-בקרים בשפת MicroPython⌂📚🏠
الفصل 4

الزمن والمؤقّتات

זמן וטיימרים – Time and Timers
ساعتان تعليميتان

كل البرامج حتى الآن تنتظر بالأمر sleep، والبرنامج أثناءه أعمى: لا يرى زرا ولا يخدم ثنائيا آخر. في هذا الفصل نقيس الزمن بدل أن ننتظره، ونترك للمؤقّت ما يجب أن يحدث في موعده.

في هذا الفصل:
  • نقيس الزمن بالأمر ticks_diff، ونشغّل مهمتين بإيقاعين مختلفين دون حجب.
  • نستعمل مؤقّتا دوريا ومؤقّتا لمرة واحدة، ونعرف الفرق بين اللوحتين.
  • نقيس عرض نبضة وترددا، ونحسب المسافة بالحسّاس HC-SR04.

4.1 الانتظار وساعة المتحكّم – השהיות ומדידת זמן (Delays and Ticks)

للانتظار time.sleep(s) بالثواني، وtime.sleep_ms(ms) وtime.sleep_us(us)، لأن بعض اللوحات لا تقبل كسرا في sleep حسب التوثيق. ولقراءة الساعة time.ticks_ms() وtime.ticks_us()، وهما عدّادان متزايدان نقطة بدايتهما غير محدّدة، وtime.ticks_diff(b, a) يعطي الزمن من a إلى b.

والانتظار حدّ أدنى: يذكر التوثيق أن sleep_ms قد يطول، فلمعرفة الزمن نقرأ الساعة. والعدّاد محدود، فإذا بلغ أعلى قيمة التفّ (גלישה) وعاد إلى الصفر. وطول دورته قوة للعدد 2 حسب التوثيق. وفي المُحاكي 230، فيلتفّ عدّاد الميلي ثانية كل 12.4 يوما تقريبا:

ساعة المُحاكي تبدأ من الصفر: القراءة الأولى قبل الالتفاف، والثانية بعده.
import time

t1 = time.ticks_add(time.ticks_ms(), -100)
t2 = time.ticks_add(t1, 250)
print("t1 =", t1)
print("t2 =", t2)
print("ticks_diff =", time.ticks_diff(t2, t1))
ESP32 + Pico W · MicroPython 1.29 · simulated board
t1 = 1073741724
t2 = 150
ticks_diff = 250
خطأ شائع: الطرح المباشر هنا يعطي -1073741574. أما ticks_diff فيعطي 250، بشرط ألا يفصل بين اللحظتين أكثر من نصف الدورة. والبرنامج الذي يطرح مباشرة ينجح أياما ثم يخطئ.

4.2 لماذا يحجب sleep؟ مهمتان معا – שתי נוריות בלי חסימה

نريد ثنائيين: الأول يتغيّر كل 500 ms، والثاني كل 300 ms. لو كتبنا sleep_ms(500) ثم sleep_ms(300)، لصار كل منهما يتغيّر كل 800 ms: كل أمر انتظار يحجب (חוסם) كل ما عداه. والحل: ندور في الحلقة بسرعة، ونسأل في كل دورة: هل حان موعد هذا الثنائي؟

الزمن بالميلي ثانية عند كل تغيير. كل ثنائي بإيقاعه.
from machine import Pin
import time
import board

led_a = Pin(board.LED, Pin.OUT)
led_b = Pin(board.LED_EXT, Pin.OUT)
start = time.ticks_ms()
last_a = start
last_b = start

while True:
    now = time.ticks_ms()
    if time.ticks_diff(now, last_a) >= 500:
        last_a = time.ticks_add(last_a, 500)
        led_a.value(not led_a.value())
        print(time.ticks_diff(now, start), "A")
    if time.ticks_diff(now, last_b) >= 300:
        last_b = time.ticks_add(last_b, 300)
        led_b.value(not led_b.value())
        print(time.ticks_diff(now, start), "B")
    time.sleep_ms(1)
ESP32 + Pico W · MicroPython 1.29 · simulated board
300 B
500 A
600 B
900 B
...
ملاحظة: نحسب الموعد القادم من last_a بالأمر ticks_add، لا من now، فلا يتراكم التأخير.
▶تجربة حية — مختبر الوميض دون حجبالثنائي يومض بانتظام، وكل ضغطة تُحسب فورا. ثم نغيّر الشيفرة إلى sleep_ms(2000) ونرى ما يضيع.
⚠
المختبر غير متاح الآن. المادة في هذا الفصل كاملة من دونه — نواصل القراءة، ونعود إلى التجربة لاحقا أو نفتحها مباشرة من الرابط الذي تحت الإطار.
https://fahed-lab.com/books/micropython/lab/mpy_lab?prog=ch4_no_block
نفتح المختبر في صفحة مستقلة ↗ fahed-lab.com/books/micropython/lab/mpy_lab?prog=ch4_no_block

4.3 المؤقّت – טיימר (Timer)

المؤقّت (טיימר): يستدعي دالة نعطيها له، في موعد نحدّده، دون أن يقف البرنامج الرئيسي. وهو دوري (מחזורי) إذا تكرّر، أو لمرة واحدة (חד-פעמי).

وهنا تختلف اللوحتان. في ESP32 أربعة مؤقّتات عتادية بالأرقام 0–3، فنكتب Timer(0). وفي Pico W مؤقّتات برمجية لا تحتاج إلى رقم، وما يقبله هو Timer(-1)، ولا يقبله ESP32. هذا الفرق مكانه board.py: الدالة board.timer() تعيد المؤقّت الصحيح، والبرنامج لا يذكر رقما.

البرنامج الرئيسي ينتظر، والمؤقّت يعمل. ثم نعيد استعماله لمرة واحدة، فيعمل مرة ولا يتكرّر.
from machine import Pin, Timer
import time
import board

led = Pin(board.LED, Pin.OUT)
tim = board.timer()

def tick(t):
    led.value(not led.value())
    print("tick", led.value())

tim.init(period=500, mode=Timer.PERIODIC, callback=tick)
time.sleep_ms(1200)
tim.deinit()
print("main: stopped")

tim.init(period=1000, mode=Timer.ONE_SHOT, callback=tick)
time.sleep_ms(1500)
ESP32 + Pico W · MicroPython 1.29 · simulated board
tick 1
tick 0
main: stopped
tick 1
تحذير: على ESP32 يعيد board.timer() المؤقّت 0 في كل مرة، فلا نشغّل به عملين معا. والدالة التي يستدعيها المؤقّت تقطع البرنامج، فنبقيها قصيرة. والأسباب في الفصل 5.

4.4 قياس عرض النبضة والتردد – מדידת רוחב פולס ותדר

عرض النبضة (רוחב פולס) هو الزمن الذي يبقى فيه الطرف عند مستوى معيّن. والأمر الجاهز لقياسه time_pulse_us(pin, level, timeout_us): ينتظر بدء النبضة ثم يعيد عرضها بالميكروثانية. ويعيد -2 إذا انتهت المهلة قبل البدء، و-1 إذا انتهت أثناء القياس. والبرنامج يقف طوال ذلك، فنختار المهلة بعناية. أما التردد (תדר) فأبسط طريقة لقياسه أن نعدّ الحواف الصاعدة خلال ثانية كاملة:

على الطرف إشارة مربعة من 50 Hz، في المُحاكي.
from machine import Pin
import time
import board

sig = Pin(board.ECHO, Pin.IN)
count = 0
last = sig.value()
start = time.ticks_ms()

while time.ticks_diff(time.ticks_ms(), start) < 1000:
    level = sig.value()
    if last == 0 and level == 1:
        count += 1
    last = level
    time.sleep_ms(1)

print("f =", count, "Hz")
ESP32 + Pico W · MicroPython 1.29 · simulated board
f = 50 Hz
تحذير: نقرأ الطرف مرة كل ميلي ثانية على الأكثر، فلا يصلح هذا إلا لترددات منخفضة: تحت 500 هرتز في أحسن الأحوال. وللترددات الأعلى نعدّ بالمقاطعات، في الفصل 5.
▶تجربة حية — مختبر رد الفعلبرنامج يضيء الثنائي ويحسب بالأمر ticks_diff الزمن حتى نضغط الزر بالفأرة.
⚠
المختبر غير متاح الآن. المادة في هذا الفصل كاملة من دونه — نواصل القراءة، ونعود إلى التجربة لاحقا أو نفتحها مباشرة من الرابط الذي تحت الإطار.
https://fahed-lab.com/books/micropython/lab/mpy_lab?prog=ch4_reaction
نفتح المختبر في صفحة مستقلة ↗ fahed-lab.com/books/micropython/lab/mpy_lab?prog=ch4_reaction

4.5 مشروع: قياس المسافة – מדידת מרחק (HC-SR04)

يرسل الحسّاس HC-SR04 نبضة صوت غير مسموع، ثم يعطي نبضة على ECHO عرضها زمن ذهاب الصوت وعودته. نطلقه بنبضة قصيرة على TRIG، طولها 10 µs حسب ورقة بيانات الوحدة الشائعة.

TRIGECHO10 µst
نبضة الإطلاق، ثم نبضة الصدى وعرضها t. الرسم ليس بمقياس رسم.
المسافة من زمن الصدى
d = c · t2
الرمزالمعنىوحدة القياس
cسرعة الصوت: نحو 0.0343 في الهواء عند نحو 20 درجة مئوية cm/µs
tعرض نبضة الصدى µs

مثلا: نبضة صدى 1200 µs تعني d = 0.0343 · 12002 = 20.58 cm.

والوحدة الشائعة تعمل على 5 V، فنبضة ECHO أعلى مما يتحمّله الطرف. لذلك نضع مجزّئ جهد، فيصبح الجهد على الطرف V = 5 · 20001000 + 2000 = 3.33 V.

VCCTRIGECHOGNDHC-SR045 VTRIGECHOGND1 kΩ2 kΩ
على ESP32: TRIG هو 27 وECHO هو 32. وعلى Pico W: GP2 وGP3. وطرف 5 V هو VIN أو VBUS حسب اللوحة.

نكتب ذلك مرة واحدة في مكتبة. المهلة 30000 µs تساوي نحو 514 cm، وهي أكثر من مدى الوحدة الشائعة، 4 m حسب ورقة بياناتها.

الملف hcsr04.py، على كل اللوحات
# hcsr04.py - HC-SR04 ultrasonic distance sensor, the same on every board.
# (c) 2026 Fahed Ghanem - MIT License - fahed-lab.com
from machine import Pin, time_pulse_us
import time

SOUND_CM_PER_US = 0.0343


class HCSR04:
    def __init__(self, trig, echo, timeout_us=30000):
        self.trig = Pin(trig, Pin.OUT, value=0)
        self.echo = Pin(echo, Pin.IN)
        self.timeout_us = timeout_us

    def distance_cm(self):
        self.trig.value(1)
        time.sleep_us(10)
        self.trig.value(0)
        us = time_pulse_us(self.echo, 1, self.timeout_us)
        if us < 0:
            return None
        return us * SOUND_CM_PER_US / 2
في المُحاكي: جسم قريب، ثم لا صدى، ثم جسم بعيد.
import time
import board
from hcsr04 import HCSR04

sensor = HCSR04(board.TRIG, board.ECHO)

while True:
    d = sensor.distance_cm()
    if d is None:
        print("no echo")
    else:
        print("%.1f cm" % d)
    time.sleep(1)
ESP32 + Pico W · MicroPython 1.29 · simulated board
20.0 cm
no echo
100.0 cm
...

أهمّ أفكار الفصل:

ملخّص قوانين الفصل:

القانونالصيغة
المسافة من زمن الصدىd = c · t2

أوامر الفصل:

الأمرعمله
time.ticks_add(t, d)موعد بعد d من t.
tim.init(period=, mode=, callback=)تشغيل المؤقّت، وtim.deinit() إيقافه.
time_pulse_us(pin, level, timeout_us)عرض نبضة بالميكروثانية.

قاموس المصطلحات – מילון מונחים:

بالعربيةبالعبريةEnglish
المؤقّتטיימרTimer
الالتفافגלישהWrap-around
عرض النبضةרוחב פולסPulse Width
الترددתדרFrequency

أسئلة للتمرين:

المستوى الأول — أساسي
1.
عرض نبضة الصدى 2000 µs. ما المسافة؟
الإجابات: d = 34.3 cm.
2.
لماذا نكتب board.timer() ولا نكتب Timer(0)؟
الإجابات: لأن Timer(0) لا يصلح في Pico W.
المستوى الثاني — تطبيق
3.
اكتبوا برنامجا يومض دون حجب، ويعدّ ضغطات الزر في الوقت نفسه.
الإجابات: نجمع حلقة الوميض مع عدّاد الفصل 3. الحل جاهز في مختبر الوميض دون حجب، ومع ضغطة واحدة طبع عند بناء الكتاب: presses: 1.
المستوى الثالث — تعمّق
4.
نريد قياس إشارة من 2 kHz بالبرنامج الذي يعدّ الحواف كل ميلي ثانية. هل ينجح؟
الإجابات: لا. في الميلي ثانية الواحدة دورتان كاملتان، فنفوّت أغلب الحواف. وهو يصلح لأقل من 500 هرتز.
5.
سرعة الصوت تقريبا c = 331.3 + 0.606 T بوحدة m/s. نبضة صدى 6000 µs عند 35 درجة مئوية: كم الخطأ إذا استعملنا 0.0343 cm/µs؟
الإجابات: c = 352.5 m/s، فالمسافة 105.8 cm. وبالقيمة الثابتة 102.9 cm، أي خطأ نحو 2.9 cm.
لوحة القوانين: كل قوانين هذا الفصل مجموعة مع رسومها وجداول رموزها في الفصل 25 — لوحة القوانين الشاملة. (נוסחאון הספר)