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() теж підходить, але варіанти з цілочисельним аргументом уникають перетворення чисел з рухомою комою та природніше читаються для коротких інтервалів.

Sleep – це блокуючий виклик. Поки скрипт виконує затримку, він більше нічого не робить – не зчитує вивід, не обслуговує UART, не оновлює світлодіод. Використовуйте sleep, коли блокування – саме те, що вам потрібно; у протилежному випадку застосовуйте неблокуючий патерн, описаний нижче.

3.2.2. Зчитування годинника

Щоб виміряти час виконання фрагмента коду, зчитайте годинник до і після:

  • time.ticks_ms() – поточне значення лічильника в мілісекундах.

  • time.ticks_us() – те саме в мікросекундах.

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. Неблокуючий таймінг

Скрипт для мікроконтролера зазвичай має виконувати кілька задач «одночасно»: зчитувати кнопку, блимати світлодіодом, опитувати датчик, обслуговувати 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)

Світлодіод перемикається кожні 500 мс, датчик опитується кожні 100 мс, і жодна задача не блокує іншу. time.ticks_add() просуває дедлайн на відомий приріст, уникаючи проблем із переповненням.

Ця структура – єдиний цикл, кілька задач із таймінгом, без sleep у тілі – зустрічається скрізь у коді для апаратного забезпечення: програмне усунення дребезгу перемикачів, керування двигунами, таймаут читання з UART. Три функції (time.ticks_ms(), time.ticks_diff(), time.ticks_add()) охоплюють усі випадки.