Большинство информации можно найти на страницах arduino.cc по ходу текста буду добавлять ссылки, где я нашел информацию.
Переходим к прерываниям.
В случае, когда основная программа имеет сравнительно небольшой размер, все события можно обрабатывать в цикле основной программы, но что если программа большая? Если ее размер множество строк, да еще и с временными задержками? В таком случае, возможна ситуация когда внешние нажатия на кнопку могут быть не обработаны или придется долго держать кнопку нажатой. А что если это не кнопка, а внешнее устройство? Чтоб преодолеть эти сложности необходимо использовать прерывания — как только было вызвано прерывание, основной цикл программы будет приостановлен и микроконтроллер начнет исполнение кода функции, которая соответствует прерыванию, а после завершения продолжится выполнение программы там где она была до этого приостановлена.
На платах Arduino есть контакты, используются для сигнализации о прерывании. Ниже в таблице указаны номера прерываний и соответствующие им контакты на плате.
Платы | Контакты | |||||
int0 | int1 | int2 | int3 | int4 | int5 | |
Arduino Uno | 2 | 3 | ||||
Arduino Mega | 2 | 3 | 21 | 20 | 19 | 18 |
Для работы с прерываниями нет необходимости подключать какие-либо библиотеки — все они уже включены автоматически.
Синтаксис:
attachInterrupt(interrupt, function, mode)
Параметры:
interrupt — номер вызываемого прерывания (0, 1 и т.д.).
function — называние функции, которая будет вызываться при срабатывании прерывания. Важно: Данная функция не должна получать параметры при запуске или возвращать какие-либо значения!
mode — режим. Возможные значения LOW, CHANGE, FALLING, RISING
Режим определяет как интерпретировать сигнал на контакте отвечающем за прерывания, в каком случае вызывать обработчик.
LOW — режим в котором прерывание вызывается тогда, когда значение на контакте будет нулевое. Вызываемая функция будет срабатывать снова и снова до тех пор, пока условие выполняется. Данный режим сильно отличается от последующих, он рассчитан на постоянные срабатывания, остальные — на единичное.
Чтоб понять как и когда вызывается функция можно использовать следующий скетч.
/* http://compblog.vlukyanov.com */ #include <LiquidCrystal.h> int buttonInt = 0; int screenLed = 4; LiquidCrystal lcd(11, 10, 9, 8, 7, 6); void setup() { attachInterrupt(buttonInt, screenon, LOW); pinMode(screenLed, OUTPUT); lcd.begin(16, 2); digitalWrite(screenLed,HIGH); } void screenon() { lcd.print("11111111111111111"); } void loop() { lcd.print("00000000000000000"); delay(100); }
В основном цикле программы на экран выводятся нули, но когда на входе прерывания сигнал LOW (кнопка отпущена), на экран выводятся единицы.
Т.е. Нажатие на кнопку останавливает повторяющиеся прерывания и запускает основной цикл программы.
CHANGE — прерывание вызывается при смене сигнала от LOW к HIGH и наоборот. Функция выполняется только один раз при любой смене сигнала.
Вероятнее всего прерывание необходимо будет выполнять только один раз и поэтому два ниже следующих прерывания будут наиболее востребованы.
RISING — прерывание вызывается когда значение меняется от LOW к HIGH. Разовый вызов функции, на каждый удовлетворяющий вызову переход.
FALLING — прерывание вызывается когда значение меняется от HIGH к LOW. Разовый вызов функции, на каждый удовлетворяющий вызову переход.
Сигнал прерывания имеет внешнее по отношению к Arduino происхождение. Его можно сравнить с периферийными устройствами компьютера. Для демонстрации я использовал кнопку и думаю это весьма распространенный способ запуска прерывания.
Для демонстрации режима LOW я использовал следующую схему с тактовой кнопкой. —> Данная схема включает кнопку S1 и стягивающий резистор R1 на 10кОм. Резистор обеспечит отсутствие статических разрядов создающих «шум» и запускающих прерывание в случайном порядке. Но при этом есть один большой недостаток данной схемы — кнопка не сразу выдает значение HIGH/LOW когда была нажата/отпущена. Есть некоторые колебания сигнала перед тем как через кнопку пойдет/прекратит_движение ток. ![]() Вот тут есть хорошая статья как этого избежать. |
![]() Схема 1.
|
Вышеприведенная схема подходит для демонстрации вызова прерывания по LOW, т.к. первичные колебания не значительны. Но она абсолютно не подходит для остальных режимов. Функция будет запущена множество раз, что может привести к некорректной работе программы. Чтоб правильно вызывать прерывания, необходимо позаботится о корректной передаче сигнала. Подробнее о схеме: S1 — Кнопка. R1 — Стягивающий резистор. C1 — Конденсатор на 1микрофарад для сглаживания сигнала иначе значения с кнопки так и будут скакать. OR1 — логическое или. Получив сигнал с кнопки сглаженный конденсатором он выдаст логическую единицу(HIGH) или ноль (LOW). Подойдет не только логический элемент «или», можно использовать «и», «не», триггер Шмидта или инвертирующий триггер Шмидта. Эти компоненты среагируют только при переходе сигналом определенной границы. |
![]() Схема 2.
|
Теория на этом заканчивается. Переходим к практике.
Светодиод подключенный к плате постепенно увеличивает яркость свечения, потом сбрасывается на ноль и все повторяется. Нажатие на кнопку приведет к фиксации текущей яркости на 3 секунды.
Кнопка подключена по первой схеме указанной выше. Поэтому имеет все описанные недостатки.
/* Interrupt / Прерывания http://compblog.vlukyanov.com */ byte intPin = 0; // Номер прерывания, которое будет вызыватся. // Не номер контакта! // Контакт которому соответствует прерывание 2. byte ledPin = 3; // Контакт с ШИМ для подключения светодиода. volatile int x = 0; // Переменная для подсчета времени, // когда необходимо вновь запустить изменение яркости диода. // Обязательно указание директивы volatile, иначе могут появится ошибки. // volotile означает возможность внезапного, для основной программы, изменения переменной. // В данном случае изменение произойдет в прерывании. // Основные параметры платы. void setup() { attachInterrupt(intPin,pause,RISING); // Параметры прерывания. pinMode(ledPin, OUTPUT); } // Функция которая будет выполнятся при нажатии на кнопку. void pause() { x = millis()/1000+3; // К текущему времени работы платы добавляем 3 секунды и запомиаем. // millis - функция, возвращающая время работы платы в миллисекундах. } // Основная программа. void loop() { for (int i = 0; i<64; i++) // Запускаем цикл на 65 оборотов. { analogWrite(ledPin,i); // Устанавлеваем яркость светодиода. delay(40); // Притормаживаем немного наш цикл, чтоб светодиод не мерцал, а плавно становился ярче. while (x > millis()/1000) delay(100); // х может быть больше текущего времени только если вызывалось прерывание. // Проверка на прошествие трех секунд, проводится 10 раз в секунду. // Данный цикл крутится пока не пройдет три секунды, значение светодиода не изменяется. } }
С прерываниями связанна еще одна важная функция
detachInterrupt(interrupt)
Запрещает вызов определенного прерывания.
Может использоваться когда необходимо выполнять какие-либо действия без остановки на выполнение прерываний.
Ссылки:
http://arduino.cc/en/Reference/AttachInterrupt
http://www.labbookpages.co.uk/electronics/debounce.html
P.s. Пока писал эту статью привезли тригеры Шмидта, регистры и прочие логические микросхемы. 🙂
ДобРый День Владимир !!
Можно ли Вам Задать Вопрос пО Кнопкам- Возникла Проблема Подключения — Проект С Купюроприемником NV 9 — Кнопка Отказывается реагировать В Железе ))-это видимо связано как то
attachInterrupt ( функция по счету импульсов с купюроприемника ) -программа компилируется но кнопка не работает — Спасибо — С уважением Заза
Сорри, я упустил этот комментарий 🙁
Если еще нужна какая-то помощь я могу попробовать помочь.
Спасибо за статью!
Скажите, а почему нельзя просто подать сигнал от кнопки на пин, без подключения дополнительной логики? Пин ведь контроллер ведь тоже сработает при определенном уровне сигнала, как и логика…
[…] Как сделать такую кнопку подробно описано вот тут: http://compblog.vlukyanov.com/?p=435 Кнопка подключена по этой схеме также ко второму […]