الزمن والمؤقّتات
كل البرامج حتى الآن تنتظر بالأمر 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))t1 = 1073741724 t2 = 150 ticks_diff = 250
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)300 B 500 A 600 B 900 B ...
last_a بالأمر ticks_add، لا من now، فلا يتراكم التأخير.sleep_ms(2000) ونرى ما يضيع.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)tick 1 tick 0 main: stopped tick 1
board.timer() المؤقّت 0 في كل مرة، فلا نشغّل به عملين معا. والدالة التي يستدعيها المؤقّت تقطع البرنامج، فنبقيها قصيرة. والأسباب في الفصل 5.4.4 قياس عرض النبضة والتردد – מדידת רוחב פולס ותדר
عرض النبضة (רוחב פולס) هو الزمن الذي يبقى فيه الطرف عند مستوى معيّن. والأمر الجاهز لقياسه time_pulse_us(pin, level, timeout_us): ينتظر بدء النبضة ثم يعيد عرضها بالميكروثانية. ويعيد -2 إذا انتهت المهلة قبل البدء، و-1 إذا انتهت أثناء القياس. والبرنامج يقف طوال ذلك، فنختار المهلة بعناية. أما التردد (תדר) فأبسط طريقة لقياسه أن نعدّ الحواف الصاعدة خلال ثانية كاملة:
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")f = 50 Hz
ticks_diff الزمن حتى نضغط الزر بالفأرة.4.5 مشروع: قياس المسافة – מדידת מרחק (HC-SR04)
يرسل الحسّاس HC-SR04 نبضة صوت غير مسموع، ثم يعطي نبضة على ECHO عرضها زمن ذهاب الصوت وعودته. نطلقه بنبضة قصيرة على TRIG، طولها 10 µs حسب ورقة بيانات الوحدة الشائعة.
| الرمز | المعنى | وحدة القياس |
|---|---|---|
| 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.
TRIG هو 27 وECHO هو 32. وعلى Pico W: GP2 وGP3. وطرف 5 V هو VIN أو VBUS حسب اللوحة.نكتب ذلك مرة واحدة في مكتبة. المهلة 30000 µs تساوي نحو 514 cm، وهي أكثر من مدى الوحدة الشائعة، 4 m حسب ورقة بياناتها.
# 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 / 2import 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)20.0 cm no echo 100.0 cm ...
أهمّ أفكار الفصل:
- الانتظار حدّ أدنى، وهو يحجب كل شيء. لقياس الزمن نقرأ الساعة بالأمر
ticks_diff، ولا نطرح مباشرة. - مهمتان بإيقاعين: حلقة سريعة، وسؤال في كل دورة: هل حان الموعد؟
board.timer()يخفي الفرق بين مؤقّتات اللوحتين، والدالة المستدعاة تبقى قصيرة.- عرض النبضة يعطي المسافة، ونخفض 5 V قبل أن تصل إلى الطرف.
ملخّص قوانين الفصل:
| القانون | الصيغة |
|---|---|
| المسافة من زمن الصدى | 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 |
أسئلة للتمرين:
board.timer() ولا نكتب Timer(0)؟Timer(0) لا يصلح في Pico W.