3.4.md
РАБОТА С ПРЕРЫВАНИЯМИ
Прерывания можно объяснить очень простым примером из жизни. Например, вы сегодня должны получить заказ. Курьер может положить заказ в почтовый ящик. Чтобы его быстрее получить, вы ходите проверять почтовый ящик каждые 10 минут. Таким образом, вы тратите свое время непродуктивно. В случае Рудирона, это выглядит, как постоянный опрос какого либо датчика, на которое микроконтроллер также будет тратить ресурсы.
Второй вариант — это оставить записку курьеру, чтобы он оставил заказ не в почтовом ящике, а передал его вам лично в руки. В этом случае вы сможете заняться своими делами, не отвлекаясь на постоянную проверку почтового ящика. Когда курьер позвонит в дверь, вы будете вынуждены прервать свою работу, чтобы открыть дверь и получить заказ, а затем вернуться к своим делам. Поэтому этот процесс и называется прерыванием.
НАПОМИНАНИЕ При использовании внешнего питания сначала подключаем внешнее питание, а потом USB-кабель для связи или программирования. При выключении все делаем наоборот: сначала вытаскиваем USB-кабель, а затем внешнее питание. Несоблюдение данного правила может привести к выходу из строя платы Рудирон.
ВНЕШНИЕ И АППАРАТНЫЕ ПРЕРЫВАНИЯ
Прерывания работают аналогичным образом. Например, микроконтроллер может ждать, когда пользователь нажмет кнопку. Для этого в программе можно использовать команду digitalRead() и контролировать кнопку с высокой частотой. Но можно использовать аппаратное прерывание, не тратя время на опрос кнопки, но как только кнопка будет нажата, аппаратный сигнал запустит функцию внутри кода.
КОНТАКТЫ, ПОДДЕРЖИВАЮЩИЕ ПРЕРЫВАНИЯ 31, 32, 35
ПРИМЕЧАНИЕ Контакты встроенных кнопок №1 - №3 распараллелены с контактами, поддерживающими прерывания
Если вывод на плате не совместим с функцией прерывания, то программа работать не будет. Прерывания срабатывают при изменении цифрового сигнала на входе, который вы отслеживаете.
Синтаксис вызова: attachInterrupt(digitalPinToInterrupt(pin), ISR); digitalPinToInterrupt(pin) — в этом параметре необходимо указать конкретный пин, который будет использоваться для обработки сигнала внешнего прерывания. ISR — название функции, которая будет вызываться при прерывании.
ИСПОЛЬЗОВАНИЕ ПРЕРЫВАНИЙ НА ПРИМЕРЕ
В следующем примере будет изменен способ контроля кнопки — к цифровым входам кнопок прикреплена функция прерывания. Когда сигнал с кнопки возрастает, то есть меняется с LOW на HIGH, основной код программы в функции void loop() будет остановлен и будет вызвана функция прерывания. Основное преимущество, которое мы получаем в этом случае — больше не требуется опрос кнопки в основной части программы. Дополнительно запрограммированы задачи включения встроенных светодиодов по таймеру с интервалом в 1 секунду (1-й встроенный светодиод) и в 2 секунды (2-й встроенный светодиод).
ПРОГРАММА С ИСПОЛЬЗОВАНИЕМ АППАРАТНОГО ПРЕРЫВАНИЯ
#include «Arduino.h»
#include “rudiron/tasks_timer.h”
void setup_pinout(); void setup_tasks(); void wellcome();
/// Первая кнопка нажата
bool pressed1 = false;
/// Вторая кнопка нажата
bool pressed2 = false;
/// Третья кнопка нажата
bool pressed3 = false;
/// Задача моргания первым светодиодом
bool task_led1(void *)
{
digitalWrite(LED_BUILTIN_1, !digitalRead(LED_BUILTIN_1)); return true;
}
/// Задача моргания вторым светодиодом
bool task_led2(void *)
{
digitalWrite(LED_BUILTIN_2, !digitalRead(LED_BUILTIN_2)); return true;
}
void setup()
{
setup_pinout(); setup_tasks();
}
/// @brief Обработчик нажатия первой кнопки
void BUTTON_1_callback()
{
Serial.begin(115200); Serial.println(“Нажата кнопка 1”);
}
/// @brief Обработчик нажатия второй кнопки
void BUTTON_2_callback()
{
Serial.begin(115200); Serial.println(“Нажата кнопка 2”);
}
/// @brief Обработчик нажатия третьей кнопки
void BUTTON_3_callback()
{
Serial.begin(115200); Serial.println(“Нажата кнопка 3”);
}
/// Устанавливаются режимы ввода-вывода для используемых пинов
void setup_pinout()
{
pinMode(BUTTON_BUILTIN_1, INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(BUTTON_BUILTIN_1), BUTTON_1_ callback);
pinMode(BUTTON_BUILTIN_2, INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(BUTTON_BUILTIN_2), BUTTON_2_ callback);
pinMode(BUTTON_BUILTIN_3, INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(BUTTON_BUILTIN_3), BUTTON_3_ callback);
pinMode(LED_BUILTIN_1, OUTPUT);
pinMode(LED_BUILTIN_2, OUTPUT);
}
/// Настройка расписания задач
void setup_tasks()
{
tasksTimer.start_every_millis(1000, task_led1); tasksTimer.start_every_millis(2000, task_led2);
}
void loop()
{
}
ВАЖНО Программа может скомпилироваться без ошибок и загрузиться на контроллер, но не будет работать. Это происходит из-за установки перемычки IRQ необходимой для работы радио модуля.
Вы можете снять перемычку. И программа корректно заработает.
Что важно учесть при работе с прерываниями:
Делайте функцию прерывания максимально быстрой Функцию прерывания следует делать максимально быстрой, поскольку она останавливает выполнение основного кода вашей программы. Если необходимо выполнить тяжелые вычисления или длительные действия при срабатывании внешнего прерывания, то их следует оставить в цикле void loop(). А внутри функций прерывания следует изменять только переменные состояния, от которых зависит выполнение нужных операций.
Не используйте функции времени в прерываниях Команда delay() в функции прерывания не будет работать. Если вы используете счетчик времени на millis(), то при выходе из функции прерывания, вы получите последнее сохраненное значение, так как внутри функции прерывания значение millis() не увеличивается.
Не используйте монитор порта внутри прерываний Когда вы находитесь внутри прерывания, данные отправленные или полученные через команды Serial могут быть потеряны. Можно использовать Serial.println() внутри функции прерывания для отладки программы, например, чтобы убедиться что прерывание сработало, но вы можете столкнуться с проблемами. Лучше просто установить флаг внутри прерывания и опросить этот флаг внутри основной программы void loop().
Использование функции attachInterrupt() очень полезно, когда необходимо быть уверенным, что микроконтроллер не пропустит никаких изменений в сигнале, который вы отслеживаете. Иногда использование простого опроса датчика может быть более подходящим вариантом.
ПРИМЕЧАНИЕ Если Вы хотите выполнять программу при последующих включениях «Рудирона», то не забудьте переставить перемычку из положения «Режим программирования» обратно в «Режим выполнения». Если этого не сделать, то программа при включении «Рудирона» выполняться не будет.
ВЫВОДЫ
Мы научились использовать прерывания в наших программах и поняли ограничения, накладываемые на функции времени при использовании прерываний.
СПИСОК КОНТРОЛЬНЫХ ВОПРОСОВ
- Что такое прерывание и зачем его необходимо использовать?
- Для каких выводов можно применить функцию прерывания?
- Можно ли использовать прерывания с встроенными кнопками «Рудирона»?
- Что делает функция attachInterrupt()?.
- Какие параметры передаются в функцию attachInterrupt()?
- Для чего необходима функция digitalPinToInterrupt()?
- Какой параметр передается в функцию digitalPinToInterrupt()?
- Что необходимо учитывать при работе с прерываниями?
- Что произойдет при вызове команды delay() в функции прерывания?
- Что будет результатом выполнения нашей программы?
СПИСОК ДОПОЛНИТЕЛЬНЫХ ЗАДАЧ
- Использовать для написания программы среду Arduino IDE.
- Поменять в расписании выполняемых задач интервал включения светодиодов, увеличить его в два раза.
- Поменять в расписании выполняемых задач интервал включения светодиодов, уменьшить его в два раза.
ЭЛЕМЕНТЫ КОНТРОЛЯ И ПРОВЕДЕНИЯ СОРЕВНОВАНИЙ
- Проведение тестирования по контрольным вопросам. Критерии: кто быстрее и допустил меньшее количество ошибок.
- Написание одного из трех вариантов (исходный и два варианта из списка дополнительных задач) программы и загрузка этого варианта в контроллер. Критерии: Правильно написанная программа согласно выданного задания, работающий контроллер и наименьшее количество времени затраченного на эти работы.