Когда используется прерывание в ардуино
Перейти к содержимому

Когда используется прерывание в ардуино

  • автор:

Arduino.ru

Задает функцию обработки внешнего прерывания, то есть функция, которая будет вызвана по внешнему прерыванию. Если до это была задана другая функция, то назначается новая. Большинство контроллеров Arduino умеют обрабатывать до двух внешних прерываний, пронумерованных так: 0 (на цифровом порту 2) и 1 (на цифровом порту 3). Arduino Mega обрабатывает дополнительно еще четыра прерывания: 2 (порт 21), 3 (порт 20), 4 (порт 19) и 5 (порт 18).

Плата int.0 int.1 int.2 int.3 int.4 int.5
UNO, Ethernet 2 3
Mega2560 2 3 21 20 19 18
Leonardo 3 2 0 1 7

Arduino Due имеет мощные возможности обработки прерываний, что позволяет прикрепить функцию прерывания для всех доступных контактов. Вы можете напрямую указать порт(пин) в attachInterrupt ().

Параметры

interrupt: номер прерывания (int)
или pin: номер цифрового порта (только для Arduino Due)

function: функция, вызваемая прерыванием, функция должна быть без параметров и не возвращать значений. В англоязычной документации употребляется термин interrupt service routine для такой функции.

mode задает режим обработки прерывания. Допустимо использование следующих констанст:

  • LOW вызывает прерывание, когда на порту LOW
  • CHANGE прерывание вызывается при смене значения на порту, с LOW на HIGH и наоборот
  • RISING прерывание вызывается только при смене значения на порту с LOW на HIGH
  • FALLING прерывание вызывается только при смене значения на порту с HIGH на LOW
Возвращаемое значение
Замечание по использованию

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данный передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.

Использование перерываний

Прерывания обычно используют для задач, которые должны быть выполнены автоматически при наступление какого либо внешнего воздействия. Например, считывание значения энкодера (датчика угла) или реакция на действия пользователя.

Так, если вы хотите считывать значения энкодера без использования прерываний, то написать программу, которая бы не пропускала ни одного импульса, практически невозможно, такая программа должна будет практически все время считывать значения с датчика, чтобы не пропусть импульс. На другие операции просто не останется процессорного времени. Это же относится и к другим датчикам, выдающим короткий импульс. В таких задачах использования внешних прерываний позволяет разгрузить процессор для других операций и не пропустить ожидаемый сигнал.

Пример

int pin = 13; volatile int state = LOW; void setup() < pinMode(pin, OUTPUT); attachInterrupt(0, blink, CHANGE); >void loop() < digitalWrite(pin, state); >void blink()

Смотрите также

Когда используется прерывание в ардуино

Задает функцию, которую необходимо вызвать при возникновении внешнего прерывания. Заменяет предыдущую функцию, если таковая была ранее ассоциирована с прерыванием. В большинстве плат Ардуино существует два внешних прерывания: номер 0 (цифровой вывод 2) и 1 (цифровой вывод 3). Номера выводов для внешних прерываний, доступные в тех или иных платах Ардуино, приведены в таблице ниже:

Плата int.0 int.1 int.2 int.3 int.4 int.5
Uno, Ethernet 2 3
Mega2560 2 3 21 20 19 18
Leonardo 3 2 0 1 7
Due (см. ниже)

Плата Arduino Due предоставляет больше возможностей по работе с прерываниями, поскольку позволяют ассоциировать функцию-обработчик прерывания с любым из доступных выводов. При этом в функции attachInterrupt() можно непосредственно указывать номер вывода.

Синтаксис

attachInterrupt(interrupt, function, mode) attachInterrupt(pin, function, mode) (только для Arduino Due)

Параметры

определяет условие, при котором должно срабатывать прерывание. Может принимать одно из четырех предопределенных значений:

  • LOW — прерывание будет срабатывать всякий раз, когда на выводе присутствует низкий уровень сигнала
  • CHANGE — прерывание будет срабатывать всякий раз, когда меняется состояние вывода
  • RISING — прерывание сработает, когда состояние вывода изменится с низкого уровня на высокий
  • FALLING — прерывание сработает, когда состояние вывода изменится с высокого уровня на низкий.

В Arduino Due доступно еще одно значение:

  • HIGH — прерывание будет срабатывать всякий раз, когда на выводе присутствует высокий уровень сигнала (только для Arduino Due).

Возвращаемые значения

Примечание

Внутри функции-обработчика прерывания функция delay() не будет работать; значения, возвращаемые функцией millis(), не будут увеличиваться. Также будут потеряны данные, полученные по последовательному интерфейсу во время выполнения обработчика прерывания. Любые переменные, которые изменяются внутри функции обработчика должны быть объявлены как volatile.

Использование прерываний

Прерывания могут использоваться для того, чтобы заставить микроконтроллер выполнять определенные участки программы автоматически и позволяют решить проблемы с синхронизацией. Типичными задачами, требующими использования прерываний, являются считывание сигнала энкодера (датчика вращения) либо мониторинг воздействий пользователя.

В программе, от которой требуется постоянно обрабатывать сигнал от датчика вращения (не пропуская при этом ни единого импульса), очень сложно реализовать еще какую-либо функциональность помимо опроса. Это объясняется тем, что для своевременной обработки поступающих импульсов, программа должна постоянно опрашивать выводы, к которым подключен энкодер. При работе с другими типами датчиков часто требуется подобный динамический интерфейс: например, при опросе звукового датчика с целью различить щелчок, или при работе с инфракрасным щелевым датчиком (фото-прерывателем) для распознавания момента броска монеты. Во всех этих примерах использование прерываний позволит разгрузить микроконтроллер для выполнения другой работы, при этом не теряя контроль над поступающими сигналами.

Пример

int pin = 13; volatile int state = LOW; void setup() < pinMode(pin, OUTPUT); attachInterrupt(0, blink, CHANGE); >void loop() < digitalWrite(pin, state); >void blink()

Смотрите также

Arduino

Прерывание (interrupt) — это сигнал, сообщающий процессору о наступлении некоторого события, требующего немедленной обработки. Процессор приостанавливает текущую активность, сохраняя свое состояние, передает управление обработчику прерывания, после чего возвращает управление в прерванный код.

Для работы с прерываниями в Arduino есть 2 функции:

  • attachInterrupt()
  • detachInterrupt()
Функция attachInterrupt()
Описание

Задает функцию, которая будет вызвана по внешнему прерыванию. Если функция уже была задана, то она будет заменена на новую. Рекомендуется использовать функцию digitalPinToInterrupt(pin) для перевода номера пина в номер прерывания. Например, если вы подключены к пину 3, используйте digitalPinToInterrupt(3) в качестве первого параметра attachInterrupt() .

Плата Arduino Пины, используемые для прерываний
Arduino Uno, Arduino Nano, Arduino Mini и остальные на ATmega328 2, 3
Arduino Mega, Arduino Mega2560 2, 3, 18, 19, 20, 21
Arduino Micro, Arduino Leonardo и остальные на ATmega32U4 0, 1, 2, 3, 7
Arduino Zero все цифровые пины кроме 4
Arduino Due все цифровые пины

Прерывания полезны, когда нужно читать значения с некоторых датчиков или обслуживать ввода пользователя.

ISR (Interrupt Service Routine) — это особые функции, которые имеют некоторые уникальные ограничения, которых нет у большинства других функций. ISR не может иметь никаких параметров, и они не должны ничего возвращать.

Как правило, функция ISR должна быть максимально короткой и быстрой. Если в одной программе задано несколько ISR-функций, то одновременно выполняться может только одна, остальные будут выполняться после завершения текущей в порядке, заданным приоритетом. Функция millis() использует прерывания для подсчета, поэтому возвращаемое ей значение не будет увеличиваться внутри ISR. Поскольку функции delay() для работы требуются прерывания, он тоже не будет работать. micros() сначала будет работает, но через 1-2 мс начнет возвращать неверный беспорядочный результат. delayMicroseconds() не использует счетчик, поэтому она будет работать как обычно.

Обычно для передачи данных между функциями ISR и основной программой используются глобальные переменные. Чтобы быть уверенными, что переменные, общие для ISR и основной программы, будут работать правильно, следует объявить их как volatile .

Синтаксис

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) — правильное использование

attachInterrupt(interrupt, ISR, mode) — так использовать не рекомендуется

attachInterrupt(pin, ISR, mode) — так использовать не рекомендуется (только для плат Arduino SAMD, Arduino Uno WiFi Rev2, Arduino Due, Arduino 101)

Параметры

interrupt — номер прерывания

pin — номер пина

ISR — функция для вызова при возникновении прерывания; эта функция не должна принимать никаких параметров и ничего не должна возвращать; эту функцию иногда называют процедурой обработки прерываний

mode — определяет, когда прерывание должно быть запущено

Допустимые значения параметра mode :

  • LOW — вызов прерывания когда на пине значение LOW
  • CHANGE — вызов прерывания когда на пине меняется значение
  • RISING — вызов когда значение переходит от LOW к HIGH
  • FALLING — вызов когда значение переходит от HIGH к LOW
  • HIGH (доступно только для Arduino Due, Arduino Zero) — вызов прерывания когда на пине значение HIGH
Возвращаемое значение
Пример

Зажигаем и гасим встроенный светодиод при изменении сигнала на 2 пине:

const byte ledPin = 13; const byte interruptPin = 2; volatile byte state = LOW; void setup() < pinMode(ledPin, OUTPUT); pinMode(interruptPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE); >void loop() < digitalWrite(ledPin, state); >void blink()
Примечания

Внутри функции, вызываемой по прерыванию, функции delay() работать не будет, а возвращаемое функцией millis() значение не будет увеличиваться. Последовательные данные, полученные во время использования функции, могут быть потеряны. Вы должны объявить как volatile любые переменные, которые вы изменяете в функции.

Функция detachInterrupt()
Описание
Синтаксис

detachInterrupt(digitalPinToInterrupt(pin)) — правильное использование

detachInterrupt(interrupt) — так использовать не рекомендуется

detachInterrupt(pin) — так использовать не рекомендуется (только для плат Arduino SAMD, Arduino Uno WiFi Rev2, Arduino Due, Arduino 101)

Параметры

interrupt — номер прерывания, которое нужно отключить

pin — номер пина прерывания для отключения

Как использовать прерывания на Arduino

Как использовать внешние прерывания в Arduino

Начиная работать с Arduino, пройдет не много времени, как вы обнаружите необходимость использования аппаратных прерываний или прерываний по таймеру. Прерывания могут помочь, когда вы хотите, чтобы Arduino выполнял несколько операций одновременно. Прерывания заставят плату Arduino прекратить то, что она делает, для выполнения другой задачи. Как только задача будет завершена, Arduino возобновит то, что делала до того, как была прервана.

В этой статье мы узнаем, как использовать два разных типа прерываний: аппаратные прерывания и прерывания по таймеру. Аппаратные прерывания запускаются внешним событием, таким как нажатие кнопки или сигнал от датчика. Прерывания по таймеру запускаются одним из внутренних таймеров Arduino.

Но прежде чем мы начнем в этом разбираться, давайте сначала рассмотрим пример того, почему прерывания полезны.

Мигающие светодиоды без прерываний

Давайте создадим пример проекта, который пытается управлять мигающим светодиодом с помощью кнопки. В приведенной ниже схеме синий светодиод будет включаться и выключаться постоянно каждые 300 миллисекунд. Желтый светодиод загорится тогда, когда вы нажмете на кнопку.

Для этого нам понадобятся следующие компоненты:

  • Arduino Nano (или другая плата Arduino)
  • Макетная плата
  • Перемычки
  • Кнопка
  • Резистор 330 Ом — 2 шт.
  • Светодиод — 2 шт.

Схема подключения в программе Proteus:

Схема для проверки внешних прерываний Arduino

Собираем на макетной плате:

Вот код для схемы:

int btnPin = 7; // Кнопка подключена к выводу D7 int btnLED = 11; // Контакт для желтого светодиода int blinkLED = 12; // Контакт для синего светодиода void setup() < pinMode(btnPin, INPUT_PULLUP); // Настройка входа с подтягивающим резистором pinMode(btnLED, OUTPUT); // Настройка контакта на выход pinMode(blinkLED, OUTPUT); // Настройка контакта на выход >void loop() < int btnState = digitalRead(btnPin); // Считывание состояния кнопки /* Если кнопка не нажата, то светодиод не должен гореть */ if (btnState == HIGH) < digitalWrite(btnLED, LOW); >/* Если кнопка нажата, то зажигаем желтый светодиод */ if (btnState == LOW) < digitalWrite(btnLED, HIGH); >/* Помигаем синим светодиодом */ digitalWrite(blinkLED, HIGH); delay(300); digitalWrite(blinkLED, LOW); delay(300); >

Если вы соберете эту схему и загрузите код в Ардуино, то увидите, что синий светодиод мигает и гаснет постоянно. А вот когда вы нажимаете кнопку, желтый светодиод иногда загорается, а иногда — нет. Это связано с использованием функции delay() . Когда контроллер вызывает эту функцию, он делает паузу и больше ничего не «хочет» выполнять, до того времени пока не «отсчитает» 300 миллисекунд. Поэтому он пропускает некоторые нажатия на кнопку.

Давайте исправим эту проблему с помощью аппаратного прерывания.

Как создать аппаратное прерывание на Ардуино

Скетч ниже добавляет аппаратное прерывание к приведенному выше скетчу с мигающим светодиодом, так, что каждое нажатие кнопки будет обработано платой Arduino:

int btnPin = 2; // Контакт для кнопки, который поддерживает прерывания int btnLED = 11; // Контакт для светодиода, который будет включаться при нажатии кнопки int blinkLED = 12; // Контакт для светодиода, который будет мигать постоянно volatile int btnState; // Переменная для хранения состояния кнопки /* Функция-обработчик прерываний */ void btnInterrupt() < btnState = digitalRead(btnPin); // Считываем состояние кнопки if (btnState == HIGH) < // Если кнопка не нажата digitalWrite(btnLED, LOW); // светодиод выключен >if (btnState == LOW) < // Если кнопка нажата digitalWrite(btnLED, HIGH); // светодиод включен >> void setup() < pinMode(btnPin, INPUT_PULLUP); // Настройка вывода как вход с подтяжкой pinMode(btnLED, OUTPUT); // Настройка вывода как выход pinMode(blinkLED, OUTPUT); // Настройка вывода как выход attachInterrupt(digitalPinToInterrupt(btnPin), btnInterrupt, CHANGE); // Установка внешнего прерывания >void loop() < /* Мигающий светодиод */ digitalWrite(blinkLED, HIGH); delay(300); digitalWrite(blinkLED, LOW); delay(300); >

Чтобы создать прерывание, вам сначала нужно написать специальную функцию, называемую обработчиком прерываний (ISR). Обработчик прерываний будет содержать весь код, который вы хотите выполнить при срабатывании прерывания. В примере с мигающим светодиодом, приведенном ранее, мы хотели, чтобы нажатия на кнопку управляли желтым светодиодом, в то время как синий светодиод мигал постояно.

В приведенном выше скетче есть функция ISR для нажатия кнопки, называемая btnInterrupt() . Внутри ISR-функции находится код, который считывает состояние кнопки и включает или выключает желтый светодиод.

Обработчик прерываний ISR в Arduino

Функции-обработчики содержат код, который вы хотите выполнить при срабатывании прерывания. Код должен быть максимально коротким и быстрым.

Если вы используете несколько прерываний, имейте в виду, что одновременно может выполняться только одно прерывание.

Функции millis() , micros() и delay() сами зависят от прерываний, поэтому они не будут работать внутри функции-обработчике. Но если вам нужна задержка в ISR-функции, то вы для этого можете использовать функцию delayMicroseconds() . Кроме того, функция Serial.print() не всегда работает внутри ISR-функции, потому что данные передаются на последовательный монитор с использованием прерывания. Обработчики прерываний не могут принимать входные данные или возвращать какие-либо значения.

Если у вас есть какие-либо переменные в обработчике (например, btnState в скетче сверху), перед ними должно стоять ключевое слово volatile . Объявление переменной как volatile предотвращает ее оптимизацию компилятором. В противном случае значение переменной может быть неточным. Ключевое слово volatile гарантирует, что переменная будет обновлена, если она будет изменена в другой части скетча.

Как запустить обработчик прерывания на Ардуино

Чтобы запустить процедуру обслуживания прерываний, необходимо вызвать функцию attachInterrupt() в функции setup() . Функция attachInterrupt() принимает три параметра.

Параметры функции attachInterrupt()

Первый параметр — это номер прерывания. Arduino Nano (также как и Uno) имеет два прерывания: прерывание 0 и прерывание 1. Прерывание 0 подключается к цифровому выводу D2, а прерывание 1 подключается к цифровому выводу D3.

attachInterrupt(0, btnInterrupt, CHANGE); // Настройка прерывания 0. Жестко указываем его в первом параметре

Чтобы было проще запомнить, какое прерывание к какому выводу подключено, существует функция digitalPinToInterrupt(), которую вы можете использовать вместо номера прерывания. Функция digitalPinToInterrupt() принимает цифровой номер вывода (2 или 3 для вывода 2 или 3 соответственно) вместо номера прерывания в качестве своего параметра.

attachInterrupt(digitalPinToInterrupt(2), btnInterrupt, CHANGE); // Настройка прерывания 0. В качестве первого параметра передается функция digitalPinToInterrupt с параметром 2 (2 - это контакт D2 платы Arduino)

Таким образом, внешнее прерывание для цифрового контакта D2 устанавливаем вот так:

attachInterrupt(0, btnInterrupt, CHANGE);
attachInterrupt(digitalPinToInterrupt(2), btnInterrupt, CHANGE);

Для цифрового контакт D3:

attachInterrupt(1, btnInterrupt, CHANGE);
attachInterrupt(digitalPinToInterrupt(3), btnInterrupt, CHANGE);

Второй параметр, используемый функцией attachInterrupt() , — это имя функции-обработчика прерывания. В приведенном выше скетче имя функции — btnInterrupt .

Третий параметр функции attachInterrupt() — это режим прерывания. Режим прерывания определяет тип сигнала, который вызовет прерывание. Существует четыре различных режима прерывания:

  • LOW — прерывание будет срабатывать всякий раз, когда на выводе прерывания будет низкий уровень LOW ;
  • RISING — прерывание будет срабатывать, когда сигнал переходит из низкого уровня LOW в высокий уровень HIGH ;
  • FALLING — прерывание будет вызвано, когда сигнал изменится с высокого уровня HIGH на низкий уровень LOW ;
  • CHANGE — прерывание будет срабатывать, когда сигнал переходит либо из высокого уровня HIGH в низкий уровень LOW , либо из низкого уровня LOW в высокий уровень HIGH .

Прерывания по таймеру в Ардуино

Аппаратные прерывания запускаются внешним событием, например нажатием кнопки. А вот прерывания по таймеру запускаются внутренними часами Arduino. Прежде чем мы сможем по-настоящему понять, как работают прерывания по таймеру, нам нужна небольшая справочная информация о таймерах Arduino.

Таймеры — это электронные схемы, встроенные в микроконтроллер, которые отсчитывают время. Прерывания по таймеру обычно используются для чтения или записи состояния контактов через определенные промежутки времени. Например, можно использовать прерывание по таймеру для получения показаний датчика влажности каждые тридцать секунд.

В Arduino есть три таймера – Timer0 , Timer1 и Timer2 :

  • Timer0 — 8-битный таймер, используемый для функций delay() , millis() и micros() ;
  • Timer1 — 16-битный таймер;
  • Timer2 — 8-битный таймер.

Не используйте Timer0 для прерываний, иначе это может привести к нарушению функций delay() , millis() и micros() . Вместо этого используйте Timer1 или Timer2 .

Все три таймера работают на тактовой частоте Arduino, которая составляет 16 МГц. Каждый такт засчитывается как один тик таймера. Timer1 — это 16-разрядный таймер, что означает, что он может хранить максимум 2 16 или 65 536 отсчетов, прежде чем он заполнится. Когда он достигает 65 536 отсчетов, таймер сбрасывается на ноль и начинает отсчет снова.

Timer0 и Timer2 являются 8-битными таймерами, поэтому они могут хранить максимум 2 8 или 256 отсчетов, прежде чем они будут заполнены. Скорость работы таймера можно замедлить, используя предварительно заданное значение — предделитель таймера. Изменяя предварительно значение предделителя, вы можете настроить таймеры на различные отрезки времени.

Использование прерываний по таймеру

Чтобы продемонстрировать, как использовать прерывания таймера, давайте создадим проект с двумя мигающими светодиодами. Зеленый светодиод будет мигать с частотой 500 миллисекунд, а красный светодиод будет мигать с частотой 5 секунд.

Обычный метод мигания светодиодов использует функцию delay() . Но в данном случае мы будем использовать прерывание по таймеру для управления одним из светодиодов.

Вот схема подключения светодиодов к Ардуино для этого эксперимента:

Схема подключения двух светодиодов к ардуино

Чтобы упростить программирование, мы будем использовать библиотеку TimerOne.

Нажмите на кнопку ниже, чтобы загрузить ZIP-архив библиотеки и установить ее на свой компьютер.

Как только библиотека TimerOne будет установлена, можете загрузить этот код на свою плату Arduino:

#include int ledGreen = 4; int ledRed = 9; void setup() < pinMode(ledRed, OUTPUT); pinMode(ledGreen, OUTPUT); Timer1.initialize(5000000); Timer1.attachInterrupt(callback); >void callback() < digitalWrite(ledRed, !digitalRead(ledRed)); >void loop()

В верхней части скетча мы подключаем библиотеку TimerOne.h. Зеленый светодиод будет мигать каждые 500 миллисекунд. Красный светодиод будет включаться и выключаться каждые 5 секунд. Мы настроим прерывание таймера только для управления красным светодиодом.

В функции setup() мы настраиваем контакты 4 (переменная ledGreen ) и 9 (переменная ledRed ) в качестве выходов. Контакт 4 (переменная ledGreen ) будет использоваться в функции loop() для мигания зеленым светодиодом каждые 500 миллисекунд.

В функции setup() мы используем Timer1.initialize() для инициализации таймера. Аргумент функции initialize() устанавливает время до срабатывания прерывания в микросекундах. В приведенной выше программе аргумент функции initialize() равен 5000000 микросекунд, поэтому прерывание по таймеру будет срабатывать каждые пять секунд. В библиотеке Timer1 минимальный период прерывания составляет 1 микросекунду, а максимальный период прерывания — 8 388 480 микросекунд.

Затем мы указываем функцию-обработчик прерывания с помощью функции Timer1.attachInterrupt() . Функция attachInterrupt() принимает только один аргумент — это имя функции обработчика прерывания. В скетче выше эта функция называется callback() . Таким образом, каждый раз, когда таймер будет достигать пяти секунд, будет запускаться ISR-функция callback() .

Код для функции callback() содержит функцию digitalWrite() , которая включает и выключает красный светодиод. Обычно мы использовали бы функцию delay() для включения и выключения светодиода. Но одновременно может выполняться только одна функция обработки прерывания, и ко всему этому сама функция delay() использует прерывание, она не может быть использована внутри другого прерывания.

Поэтому нам нужен другой способ включать и выключать светодиод. Вместо использования функции delay() мы можем считать состояние вывода 9 и использовать его для установки нового состояния. Итак, мы используем !digitalRead(ledRed) в качестве второго параметра функции digitalWrite() . Восклицательный знак ! — это логический оператор для обозначения НЕ (NOT). Это приведет считыванию текущего состояния вывода 9, а затем к установке вывода 9 с противоположным значением. Если вывод 9 находится в высоком состоянии HIGH, то будет установлен низкий уровень LOW, а если на выводе 9 низкий уровень LOW, то будет установлен высокий уровень HIGH.

В функции loop() у нас есть код, который мигает зеленым светодиодом. Здесь, как и обычно, мы мигаем светодиодом с помощью функции delay() , чтобы сделать паузу между установкой высокого и низкого уровней на светодиоде.

Если все работает правильно, зеленый светодиод должен менять свое состояние каждые 500 миллисекунд, а красный светодиод — каждые 5 секунд. Это очень простой пример, но прерывания по таймеру полезны и во многих других случаях. Вместо того, чтобы использовать прерывания для включения светодиода, вы можете использовать его для считывания показаний датчика через определенные промежутки времени.

Вот как работает код в программе Proteus:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *