Что такое внешние и аппаратные прерывания в Ардуино? Как использовать прерывания в программе? Рассмотрим на простых примерах работу с прерываниями в Arduino IDE, в каких случаях можно использовать Interrupts и как правильно с ними обращаться. Представим несколько примеров программ с пояснениями и важными моментами, на которые следует обратить внимание при использовании прерываний в скетче.
СОДЕРЖАНИЕ
Необходимые компоненты:
- Arduino Uno / Arduino Nano / Arduino Mega
- светодиод
- тактовая кнопка
- резисторы
- макетная плата
- коннекторы
Дополнительные материалы на эту тему:
Прерывания можно объяснить простым примером из жизни. Например, почтальон должен сегодня принести вам очень важное письмо. Чтобы его быстрее прочитать, вы ходите проверять почтовый ящик каждые 10 минут. Таким образом вы будете тратить свое время не продуктивно. В случае микроконтроллера Ардуино, то это выглядит, как постоянный опрос какого либо датчика, на которое процессор также будет тратить ресурсы.
Второй вариант — это оставить записку почтальону, чтобы он положил письмо не в почтовый ящик, а передал вам лично в руки. В этом случае вы сможете заняться своим делом не отвлекаясь на постоянную проверку почтового ящика. Когда почтальон позвонит в дверь, вы будете вынуждены прервать свою работу, чтобы открыть дверь и получить письмо, а затем вернуться к своим делам. Поэтому это и называется прерыванием.
Внешние и аппаратные прерывания в Arduino
Прерывания в Arduino IDE работают аналогичным образом. Например, микроконтроллер может ждать, когда пользователь нажмет кнопку. Для этого в программе можно использовать команду digitalRead() и контролировать кнопку с высокой частотой. Но можно использовать аппаратное прерывание, не тратя время на опрос кнопки, но как только кнопка будет нажата, аппаратный сигнал запустит функцию внутри кода Arduino.

| платы Ардуино | пины, поддерживающие прерывания |
| Arduino Uno, Nano, Mini | 2, 3 |
| Arduino Mega | 2, 3, 18, 19, 20, 21 |
| Arduino Micro, Leonardo | 0, 1, 2, 3, 7 |
| Arduino Zero | все цифровые пины, кроме 4 |
| Arduino Due | все цифровые пины |
В таблице указаны пины, которые можно использовать для прерываний. Если вывод на плате не совместим с функцией прерывания, то программа работать не будет. Прерывания в Arduino IDE срабатывают при изменении цифрового сигнала на входе, который вы отслеживаете, но вы можете выбрать именно то изменение, которое хотите отслеживать. Для этого вам следует изменить третий параметр в функции attachInterrupt().
Синтаксис вызова: attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);
digitalPinToInterrupt(pin) — в этом параметре необходимо указать конкретный пин, который будет использоваться для обработки сигнала внешнего прерывания.
ISR — название функции, которая будет вызываться при прерывании
mode — тип перехода (изменения состояния), который будет обрабатываться, например, понижение уровня, повышение уровня и т.д. Возможны следующие значения:
- RISING: когда сигнал меняется с LOW на HIGH
- FALLING: когда сигнал меняется с HIGH на LOW
- CHANGE: при любом изменении сигнала (с LOW на HIGH или с HIGH на LOW)
- LOW: прерывание будет срабатывать каждый раз, когда сигнал LOW
Использование прерываний в Ардуино на примерах

В первом примере микроконтроллер постоянно следит за состоянием кнопки, и в момент нажатия включается светодиод. Но используя цифровой вход 2 в режиме прерывания, микроконтроллеру нет необходимости производить опрос кнопки. При изменении цифрового сигнала на пине, поддерживающего прерывание, Arduino остановит выполнение основного кода программы, при этом постоянного опроса кнопки не требуется.
Скетч для включения светодиода без прерывания
#define LED 10
#define BUTTON 2
byte ledState = LOW;
void setup() {
pinMode(LED, OUTPUT);
pinMode(BUTTON, INPUT);
}
void loop() {
if (digitalRead(BUTTON) == HIGH) {
ledState = !ledState;
}
digitalWrite(LED, ledState);
}
В следующем примере будет изменен способ контроля кнопки — к цифровому входу 2 будет прикреплена функция прерывания. Когда сигнал с кнопки возрастает, то есть меняется с LOW на HIGH, основной код программы в функции void loop() будет остановлен и будет вызвана функция прерывания void blinkLed(). Основное преимущество, которое мы получаем в этом случае — больше не требуется опрос кнопки в программе.
Скетч с использованием аппаратного прерывания
#define LED 10
#define BUTTON 2
volatile byte ledState = LOW;
void setup() {
pinMode(LED, OUTPUT);
pinMode(BUTTON, INPUT);
attachInterrupt(digitalPinToInterrupt(BUTTON), blinkLed, RISING);
}
void loop() {
// в основном цикле нет никаких команд
}
void blinkLed() {
ledState = !ledState;
digitalWrite(LED, ledState);
}
Что важно учесть при работе с прерываниями
Делайте функцию прерывания максимально быстрой
Функцию прерывания следует делать максимально быстрой, поскольку она останавливает выполнение основного кода вашей программы. Если необходимо выполнить тяжелые вычисления или длительные действия при срабатывании внешнего прерывания, то их следует оставить в цикле void loop(). А внутри функций прерывания следует изменять только переменные состояния, от которых зависит выполнение нужных операций.
Не используйте функции времени в прерываниях
Команда delay() в функции прерывания не будет работать. Если вы используете счетчик времени на millis(), то при выходе из функции прерывания, вы получите последнее сохраненное значение, так как внутри функции прерывания значение millis() не увеличивается. Возможно, использование millis() используется для предотвращения дребезжания кнопки, но лучше ее использовать в основном цикле void loop().
Не используйте монитор порта внутри прерываний
Когда вы находитесь внутри прерывания, данные отправленные или полученные через команды Serial могут быть потеряны. Можно использовать Serial.println() внутри функции прерывания для отладки программы, например, чтобы убедиться что прерывание сработало, но вы можете столкнуться с проблемами. Лучше просто установить флаг внутри прерывания и опросить этот флаг внутри основной программы void loop().
Для объявления переменных используйте volatile
Переменные, используемые внутри функций ISR и в обычных функциях, должны быть объявлены через volatile. Это сообщает компилятору, что переменные такого типа могут измениться в любое время, и микроконтроллер должен перезагружать переменную всякий раз, когда вы обращаетесь к ней, а не полагаться на ее копию. Иначе алгоритмы оптимизации могут удалить переменную полностью из исполняемого файла.
Заключение. Использование функции attachInterrupt Arduino очень полезно, когда необходимо быть уверенным, что микроконтроллер не пропустит никаких изменений в сигнале, который вы отслеживаете. Однако, при использовании прерывания по кнопке в Ардуино следует быть внимательным и не использовать их слишком часто. Иногда использование простого опроса датчика может быть более подходящим вариантом.
