Как выясняется, существует отличный (но недостаточно часто используемый) механизм, встроенный во все Arduino, который идеально подходит для отслеживания событий в режиме реального времени. Данный механизм называется прерыванием. Работа прерывания заключается в том, чтобы убедиться, что процессор быстро отреагирует на важные события. При обнаружении определенного сигнала прерывание (как и следует из названия) прерывает всё, что делал процессор, и выполняет некоторый код, предназначенный для реагирования на вызвавшую его внешнюю причину, воздействующую на Arduino. После того, как этот код будет выполнен, процессор возвращается к тому, что он изначально делал, как будто ничего не случилось!
Что в этом удивительного, так это то, что прерывания позволяют организовать вашу программу так, чтобы быстро и эффективно реагировать на важные события, которые не так легко предусмотреть в цикле программы. И лучше всего это то, что прерывания позволяют процессору заниматься другими делами, а тратить время на ожидание события.
Уроки Ардуино #16 — аппаратные прерывания
Прерывания по кнопке
Начнем с простого примера: использования прерывания для отслеживания нажатия кнопки. Для начала, мы возьмем скетч, который вы, вероятно, уже видели: пример « Button », включенный в Arduino IDE (вы можете найти его в каталоге « Примеры », проверьте меню Файл → Примеры → 02. Digital → Button ).
const int buttonPin = 2; // номер вывода с кнопкой const int ledPin = 13; // номер вывода со светодиодом int buttonState = 0; // переменная для чтения состояния кнопки void setup() < // настроить вывод светодиода на выход: pinMode(ledPin, OUTPUT); // настроить вывод кнопки на вход: pinMode(buttonPin, INPUT); >void loop() < // считать состояние кнопки: buttonState = digitalRead(buttonPin); // проверить нажата ли кнопка. // если нажата, то buttonState равно HIGH: if (buttonState == HIGH) < // включить светодиод: digitalWrite(ledPin, HIGH); >else < // погасить светодиод: digitalWrite(ledPin, LOW); >>
В том, что вы видите здесь, нет ничего шокирующего и удивительного: всё, что программа делает снова и снова, это прохождение через цикл loop() и чтение значения buttonPin . Предположим на секунду, что вы хотели бы сделать в loop() что-то еще, что-то большее, чем просто чтение состояния вывода. Вот здесь и пригодится прерывание. Вместо того, чтобы постоянно наблюдать за состоянием вывода, мы можем поручить эту работу прерыванию и освободить loop() для выполнения в это время того, что нам необходимо! Новый код будет выглядеть следующим образом:
const int buttonPin = 2; // номер вывода с кнопкой const int ledPin = 13; // номер вывода со светодиодом volatile int buttonState = 0; // переменная для чтения состояния кнопки void setup() < // настроить вывод светодиода на выход: pinMode(ledPin, OUTPUT); // настроить вывод кнопки на вход: pinMode(buttonPin, INPUT); // прикрепить прерывание к вектору ISR attachInterrupt(0, pin_ISR, CHANGE); >void loop() < // Здесь ничего нет! >void pin_ISR()
Циклы и режимы прерываний
Здесь вы заметите несколько изменений. Первым и самым очевидным из них является то, что loop() теперь не содержит никаких инструкций! Мы можем обойтись без них, так как вся работа, которая ранее выполнялась в операторе if/else , теперь выполняется в новой функции pin_ISR() . Этот тип функций называется обработчиком прерывания: его работа состоит в том, чтобы быстро запуститься, обработать прерывание и позволить процессору вернуться обратно к основной программе (то есть к содержимому loop() ). При написании обработчика прерывания следует учитывать несколько важных моментов, отражение которых вы можете увидеть в приведенном выше коде:
❓ Обязательно к просмотру начинающему в Arduino IDE (ошибка компилятора)
- обработчики должны быть короткими и лаконичными. Вы ведь не хотите прерывать основной цикл надолго!
- у обработчиков нет входных параметров и возвращаемых значений. Все изменения должны быть выполнены на глобальных переменных.
Вам, наверное, интересно: откуда мы знаем, когда запустится прерывание? Что его вызывает? Третья функция, вызываемая в функции setup() , устанавливает прерывание для всей системы. Данная функция, attachInterrupt() , принимает три аргумента:
- вектор прерывания, который определяет, какой вывод может генерировать прерывание. Это не сам номер вывода, а ссылка на место в памяти, за которым процессор Arduino должен наблюдать, чтобы увидеть, не произошло ли прерывание. Данное пространство в этом векторе соответствует конкретному внешнему выводу, и не все выводы могут генерировать прерывание! На Arduino Uno генерировать прерывания могут выводы 2 и 3 с векторами прерываний 0 и 1, соответственно. Для получения списка выводов, которые могут генерировать прерывания, смотрите документацию на функцию attachInterrupt для Arduino;
- имя функции обработчика прерывания: определяет код, который будет запущен при совпадении условия срабатывания прерывания;
- режим прерывания, который определяет, какое действие на выводе вызывает прерывание. Arduino Uno поддерживает четыре режима прерывания:
- RISING – активирует прерывание по переднему фронту на выводе прерывания;
- FALLING – активирует прерывание по спаду;
- CHANGE – реагирует на любое изменение значения вывода прерывания;
- LOW – вызывает всякий раз, когда на выводе низкий уровень.
И резюмируя, наша настройка attachInterrupt() соответствует отслеживанию вектора прерывания 0 (вывод 2), чтобы отреагировать на прерывание с помощью pin_ISR() , и вызвать pin_ISR() всякий раз, когда произойдет изменение состояния на выводе 2.
Volatile
Еще один момент, на который стоит указать: наш обработчик прерывания использует переменную buttonState для хранения состояния вывода. Проверьте определение buttonState : вместо типа int , мы определили его, как тип volatile int . В чем же здесь дело? volatile является ключевым словом языка C, которое применяется к переменным. Оно означает, что значение переменной находится не под полным контролем программы. То есть значение buttonState может измениться и измениться на что-то, что сама программа не может предсказать – в этом случае, пользовательский ввод.
Еще одна полезная вещь в ключевом слове volatile заключается в защите от любой случайной оптимизации. Компиляторы, как выясняется, выполняют еще несколько дополнительных задач при преобразовании исходного кода программы в машинный исполняемый код. Одной из этих задач является удаление неиспользуемых в исходном коде переменных из машинного кода.
Так как переменная buttonState не используется или не вызывается напрямую в функциях loop() или setup() , существует риск того, что компилятор может удалить её, как неиспользуемую переменную. Очевидно, что это неправильно – нам необходима эта переменная! Ключевое слово volatile обладает побочным эффектом, сообщая компилятору, что эту переменную необходимо оставить в покое.
Удаление неиспользуемых переменных из кода – это функциональная особенность, а не баг компиляторов. Люди иногда оставляют в коде неиспользуемые переменные, которые занимают память. Это не такая большая проблема, если вы пишете программу на C для компьютера с гигабайтами оперативной памяти. Однако, на Arduino оперативная память ограничена, и вы не хотите тратить её впустую!
Даже C компиляторы для компьютеров будут поступать точно так же, несмотря на массу доступной системной памяти. Зачем? По той же причине, по которой люди убирают за собой после пикника – это хорошая практика, не оставлять после себя мусор.
Подводя итоги
Прерывания – это простой способ заставить вашу систему быстрее реагировать на чувствительные к времени задачи. Они также обладают дополнительным преимуществом – освобождением главного цикла loop() , что позволяет сосредоточить в нем выполнение основной задачи системы (я считаю, что использование прерываний, как правило, позволяет сделать мой код немного более организованным: проще увидеть, для чего разработан основной кусок кода, и какие периодические события обрабатываются прерываниями). Пример, показанный здесь, – это самый базовый случай использования прерываний; вы можете использовать для чтения данных с I2C устройства, беспроводных передачи и приема данных, или даже для запуска или остановки двигателя.
Есть какие-нибудь крутые проекты с прерываниями? Оставляйте комментарии ниже!
Источник: radioprog.ru
Как остановить выполнение кода?
Всем привет!
Для отладки нужно, чтобы ардуино останавливал выполнение скетча полностью. Есть ли какой-то путь для решения этой проблемы кроме delay с бешеным интервалом?
Сам спросил, сам же и отвечу. Вдруг кому-нибудь пригодится.
Надо загнать ардуино в пустой цикл:
ардуино для остановки не предназначен
если уж нужно игнорировать какой то код — то код выделяется в отдельную функцию/процедуру и перед её выполнением ставится условие на проверку флага =) флаг — глобальная переменная, которая для игнорирования кода должна быть выставлена в false
а цикл while(1) — это попытка загрузить ардуину пустым циклом.
в далёких 90х после таких команд компьютер зависал наглухо =)
Источник: www.drive2.ru
Arduino.ru
Останавливает выполнение программы на заданное в параметре количество миллисекунд (1000 миллисекунд в 1 секунде).
Синтаксис
delay(ms)
Параметры
ms: количество миллисекунд, на которое приостанавливается выполнение программы. (unsigned long)
Возвращаемое значение
Пример
int ledPin = 13; // светодиод подключен на порт 13 void setup() < pinMode(ledPin, OUTPUT); // устанавливается режим порта — выход >void loop() < digitalWrite(ledPin, HIGH); // включаем светодиод delay(1000); // ожидаем секунду digitalWrite(ledPin, LOW); // выключаем светодиод delay(1000); // ожидаем секунду >
Замечания по использования функции
Не рекомендуется использовать эту функцию для событий длиннее 10 миллисекунд, т.к. во время останова, не могут быть произведены манипуляции с портам, не могут быть считаны сенсоры или произведены математические операции. В качестве альтернативного подхода возможно контролирование времени выполнения тех или иных функций с помощью millis().
Большинство активности платы останавливается функцией delay(). Тем не менее работа прерываний не останавливается, продолжается запись последовательно (serial) передаваемых данных на RX порту, ШИМ сигнал (analogWrite) продолжает генерироваться на портах.
Источник: arduino.ru