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

Добавил пользователь Владимир З.
Обновлено: 05.10.2024

Для урока Вам понадобится следующие детали:

Также Вам понадобится скачать и установить библиотеку Low Power и среду Arduino IDE. Если не знаете/забыли как это сделать, то вернитесь к уроку по среде Arduino IDE.

Для начала давайте разберёмся, что же такое прерывание. Прерывание — это сигнал, который сообщает процессору о наступлении какого-либо события, которое требует немедленного внимания. Процессор реагирует на этот сигнал, прервав выполнение всех текущих команд и передаёт управление обработчику прерывания. Обработчик прерывания — это обычная функция, в которой находятся команды, которые выполняют определённые действия, как реакцию на прерывание.

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

Когда приходит сигнал прерывания, процессор приостанавливает выполнение текущей программы и переходит к подпрограмме обработки прерывания. Далее он выполняет те команды, которые находятся непосредственно в самом обработчике. После это процессор продолжает выполнять основную программу начиная с того места, в котором остановился.

Прерывания в модулях Arduino бывают нескольких видов:

  • Аппаратные (на уровне микропроцессора). Само же событие может произойти в произвольный момент времени и от внешнего устройства (нажатие на кнопку и т.д.);
  • Программные (на уровне программы). Они запускаются внутри программы при помощи специальной команды и используются для того, чтобы вызвать обработчик прерываний;
  • Внутренние (синхронные). Они возникают в результате изменения или ошибки при выполнении программы (например, при обращении к недопустимому адресу, недопустимый код операции и т.д.).

В этом уроке мы рассмотрим только аппаратные прерывания.

В модулях Arduino реализовано 4 типа аппаратных прерывания. Все они различаются уровнем сигнала на выводе модуля, на котором разрешено прерывание:

  • LOW. Прерывание срабатывает когда на выводе Arduino низкий уровень напряжения (0 В);
  • CHANGE. Прерывание срабатывает когда на выводе изменяется уровень напряжения с низкого на высокий (с 0 В на 5 В) или с высокого на низкий (с 5 В на 0 В);
  • RISING. Прерывание срабатывает когда на выводе изменяется уровень напряжения с низкого на высокий (с 0 В на 5 В);
  • FALLING.Прерывание срабатывает когда на выводе изменяется уровень напряжения с высокого на низкий (с 5 В на 0 В).

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

Существует ряд ограничений при работе с прерываниями:

  • Обработчик прерывания не должен выполняться слишком долго по времени. Проблема в том, что модули Arduino не могут обрабатывать несколько прерываний одновременно. Пока выполняется один обработчик прерывания, все остальные прерывания останутся без внимания со стороны процессора и таким образом мы просто пропустим их. Есть довольно простой выход из этой ситуации. Если нужно выполнить довольно большой объём команд в обработчике прерывания, то нужно передавать саму обработку в цикл loop(), а в обработчике прерывания установить флаг события. Тогда в loop() нужно только проверять флаг и в соответствии с его значением выполнять обработку;
  • Нужно быть очень аккуратным с переменными, значение которых изменяется в обработчике прерывания. Переменная должна быть объявлена квалификатором volatile, если её значение может быть изменено чем-либо за пределами того участка программы, где она объявлена (например, параллельно выполняющимся процессом — прерыванием). Квалификатор volatile изменяет способ интерпретации и доступа к переменной компилятором и программой. Он указывает компилятору загрузить переменную из ОЗУ, и не из запоминающего регистра, т.к. при определенных условиях значения переменных, хранящихся в регистрах, могут оказаться неточными;
  • В обработчике прерывания нельзя использовать функцию delay(), т.к. механизм определения интервала задержки использует таймеры, а они тоже работают в прерываниях, которые заблокирует обработчик. В итоге все будут ждать всех и программа зависнет. По этой же причине нельзя использовать протоколы связи, основанные на прерываниях (например, I2C);
  • Возможна потеря данный передаваемых по последовательному соединению (Serial) в момент выполнения функции обработки прерывания.

С основной теорией по прерываниям мы теперь познакомились. Теперь пришло время попытаться сделать какой-нибудь практический пример.

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

Электрическая принципиальная схема:

Схема подключения на макетной плате:

Итак, схема собрана. Теперь подключите модуль Arduino к компьютеру. Далее откройте среду разработки Arduino IDE и запишите в Arduino следующую программу:

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

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

Функция 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 пине:

Примечания

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

Функция detachInterrupt()

Описание
Синтаксис

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

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

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

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

Это могут быть прерывания по таймеру или внешние прерывания, уведомляющие о событиях, воздействующих на систему извне.

Прерывания и их источники

В процессе работы управляющий процессор выполняет определенные операции, а прерывание вызывает их остановку и в соответствие с кодом заставляет выполнить операции с более высоким приоритетом.

Проще говоря, прерывания – это набор приоритетов для тех или иных процессов, исполняемых контроллером.

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

Источником сигнала прерывания может стать таймер Arduino или процесс изменения состояния одного из контактов (пинов). Еще одним источником может стать какой-либо вход внешних прерываний: нужный сигнал появится при изменении его состояния.

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

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

Особенности прерывания по таймеру

Как же управлять временем и запускать процессы в нужном вам порядке? При работе с микросхемой Arduino для этого можно использовать millis(), ее эффективность зависит от постоянного обращения к ней. Тогда, в случае вызова этой функции, можно будет понять – наступило время определенной операции или нет.

Добиться эффективности можно при вызове millis() несколько раз в миллисекунду, но это довольно расточительно. Для вызова функции раз в миллисекунду (что является оптимальным вариантом) необходимо использовать таймер. Его можно установить на интервал в миллисекунду и добиться желаемого результата.

Микроконтроллер Arduino Uno укомплектован тремя таймерами: из них timer0 предназначен для генерации прерываний с интервалом в одну миллисекунду. При этом будет постоянно обновляться счетчик, передающий информацию функции millis(). Вести точный подсчет таймеру позволяет определенная частота, получаемая из 16 МГц процессора.

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

Функция timer0 оперирует тактовым делителем на 64 и изменять это значение не стоит. Оно позволяет получить частоту прерывания, близкую к 1 кГц, оптимальную для целей большинства схемотехников. Если попытаться изменить данный параметр, то можно нарушить работу функции millis().

Регистры сравнения и их роль в генерации прерывания

Регистры сравнения выполняют функцию анализа хранимых данных с текущим состоянием счетчика прерывания. Например, регистр сравнения (OCR0A) эффективно применяется для прерывания в середине счета.

Программный код, пример которого приведен ниже, позволит генерировать функцию TIMER0_COMPA при прохождении счетчика 0xAF:

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

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

Какие внешние воздействия вызывают прерывание

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

Микроконтроллер Arduino может иметь несколько пинов, способных обрабатывать внешние прерывания. Так, на плате Arduino Uno их два, а на Arduino Mega 2560 – 6. Продемонстрировать их функционал можно с помощью кнопки сброса сервопривода. Для этого при написании кода в класс Sweeper необходимо добавит функцию reset(). Она способна установить нулевое положение и перетаскивать в нее сервопривод.

Прерывания в ардуино

Соединить обработчик с внешним прерыванием поможет другая функция – attachInterrupt(). На приведенных в качестве примеров микроконтроллерах Interrupt0 реализована на втором контакте. Она сообщает микроконтроллеру о том, что на данном входе ожидается спад сигнала.

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

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

Полный код программы с таймерами и внешними прерываниями:

// Timer0 уже используется millis() - прерываемся примерно посередине и вызываем ниже функцию "Compare A"

// Прерывание вызывается один раз в миллисекунду, ищет любые новые данные, и если нашло, сохраняет их

Библиотеки прерываний

У схемотехников, благодаря Всемирной паутине есть доступ к широкому кругу библиотек, которые существенно облегчат работу с таймерами. Большинство из них предназначены для тех, кто применяет функцию millis(), но есть и такие, которые позволяют произвести желаемую настройку таймеров и сгенерировать прерывания. Оптимальным вариантом для этого являются библиотеки TimerThree и TimerOne, разработанные Paul Stoffregan.

Благодаря им, схемотехники получают широкий выбор возможностей, с помощью которых можно сконфигурировать прерывания с помощью таймера. Первая из этих библиотек не работает с Adruino Uno, но прекрасно зарекомендовала себя с платами Teensy, микроконтроллерами Adruino Mega2560 и Adruino Leonardo.

Недостатком Adruino Uno является наличие всего двух ходов, предназначенных для работы с внешними прерываниями. Если требуется большее количество подобных пинов, то отчаиваться не стоит, ведь этот микроконтроллер поддерживает pin-change – прерывания по изменению входа и работает это на всех восьми входах.

Их отличие от простых внешних прерываний – в сложности обработки, так как схемотехнику требуется отслеживать последнее из известных состояний пинов. Только в этом случае можно будет понять, какой из них вызвал прерывание.

Наиболее информативное и практичной библиотекой для прерываний по изменению входа является PinChangeInt.

Прерывания: основные правила работы

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

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

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

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

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

Если вызов функции Update() все же необходим, то предварительно необходимо будет проверить переменную состояния. Это позволит выяснить, необходима ли последующая обработка.

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

Какими функциями оперирует тот или иной таймер?

Для микроконтроллера Arduino Uno у каждого из трех таймеров свои операции.

Так Timer0 отвечает за ШИМ на пятом и шестом пине, функции millis(), micros(), delay().

Другой таймер – Timer1, используется с ШИМ на девятом и десятом пине, с библиотеками WaveHC и Servo.

Timer2 работает с ШИМ на 11 и 13 пинах, а также с Tone.

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

Результатом этого процесса будет сохранение в регистре копии основных переменных кода, что позволит обеспечить максимальную скорость доступа к ним.

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

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

Пожалуйста, объясните, как работают прерывания на Arduino Uno и связанных платах с использованием процессора ATmega328P. Доски, такие как:

  • Организация Объединенных Наций
  • Мини
  • Nano
  • Pro Mini
  • Lilypad

В частности, пожалуйста, обсудите:

  • Для чего нужны прерывания
  • Как написать процедуру обработки прерывания (ISR)
  • Сроки проблемы
  • Критические разделы
  • Атомарный доступ к данным

Примечание: это справочный вопрос .

При написании процедуры обработки прерывания (ISR):

Этот пример показывает, как, даже если основной цикл ничего не делает, вы можете включить или выключить светодиод на контакте 13, если нажата кнопка включения на контакте D2.

Чтобы проверить это, просто подключите провод (или переключатель) между D2 и массой. Внутренний подтягивающий сигнал (включенный в настройке) нормально поднимает вывод на ВЫСОКИЙ. Когда оно заземлено, оно становится НИЗКИМ. Изменение в выводе обнаруживается прерыванием CHANGE, которое вызывает функцию обработки прерывания (ISR).

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

Чтобы упростить преобразование номеров векторов прерываний в номера контактов, вы можете вызвать функцию digitalPinToInterrupt() , передав номер контакта. Возвращает соответствующий номер прерывания или NOT_AN_INTERRUPT (-1).

Например, на Uno вывод D2 на плате - это прерывание 0 (INT0_vect из таблицы ниже).

Таким образом, эти две строки имеют одинаковый эффект:

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

Ниже приведен список прерываний в приоритетном порядке для Atmega328:

Внутренние имена (которые вы можете использовать для настройки обратных вызовов ISR) указаны в скобках.

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

Основные причины, по которым вы можете использовать прерывания:

  • Для обнаружения смены контактов (например, поворотные датчики, нажатия кнопок)
  • Сторожевой таймер (например, если через 8 секунд ничего не происходит, прервите меня)
  • Прерывания по таймеру - используются для сравнения / переполнения таймеров
  • Передача данных SPI
  • Передача данных I2C
  • USART передача данных
  • АЦП преобразования (аналого-цифровой)
  • EEPROM готова к использованию
  • Флэш-память готова

Разбуди процессор

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

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

Включить прерывания

Отключить прерывания

Любой метод имеет одинаковый эффект, с помощью interrupts / noInterrupts немного легче запомнить, как они обходятся.

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

Зачем отключать прерывания?

Могут быть критические по времени фрагменты кода, которые вы не хотите прерывать, например, прерыванием по таймеру.

Временное отключение прерываний гарантирует, что isrCounter (счетчик, установленный внутри ISR) не изменится, пока мы получаем его значение.

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

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

Имена функций

Функции cli / sei и регистр SREG специфичны для процессоров AVR. Если вы используете другие процессоры, такие как ARM, функции могут немного отличаться.

Отключение глобально против отключения одного прерывания

При использовании cli() вы отключите все прерывания (включая прерывания по таймеру, последовательные прерывания и т. Д.).

Однако, если вы просто хотите отключить определенное прерывание, вы должны очистить флаг включения прерывания для этого конкретного источника прерывания. Например, для внешних прерываний звоните detachInterrupt() .

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

Порядок приоритетов - это последовательность, в которой процессор проверяет наличие событий прерывания. Чем выше список, тем выше приоритет. Так, например, запрос внешнего прерывания 0 (вывод D2) будет обслуживаться до запроса внешнего прерывания 1 (вывод D3).

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

Написание ISR

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

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

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

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

В этом случае функция attachInterrupt добавляет функцию switchPressed во внутреннюю таблицу и дополнительно настраивает соответствующие флаги прерываний в процессоре.

Настройка процессора для обработки прерывания

Следующий шаг, когда у вас есть ISR, - сказать процессору, что вы хотите, чтобы это конкретное условие вызывало прерывание.

Например, для внешнего прерывания 0 (прерывание D2) вы можете сделать что-то вроде этого:

Более читабельным было бы использовать определенные имена, например так:

EICRA (регистр внешнего управления прерываниями A) будет установлен в соответствии с этой таблицей из таблицы данных Atmega328. Это определяет точный тип прерывания, которое вы хотите:

  • 0: низкий уровень INT0 генерирует запрос прерывания (НИЗКОЕ прерывание).
  • 1: Любое логическое изменение в INT0 генерирует запрос прерывания (CHANGE interrupt).
  • 2: Задний фронт INT0 генерирует запрос прерывания (прерывание FALLING).
  • 3: передний фронт INT0 генерирует запрос прерывания (прерывание RISING).

EIMSK (Регистр внешней маски прерываний) фактически разрешает прерывание.

Чтобы упростить вашу жизнь, некоторые общие обработчики прерываний фактически находятся внутри библиотечного кода (например, INT0_vect и INT1_vect), а затем предоставляется более удобный интерфейс (например, attachInterrupt). Что на самом деле делает attachInterrupt, так это сохраняет адрес требуемого обработчика прерываний в переменной, а затем вызывает его из INT0_vect / INT1_vect, когда это необходимо. Он также устанавливает соответствующие флаги регистра для вызова обработчика при необходимости.

Короче говоря, нет, если вы не хотите, чтобы они были.

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

При выходе из ISR прерывания снова включаются . Компилятор также генерирует код внутри ISR для сохранения регистров и флагов состояния, чтобы на все, что вы делали при возникновении прерывания, это не повлияло.

Однако вы можете включить прерывания внутри ISR, если это абсолютно необходимо, например.

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

Внешние прерывания (где вы используете attachInterrupt) делают немного больше и занимают всего около 5,125 мкс (с тактовой частотой 16 МГц).

Это несколько меняется. Цифры, приведенные выше, являются идеальными для обработки прерывания. Несколько факторов могут задержать это:

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

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

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

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

Так как прерывания имеют приоритет, прерывание с более высоким приоритетом может быть обслужено перед интересующим вас прерыванием.

Есть два вида прерываний:

Некоторые устанавливают флаг, и они обрабатываются в приоритетном порядке, даже если событие, вызвавшее их, остановилось. Например, прерывание повышения, понижения или изменения уровня на выводе D2.

Другие проверяются, только если они происходят "прямо сейчас". Например, прерывание низкого уровня на выводе D2.

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

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

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

  • Не пытайтесь отложить, например: delay (100);
  • Вы можете получить время от звонка до миллис, но оно не будет увеличиваться, поэтому не пытайтесь отложить, ожидая его увеличения.
  • Не делайте серийных отпечатков (например. Serial.println ("ISR entered"); )
  • Не пытайтесь читать по порядку.

Для обработки прерывания смены штифта вам необходимо:

  • Укажите, какой контакт в группе. Это переменная PCMSKn (где n равно 0, 1 или 2 из таблицы ниже). Вы можете иметь прерывания на более чем один контакт.
  • Включить соответствующую группу прерываний (0, 1 или 2)
  • Укажите обработчик прерываний, как показано выше

Таблица выводов -> имена / маски смены выводов

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

Обработчику прерываний нужно будет определить, какой вывод вызвал прерывание, если маска указывает более одного (например, если вы хотели прерывания на D8 / D9 / D10). Чтобы сделать это, вам нужно будет сохранить предыдущее состояние этого пина и отработать (выполнив digitalRead или подобное), если этот конкретный пин был изменен.

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

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

Это позволяет вам писать код так:

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

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

Существуют некоторые тонкие проблемы, связанные с переменными, которые совместно используются подпрограммами обработки прерываний (ISR) и основным кодом (то есть кодом, не входящим в ISR).

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

Во-первых . когда вы используете "переменные" переменные?

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

  • Переменные, используемые только вне ISR, не должны быть изменчивыми.
  • Переменные, используемые только внутри ISR, не должны быть изменчивыми.
  • Переменные, используемые как внутри, так и вне ISR, должны быть изменчивыми.

Отключить прерывания при доступе к изменяемой переменной

Например, для сравнения count с каким-либо числом отключите прерывания во время сравнения, если count ISR обновил один байт, а не другой байт.

Более подробную информацию о прерываниях, таймерах и т. Д. Можно получить из таблицы данных для процессора.

Из-за недостатка места (размер поста) в моем списке больше примеров кода. Для более примера кода см. Мою страницу о прерываниях .

Читайте также: