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