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

Прерывание (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 все цифровые пины

Уроки Ардуино #16 — аппаратные прерывания

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

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-technology.ru

Аппаратные прерывания

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

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

External hardware interrupt – это прерывание, вызванное изменением напряжения на пине микроконтроллера. Основная суть состоит в том, что системное ядро микроконтроллера не занимается опросом пина и не тратит на это время. Но как только напряжение на пине меняется (цифровой сигнал) – микроконтроллер получает сигнал, бросает все дела, обрабатывает прерывание, и возвращается к работе.

Зачем это нужно? Чаще всего прерывания используются для детектирования коротких событий – импульсов, или даже для подсчёта их количества, не нагружая основной код. Аппаратное прерывание может поймать короткое нажатие кнопки или срабатывание датчика во время сложных долгих вычислений или задержек в коде, т.е. грубо говоря – пин опрашивается параллельно основному коду. Также прерывания могут будить МК из режимов энергосбережения, когда вообще практически вся периферия отключена. Посмотрим, как работать с аппаратными прерываниями в среде Arduino IDE.

Прерывания в Arduino

Arduino Nano (AVR)

У микроконтроллера есть возможность получать прерывания с любого пина, такие прерывания называются PCINT и работать с ними можно только при помощи сторонних библиотек (вот отличная), либо вручную (читай у меня вот тут). В этом уроке речь пойдёт об обычных прерываниях, которые называются INT, потому что стандартный фреймворк Ардуино умеет работать только с ними. Таких прерываний и соответствующих им пинов очень мало:

МК / номер прерывания INT 0 INT 1 INT 2 INT 3 INT 4 INT 5
ATmega 328/168 (Nano, UNO, Mini) D2 D3
ATmega 32U4 (Leonardo, Micro) D3 D2 D0 D1 D7
ATmega 2560 (Mega) D2 D3 D21 D20 D19 D18

Как вы поняли из таблицы, прерывания имеют свой номер, который отличается от номера пина. Есть кстати удобная функция digitalPinToInterrupt(pin) , которая принимает номер пина и возвращает номер прерывания. Скормив этой функции цифру 3 на Arduino Nano, мы получим 1 . Всё по таблице выше, функция для ленивых.

Wemos Mini (esp8266)

На esp8266 прерывание можно настроить стандартными средствами на любом пине.

Обработчик прерывания

Сначала нужно объявить функцию-обработчик прерывания, эта функция будет выполнена при срабатывании прерывания:

  • Для AVR Arduino это функция вида void имя()<>
  • Для ESP8266/32 функция создаётся с атрибутом IRAM_ATTR или ICACHE_RAM_ATTR . Подробнее читай в уроке про esp8266.

К коду внутри этой функции есть некоторые требования:

  • Переменные, которые изменяют своё значение в прерывании, должны быть объявлены со спецификатором volatile . Пример: volatile byte val;
  • Не работают задержки типа delay()
  • Не меняет своё значение millis() и micros()
  • Некорректно работает вывод в порт Serial.print()
  • Нужно стараться делать как можно меньше вычислений и вообще “долгих” действий – это будет тормозить работу МК при частых прерываниях:
  • Вычисления с float
  • Работа с динамической памятью (функции new(), malloc(), realloc() и прочие)
  • Работа со String-строками
Читайте также:
Кто в 1953 г выступил с программой ускоренного развития легкой промышленности и аграрного сектора

Подключение прерывания

Подключается прерывание при помощи функции attachInterrupt(pin, handler, mode) :

  • pin – пин прерывания
  • Для AVR Arduino это номер прерывания (см. таблицу выше)
  • Для ESP8266 это номер GPIO или D-пин на плате (как в уроке про цифровые пины)
  • RISING (рост) – срабатывает при изменении сигнала с LOW на HIGH
  • FALLING (падение) – срабатывает при изменении сигнала с HIGH на LOW
  • CHANGE (изменение) – срабатывает при изменении сигнала (с LOW на HIGH и наоборот)
  • LOW (низкий) – срабатывает постоянно при сигнале LOW(не поддерживается на ESP8266)

Прерывание можно отключить при помощи функции detachInterrupt(pin) .

Можно глобально запретить прерывания функцией noInterrupts() и снова разрешить их при помощи interrupts() . Аккуратнее с ними! noInterrupts() остановит также прерывания таймеров, и у вас “сломаются” все функции времени и генерация ШИМ.

Пример

Давайте рассмотрим пример, в котором в прерывании считаются нажатия кнопки, а в основном цикле они выводятся с задержкой в 1 секунду. Работая с кнопкой в обычном режиме, совместить такой грубый вывод с задержкой невозможно:

volatile int counter = 0; // переменная-счётчик void setup() < Serial.begin(9600); // открыли порт для связи // подключили кнопку на D2 и GND pinMode(2, INPUT_PULLUP); // FALLING — при нажатии на кнопку будет сигнал 0, его и ловим attachInterrupt(0, btnIsr, FALLING); >void btnIsr() < counter++; // + нажатие >void loop() < Serial.println(counter); // выводим delay(1000); // ждём >

Ловим событие

Если прерывание отлавливает какое-то короткое событие, которое необязательно обрабатывать сразу, то лучше использовать следующий алгоритм работы с прерыванием:

  • В обработчике прерывания просто поднимаем флаг ( volatile bool переменная)
  • В основном цикле программы проверяем флаг, если поднят – сбрасываем его и выполняем нужные действия

volatile bool intFlag = false; // флаг void setup() < Serial.begin(9600); // открыли порт для связи // подключили кнопку на D2 и GND pinMode(2, INPUT_PULLUP); attachInterrupt(0, buttonTick, FALLING); >void buttonTick() < intFlag = true; // подняли флаг прерывания >void loop() < if (intFlag) < intFlag = false; // сбрасываем // совершаем какие-то действия Serial.println(«Interrupt!»); >>

Следующий возможный сценарий: нам надо поймать сигнал с “датчика” и сразу на него отреагировать однократно до появления следующего сигнала. Если датчик – кнопка, нас поджидает дребезг контактов. С дребезгом лучше бороться аппаратно, но можно решить проблему программно: запомнить время нажатия и игнорировать последующие срабатывания. Рассмотрим пример, в котором прерывание будет настроено на изменение ( CHANGE ).

void setup() < // прерывание на D2 (UNO/NANO) attachInterrupt(0, isr, CHANGE); >volatile uint32_t debounce; void isr() < // оставим 100 мс таймаут на гашение дребезга // CHANGE не предоставляет состояние пина, // придётся узнать его при помощи digitalRead if (millis() — debounce >= 100 digitalRead(2)) < debounce = millis(); // ваш код по прерыванию по высокому сигналу >> void loop()

Вы скажете: но ведь millis() Не меняет значение в прерывании! Да, не меняет, но он меняется между прерываниями! Это в принципе всё, что нужно знать о прерываниях, более конкретные случаи мы разберём в продвинутых уроках.

Видео

Полезные страницы

  • Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
  • Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с Aliexpress у проверенных продавцов
  • Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
  • Полная документация по языку Ардуино, все встроенные функции и макросы, все доступные типы данных
  • Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
  • Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
  • Поддержать автора за работу над уроками
  • Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])
Читайте также:
Полезные программы для teyes cc3

Источник: alexgyver.ru

Прерывания. Урок 11. Ардуино

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

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

Для этого в Ардуино существуют прерывания.

В одном из последних обзоров датчиков, мы рассматривали датчик препятствий KY-033. Посмотрите тот обзор если вы пропустили или уже забыли, потому что в этом уроке мы будем его использовать.

Итак, прерывания. Они позволяют выполнять программу асинхронно. При наступлении определенного события — нажатия на кнопку, сигнала от датчика, они дают возможность остановить ход текущей программы, выполнить код прерывания, а затем вернуться к прерванной задаче.

Для того, чтобы выполнить этот урок нам понадобиться.

  • Ардуино UNO
  • Макетная плата
  • Перемычки
  • Датчик KY-033
  • 3 светодиода разного цвета
  • 3 резистора 220 Ом
  • Кабель USB

Прерывания

Аппаратные прерывания — это альтернатива постоянного сканирования состояния контактов в цикле loop(). Они не лучше и не хуже, вы всегда можете выбрать тот способ, который будет лучше работать в вашем проекте.

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

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

Программа

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

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

Кроме того, используем датчик препятствий из последнего обзора, он будет выполнять функцию бесконтактной кнопки.

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

Принципиальная схема подключения датчика

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

volatile

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

volatile int selectedLED = 9;

attachInterrupt

Далее создадим 0 прерывание. 0 прерывание связано с пином 2 на плате Ардуино Uno, поэтому датчик препятствий подключен именно ко 2 контакту.

void setup()

Теперь напишем функцию swap(), чтобы вызвать ее по прерыванию. Она будет переключать активный светодиод.

void swap()

И в конце осталось только написать бесконечный цикл в основном цикле программы. Он будет изменять яркость светодиодов.

void loop() < for (int i = 0; ifor (int i = 255; i>= 0; i—) < analogWrite(selectedLED, i); delay(10); >>

Полный текст программы

const int RED=9; const int YELLOW=10; const int GREEN=11; volatile int selectedLED = RED; void setup() < pinMode (RED, OUTPUT); pinMode (GREEN, OUTPUT); pinMode (YELLOW, OUTPUT); attachInterrupt(0, swap, RISING); >void swap() < analogWrite(selectedLED, 0); if (selectedLED == GREEN) selectedLED = RED; else if (selectedLED == RED) selectedLED = YELLOW; else if (selectedLED == YELLOW) selectedLED = GREEN; >void loop() < for (int i = 0; ifor (int i = 255; i>= 0; i—) < analogWrite(selectedLED, i); delay(10); >>

Заключение

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru