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() тоже подходит, но варианты с целочисленным аргументом избегают преобразования с плавающей точкой и читаются естественнее для коротких интервалов.
Сон – это блокирующий вызов. Пока скрипт спит, он не делает ничего другого – не читает вывод, не обслуживает 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()) покрывают все случаи.