3.2. Định thời

Mô-đun time nhóm các hàm dùng để ngủ (tạm dừng tập lệnh trong một khoảng thời gian xác định) và đo thời gian thực hiện của một đoạn code. Trên vi điều khiển, đây không phải là tính năng tùy chọn; chúng là cách tập lệnh điều phối các tương tác với thế giới bên ngoài -- bao lâu để giữ một chân (pin) ở mức cao, bao lâu chờ giữa các lần lấy mẫu, bao lâu kể từ lần nhấn nút cuối cùng.

3.2.1. Ngủ

Ba hàm ngủ chặn tập lệnh trong khoảng thời gian yêu cầu:

  • time.sleep(s) -- tạm dừng trong s giây. Chấp nhận số thực, vì vậy time.sleep(0.5) chờ nửa giây.

  • time.sleep_ms(ms) -- tạm dừng trong ms mili giây. Đối số phải là số nguyên.

  • time.sleep_us(us) -- tạm dừng trong us micro giây.

import time

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

Sử dụng time.sleep_ms() cho các nhu cầu "chờ một chút" thông thường và time.sleep_us() chỉ khi định thời phải chính xác. time.sleep() thông thường cũng được, nhưng các biến thể đối số nguyên tránh chuyển đổi dấu phẩy động và đọc tự nhiên hơn cho các khoảng thời gian ngắn.

Ngủ là lệnh gọi chặn. Trong khi tập lệnh đang ngủ, nó không làm gì khác -- không đọc chân (pin), không phục vụ UART, không cập nhật LED. Dùng sleep khi chặn là điều bạn muốn; dùng mẫu không chặn bên dưới khi không muốn.

3.2.2. Đọc đồng hồ

Để đo thời gian một đoạn code mất bao lâu, đọc đồng hồ trước và sau:

import time

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

Điều này thường hoạt động đúng. Bộ đếm tick quay vòng (trở về không) sau một số lượng tick lớn nhưng hữu hạn, và phép trừ đơn giản qua điểm quay vòng đó tạo ra một số âm hoặc dương sai hoàn toàn.

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.

Bộ đếm tick quay trở lại không khi đạt đến giới hạn số nguyên. Phép trừ đơn giản qua điểm quay vòng đó là sai.

3.2.3. ticks_diff

Để lấy các tick đã trôi qua chính xác, ngay cả qua điểm quay vòng, hãy sử dụng time.ticks_diff():

import time

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

Thứ tự đối số là ticks_diff(later, earlier) -- biểu thức đọc là "later cách earlier bao xa". Kết quả là một số nguyên có dấu; dương có nghĩa là later thực sự là sau, âm có nghĩa là nó ở trong quá khứ. Hàm xử lý quay vòng nội bộ.

Mẹo

Luôn ghép time.ticks_ms() / time.ticks_us() với time.ticks_diff(). Phép trừ thô là đúng hầu hết thời gian; thời điểm nó sai là khi tập lệnh đã chạy trong thời gian dài -- thường là thời điểm tồi tệ nhất để gỡ lỗi sự cố định thời.

3.2.4. Định thời không chặn

Một tập lệnh vi điều khiển thường có nhiều thứ phải làm "cùng một lúc": đọc nút nhấn, nhấp nháy LED, thăm dò cảm biến, phục vụ UART. sleep không phù hợp cho điều đó -- trong khi một sleep đang chạy, các tác vụ khác bị dừng lại.

Mẫu chuẩn là giữ một thời hạn cho mỗi tác vụ và kiểm tra mỗi vòng lặp xem thời hạn đã qua chưa. Quyết định hành động được xây dựng trên time.ticks_diff(), không bao giờ dựa trên 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 chuyển đổi mỗi 500 ms, cảm biến được thăm dò mỗi 100 ms và không tác vụ nào chặn tác vụ kia. time.ticks_add() tăng thời hạn thêm một lượng tăng đã biết mà không mắc phải vấn đề quay vòng.

Cấu trúc này -- một vòng lặp đơn, nhiều tác vụ có định thời, không có sleep trong thân -- xuất hiện ở khắp nơi trong code phần cứng: khử rung phần mềm cho công tắc, điều phối động cơ, thời gian chờ đọc UART. Ba hàm tương tự (time.ticks_ms(), time.ticks_diff(), time.ticks_add()) bao phủ mọi trường hợp.