Таймеры и прерывания: реагируем мгновенно
Учимся ловить события и выполнять задачи по расписанию, не загружая основной цикл.
Прерывание (interrupt) — механизм, при котором аппаратура сама «дёргает» процессор при событии (нажатие, тик таймера), и тот немедленно выполняет специальную функцию-обработчик.
Проблема опроса (polling)
До сих пор мы читали кнопку в цикле: if button.value() == 0. Это опрос — процессор постоянно проверяет вход. Если в цикле есть долгие операции (отправка по сети, чтение датчика), нажатие можно пропустить. Прерывания решают проблему: процессор занят своими делами, но как только пин изменился — мгновенно вызывается обработчик.
Прерывание по фронту сигнала
from machine import Pin
presses = 0
def on_press(pin):
global presses
presses += 1
button = Pin(15, Pin.IN, Pin.PULL_UP)
button.irq(trigger=Pin.IRQ_FALLING, handler=on_press)
# основной цикл свободен для другой работы
while True:
passIRQ_FALLING — реагировать на спадающий фронт (переход 1→0, то есть нажатие с pull-up). Есть также IRQ_RISING и их комбинация.
Таймер: задача по расписанию
from machine import Pin, Timer
led = Pin(2, Pin.OUT)
def blink(timer):
led.value(not led.value())
tim = Timer(0)
tim.init(period=500, mode=Timer.PERIODIC, callback=blink)
# светодиод мигает каждые 500 мс сам по себеТаймер мигает светодиодом «в фоне», а основной цикл свободен для другой логики.
Как работает под капотом
В обычном режиме процессор выполняет ваш код строка за строкой. У ESP32 есть контроллер прерываний: когда настроенное событие происходит (фронт на пине, переполнение таймера), он приостанавливает текущую работу, сохраняет состояние, выполняет ваш обработчик, а потом возвращается ровно туда, где остановился. Поэтому реакция почти мгновенная — не нужно ждать конца цикла.
Золотое правило обработчиков
Обработчик прерывания должен быть максимально коротким. Внутри нельзя делать долгие операции (сеть, печать, sleep) и создавать объекты. Правильный приём — выставить флаг или увеличить счётчик, а тяжёлую работу сделать в основном цикле:
flag = False
def handler(pin):
global flag
flag = True # только флаг!
while True:
if flag:
flag = False
# тут безопасно делать долгие дела
print("Событие обработано")Частые ошибки
- Тяжёлый код в обработчике. Печать, сеть, выделение памяти внутри irq приводят к сбоям.
- Забыть
global. Без него обработчик не изменит внешнюю переменную. - Не учитывать дребезг. Прерывание от кнопки тоже срабатывает на дребезг — добавьте проверку времени.
Итог
- Прерывания реагируют на события мгновенно, не загружая основной цикл.
pin.irq()ловит фронты,Timerвыполняет задачу по расписанию.- Обработчик должен быть коротким: лучше выставить флаг.
- Кнопочные прерывания тоже надо защищать от дребезга.