Create external interrupt in arduino

Содержание

Исходный код программы (скетча)

Arduino

/*
YF‐ S201 Water Flow Sensor
Water Flow Sensor output processed to read in litres/hour
Adaptation Courtesy: hobbytronics.co.uk
*/
volatile int flow_frequency; // с помощью этой переменной мы будем подсчитывать импульсы от датчика расходы воды
// Calculated litres/hour
float vol = 0.0,l_minute;
unsigned char flowsensor = 2; // Sensor Input
unsigned long currentTime;
unsigned long cloopTime;
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 9);
void flow () // функция обработки прерывания
{
flow_frequency++;
}
void setup()
{
pinMode(flowsensor, INPUT);
digitalWrite(flowsensor, HIGH); // Optional Internal Pull-Up
Serial.begin(9600);
lcd.begin(16, 2);
attachInterrupt(digitalPinToInterrupt(flowsensor), flow, RISING); // Setup Interrupt
lcd.clear();
lcd.setCursor(0,0);
lcd.print(«Water Flow Meter»);
lcd.setCursor(0,1);
lcd.print(«Circuit Digest»);
currentTime = millis();
cloopTime = currentTime;
}
void loop ()
{
currentTime = millis();
// каждую секунду рассчитываем и выводим на экран ЖК дисплея скорость потока воды в литрах в минуту
if(currentTime >= (cloopTime + 1000))
{
cloopTime = currentTime; // Updates cloopTime
if(flow_frequency != 0){
// Pulse frequency (Hz) = 7.5Q, Q is flow rate in L/min.
l_minute = (flow_frequency / 7.5); // (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour
lcd.clear();
lcd.setCursor(0,0);
lcd.print(«Rate: «);
lcd.print(l_minute);
lcd.print(» L/M»);
l_minute = l_minute/60;
lcd.setCursor(0,1);
vol = vol +l_minute;
lcd.print(«Vol:»);
lcd.print(vol);
lcd.print(» L»);
flow_frequency = 0; // сбрасываем счетчик
Serial.print(l_minute, DEC); // Print litres/hour
Serial.println(» L/Sec»);
}
else {
Serial.println(» flow rate = 0 «);
lcd.clear();
lcd.setCursor(0,0);
lcd.print(«Rate: «);
lcd.print( flow_frequency );
lcd.print(» L/M»);
lcd.setCursor(0,1);
lcd.print(«Vol:»);
lcd.print(vol);
lcd.print(» L»);
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

/*
YF‐ S201 Water Flow Sensor
Water Flow Sensor output processed to read in litres/hour
Adaptation Courtesy: hobbytronics.co.uk
*/

volatileintflow_frequency;// с помощью этой переменной мы будем подсчитывать импульсы от датчика расходы воды

// Calculated litres/hour

floatvol=0.0,l_minute;

unsignedcharflowsensor=2;// Sensor Input

unsignedlongcurrentTime;

unsignedlongcloopTime;

#include <LiquidCrystal.h>

LiquidCrystallcd(12,11,5,4,3,9);

voidflow()// функция обработки прерывания

{

flow_frequency++;

}

voidsetup()

{

pinMode(flowsensor,INPUT);

digitalWrite(flowsensor,HIGH);// Optional Internal Pull-Up

Serial.begin(9600);

lcd.begin(16,2);

attachInterrupt(digitalPinToInterrupt(flowsensor),flow,RISING);// Setup Interrupt

lcd.clear();

lcd.setCursor(,);

lcd.print(«Water Flow Meter»);

lcd.setCursor(,1);

lcd.print(«Circuit Digest»);

currentTime=millis();

cloopTime=currentTime;

}

voidloop()

{

currentTime=millis();

// каждую секунду рассчитываем и выводим на экран ЖК дисплея скорость потока воды в литрах в минуту

if(currentTime>=(cloopTime+1000))

{

cloopTime=currentTime;// Updates cloopTime

if(flow_frequency!=){

// Pulse frequency (Hz) = 7.5Q, Q is flow rate in L/min.

l_minute=(flow_frequency7.5);// (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour

lcd.clear();

lcd.setCursor(,);

lcd.print(«Rate: «);

lcd.print(l_minute);

lcd.print(» L/M»);

l_minute=l_minute60;

lcd.setCursor(,1);

vol=vol+l_minute;

lcd.print(«Vol:»);

lcd.print(vol);

lcd.print(» L»);

flow_frequency=;// сбрасываем счетчик

Serial.print(l_minute,DEC);// Print litres/hour

Serial.println(» L/Sec»);

}

else{

Serial.println(» flow rate = 0 «);

lcd.clear();

lcd.setCursor(,);

lcd.print(«Rate: «);

lcd.print(flow_frequency);

lcd.print(» L/M»);

lcd.setCursor(,1);

lcd.print(«Vol:»);

lcd.print(vol);

lcd.print(» L»);

}

}

}

Исходный код программы

Далее приведен полный текст программы. Работа нашего проекта продемонстрирована на видео, приведенном в конце статьи.

Arduino

#include<LiquidCrystal.h> //подключение библиотеки для работы с ЖК дисплеем
#define ledPin 7
LiquidCrystal lcd(8,9,10,11,12,13);
float value = 3035; //Preload timer value (3035 for 4 seconds)
void setup()
{
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print(«ARDUINO TIMERS»);
delay(2000);
lcd.clear();

pinMode(ledPin, OUTPUT);
pinMode(2,INPUT);
pinMode(4,INPUT);

noInterrupts(); // отключаем все прерывания

TCCR1A = 0;
TCCR1B = 0;
TCNT1 = value; // preload timer
TCCR1B |= (1 << CS10)|(1 << CS12); // 1024 prescaler (коэффициент деления предделителя)
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt ISR (разрешаем вызов процедуры обработки прерывания переполнения счетчика)
interrupts(); // разрешаем все прерывания
}
ISR(TIMER1_OVF_vect) // процедура обработки прерывания переполнения счетчика
{
TCNT1 = value; // preload timer
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); //включаем и выключаем светодиод
}
void loop()
{
if(digitalRead(2) == HIGH)
{
value = value+10; //Incement preload value
}
if(digitalRead(4)== HIGH)
{
value = value-10; //Decrement preload value
}
lcd.setCursor(0,0);
lcd.print(value);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#include<LiquidCrystal.h>            //подключение библиотеки для работы с ЖК дисплеем
#define ledPin 7

LiquidCrystallcd(8,9,10,11,12,13);

floatvalue=3035;//Preload timer value (3035 for 4 seconds)

voidsetup()

{

lcd.begin(16,2);

lcd.setCursor(,);

lcd.print(«ARDUINO TIMERS»);

delay(2000);

lcd.clear();

pinMode(ledPin,OUTPUT);

pinMode(2,INPUT);

pinMode(4,INPUT);

noInterrupts();// отключаем все прерывания

TCCR1A=;

TCCR1B=;

TCNT1=value;// preload timer

TCCR1B|=(1<<CS10)|(1<<CS12);// 1024 prescaler (коэффициент деления предделителя)

TIMSK1|=(1<<TOIE1);// enable timer overflow interrupt ISR (разрешаем вызов процедуры обработки прерывания переполнения счетчика)

interrupts();// разрешаем все прерывания

}

ISR(TIMER1_OVF_vect)// процедура обработки прерывания переполнения счетчика

{

TCNT1=value;// preload timer

digitalWrite(ledPin,digitalRead(ledPin)^1);//включаем и выключаем светодиод

}

voidloop()

{

if(digitalRead(2)==HIGH)

{

value=value+10;//Incement preload value

}

if(digitalRead(4)==HIGH)

{

value=value-10;//Decrement preload value

}

lcd.setCursor(,);

lcd.print(value);

}

Регистры таймеров в Arduino Uno

Для изменения конфигурации таймеров в плате Arduino Uno используются следующие регистры:

1. Timer/Counter Control Registers (TCCRnA/B) – управляющие регистры таймера/счетчика

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

Формат этих регистров:

Предделитель (Prescaler)

Биты CS12, CS11, CS10 в регистре TCCR1B устанавливают коэффициент деления предделителя, то есть скорость часов таймера. В плате Arduino Uno можно установить коэффициент деления предделителя равный 1, 8, 64, 256, 1024.

2. Timer/Counter Register (TCNTn) – регистры таймера/счетчика

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

Формула для расчета заранее загружаемого значения (preloader value) для необходимого интервала времени (Time) в секундах выглядит следующим образом:

TCNTn = 65535 – (16x106xTime in sec / Prescaler Value)

Откуда берется величина 16х106? Здесь все просто — это переведенная в Герцы частота кварцевого генератора 16 МГц.

Чтобы для таймера 1 (timer1) задать время равное 2 секундам, при коэффициент деления предделителя (Prescaler Value) равном 1024, получим:

TCNT1 = 65535 – (16x106x2 / 1024) = 34285

attachInterrupt() Function

To set an interrupt in the Arduino IDE, you use the attachInterrupt() function, that accepts as arguments: the GPIO interrupt pin, the name of the function to be executed, and mode:

GPIO interrupt pin

The first argument is a GPIO interrupt. You should use digitalPinToInterrupt(GPIO) to set the actual GPIO as an interrupt pin. For example, if you want to use GPIO 14 as an interrupt, use:

The ESP8266 supports interrupts in any GPIO, except GPIO16.

ISR

The second argument of the attachInterrupt() function is the name of the function that will be called every time the interrupt is triggered – the interrupt service routine (ISR).

The ISR function should be as simple as possible, so the processor gets back to the execution of the main program quickly.

The best approach is to signal the main code that the interrupt has happened by using a global variable and within the loop() check and clear that flag, and execute code.

ISRs need to have ICACHE_RAM_ATTR before the function definition to run the interrupt code in RAM.

Interrupt modes

The third argument is the mode and there are 3 different modes:

  • CHANGE: to trigger the interrupt whenever the pin changes value – for example from HIGH to LOW or LOW to HIGH;
  • FALLING: for when the pin goes from HIGH to LOW;
  • RISING: to trigger when the pin goes from LOW to HIGH.

For our example, will be using the RISING mode, because when the PIR motion sensor detects motion, the GPIO it is connected to goes from LOW to HIGH.

Description

Digital Pins With Interrupts

The first parameter to attachInterrupt() is an interrupt number. Normally you should use digitalPinToInterrupt(pin) to translate the actual digital pin to the specific interrupt number. For example, if you connect to pin 3, use digitalPinToInterrupt(3) as the first parameter to attachInterrupt().

Board Digital Pins Usable For Interrupts
Uno, Nano, Mini, other 328-based 2, 3
Uno WiFi Rev.2, Nano Every all digital pins
Mega, Mega2560, MegaADK 2, 3, 18, 19, 20, 21
Micro, Leonardo, other 32u4-based 0, 1, 2, 3, 7
Zero all digital pins, except 4
MKR Family boards 0, 1, 4, 5, 6, 7, 8, 9, A1, A2
Nano 33 IoT 2, 3, 9, 10, 11, 13, A1, A5, A7
Nano 33 BLE, Nano 33 BLE Sense all pins
Due all digital pins
101 all digital pins (Only pins 2, 5, 7, 8, 10, 11, 12, 13 work with CHANGE)

※ NOTES AND WARNINGS:

Inside the attached function, delay() won’t work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function. See the section on ISRs below for more information.

Using Interrupts

Interrupts are useful for making things happen automatically in microcontroller programs and can help solve timing problems. Good tasks for using an interrupt may include reading a rotary encoder, or monitoring user input.

If you wanted to ensure that a program always caught the pulses from a rotary encoder, so that it never misses a pulse, it would make it very tricky to write a program to do anything else, because the program would need to constantly poll the sensor lines for the encoder, in order to catch pulses when they occurred. Other sensors have a similar interface dynamic too, such as trying to read a sound sensor that is trying to catch a click, or an infrared slot sensor (photo-interrupter) trying to catch a coin drop. In all of these situations, using an interrupt can free the microcontroller to get some other work done while not missing the input.

About Interrupt Service Routines

ISRs are special kinds of functions that have some unique limitations most other functions do not have. An ISR cannot have any parameters, and they shouldn’t return anything.

Generally, an ISR should be as short and fast as possible. If your sketch uses multiple ISRs, only one can run at a time, other interrupts will be executed after the current one finishes in an order that depends on the priority they have. millis() relies on interrupts to count, so it will never increment inside an ISR. Since delay() requires interrupts to work, it will not work if called inside an ISR. micros() works initially but will start behaving erratically after 1-2 ms. delayMicroseconds() does not use any counter, so it will work as normal.

Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile.

For more information on interrupts, see Nick Gammon’s notes.

Syntax

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) (recommended)

attachInterrupt(interrupt, ISR, mode) (not recommended)

attachInterrupt(pin, ISR, mode) (Not recommended. Additionally, this syntax only works on Arduino SAMD Boards, Uno WiFi Rev2, Due, and 101.)

Parameter Values

  • interrupt: the number of the interrupt. Allowed data types: int.

  • pin: the Arduino pin number.

  • ISR: the ISR to call when the interrupt occurs; this function must take no parameters and return nothing. This function is sometimes referred to as an interrupt service routine.

  • mode: defines when the interrupt should be triggered. Four constants are predefined as valid values:

    • LOW to trigger the interrupt whenever the pin is low,

    • CHANGE to trigger the interrupt whenever the pin changes value

    • RISING to trigger when the pin goes from low to high,

    • FALLING for when the pin goes from high to low.

  • The Due, Zero and MKR1000 boards allow also:

    HIGH to trigger the interrupt whenever the pin is high.

Nothing

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() {
state = !state;
}

Объяснение программы для Arduino

Полный текст программы приведен в конце статьи, здесь же мы рассмотрим его основные фрагменты.

1. Сначала в программе нам необходимо подключить библиотеку для работы с ЖК дисплеем и инициализировать используемые контакты.

Arduino

#include<LiquidCrystal.h>
LiquidCrystal lcd (7,8,9,10,11,12); // Define LCD display pins RS, E, D4, D5, D6, D7

1
2

#include<LiquidCrystal.h>                        

LiquidCrystallcd(7,8,9,10,11,12);// Define LCD display pins RS, E, D4, D5, D6, D7

2. Внутри функции void setup () мы на экране ЖК дисплея покажем пару приветственных сообщений.

Arduino

lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print(«CIRCUIT DIGEST»);
lcd.setCursor(0,1);
lcd.print(«ArduinoInterrupt»);
delay(3000);
lcd.clear();

1
2
3
4
5
6
7

lcd.begin(16,2);

lcd.setCursor(,);

lcd.print(«CIRCUIT DIGEST»);

lcd.setCursor(,1);

lcd.print(«ArduinoInterrupt»);

delay(3000);

lcd.clear();

3. Также в функции void setup () необходимо задать режим работы для контакта D13 (к нему подключен светодиод) – на вывод данных.

Arduino

pinMode(13,OUTPUT);

1 pinMode(13,OUTPUT);

4. Далее следует такая важная часть для нашей программы, как использование функции attachInterrupt() – это тоже происходит в функции void setup().

Arduino

attachInterrupt(digitalPinToInterrupt(2),buttonPressed1,RISING);
attachInterrupt(digitalPinToInterrupt(3),buttonPressed2,RISING);

1
2

attachInterrupt(digitalPinToInterrupt(2),buttonPressed1,RISING);

attachInterrupt(digitalPinToInterrupt(3),buttonPressed2,RISING);

В первой из этих команд контакт 2 назначается для обработки внешнего прерывания, а функция buttonPressed1 будет вызываться всегда, когда будет происходить повышение уровня (с LOW до HIGH) на контакте D2. Контакт 3 назначается для обработки внешнего прерывания, а функция buttonPressed2 будет вызываться всегда, когда будет происходить повышение уровня (с LOW до HIGH) на контакте D3.

5. Внутри функции void loop() число i инкрементируется начиная с 0 и выводится на экран ЖК дисплея.

Arduino

lcd.clear();
lcd.print(«COUNTER:»);
lcd.print(i);
++i;
delay(1000);

1
2
3
4
5

lcd.clear();

lcd.print(«COUNTER:»);

lcd.print(i);

++i;

delay(1000);

Также в функции void loop() функция digitalWrite() включает или выключает светодиод (в зависимости от состояния переменной output), подключенный к контакту D13.

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

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

Arduino

void buttonPressed1()
{
output = LOW;
lcd.setCursor(0,1);
lcd.print(«Interrupt 1»);
}

1
2
3
4
5
6

voidbuttonPressed1()

{

output=LOW;

lcd.setCursor(,1);

lcd.print(«Interrupt 1»);

}

Эта функция будет выполняться когда будет происходить повышение уровня на контакте D2. Эта функция будет изменять состояние переменной output на LOW, что будет приводить к выключению светодиода, а также будет выводить сообщение “interrupt1” на экран ЖК дисплея.

Arduino

void buttonPressed2()
{
output = HIGH;
lcd.setCursor(0,1);
lcd.print(«Interrupt2»);
}

1
2
3
4
5
6

voidbuttonPressed2()

{

output=HIGH;

lcd.setCursor(,1);

lcd.print(«Interrupt2»);

}

Эта функция будет выполняться при нажатии кнопки подключенной к контакту D3. Эта функция будет изменять состояние переменной output на HIGH, что будет приводить к включению светодиода, а также будет выводить сообщение “interrupt2” на экран ЖК дисплея.

Wrapping Up

To sum up, interrupts are useful to detect a change in a GPIO state and instantly trigger a function. You’ve also learned that you should use timers to write non-blocking code.

We hope you’ve found this tutorial useful. We have other tutorials on how to handle interrupts using MicroPython and using ESP32:

  • MicroPython: Interrupts with ESP32 and ESP8266
  • ESP32 with PIR Motion Sensor using Interrupts and Timers

Learn more about the ESP8266 board with our resources:

  • Home Automation using EPS8266
  • MicroPython Programming with ESP32 and ESP8266
  • Free ESP8266 Projects and Tutorials

Thanks for reading.

Параметры

: номер прерывания ().

: номер вывода (только для Arduino Due, Zero, MKR1000).

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

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

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

Платы Due, Zero и MKR1000 также поддерживают:

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

Цикл FOR в Ардуино

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

Синтаксис цикла FOR

Здесь конструкция будет немного сложнее:for(<начальное значение счетчика>;<условие продолжения выполнения цикла>;<изменение значения счетчика на каждом шаге>){<список_команд>}

Самый простой пример:

for(int i=5;i<10;i++){  // Конструкция «3 в одном»
 pinMode(i, OUTPUT);
}

Мы сразу создали переменную, инициализировали ее, указали, что в конце каждого цикла значение счетчика нужно увеличивать на единицу. И все – теперь можно использовать переменную внутри цикла.

Шаг переменной может быть иным. Вот примеры:

  • for(int i=0; i<10; i=i+2) // Шаг 2
  • for(int i=0; i<10; i+=2) // Аналогичен предыдущему
  • for(int i=10; i>0; i–) // Идем обратно – от 10 до 1

Цикл do while

В некоторых случаях нам нужно организовать цикл таким образом, чтобы инструкции блока выполнялись хотя бы один раз, а затем уже осуществлялась проверка. Для таких алгоритмов можно использовать конструкцию do while. Пример:

do {
  Serial.println("Working");
} while (checkSomething());

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

Справочник языка Ардуино

Операторы

  • setup()
  • loop()

Синтаксис

  • ; (точка с запятой)
  • {} (фигурные скобки)
  • // (одностроковый коментарий)
  • /* */ (многостроковый коментарий)
  • #define
  • #include

Битовые операторы

  • & (побитовое И)
  • | (побитовое ИЛИ)
  • ^ (побитовое XOR или исключающее ИЛИ)
  • ~ (побитовое НЕ)
  • << (побитовый сдвиг влево)
  • >> (побитовый сдвиг вправо)
  • ++ (инкремент)
  • — (декремент)
  • += (составное сложение)
  • -= (составное вычитание)
  • *= (составное умножение)
  • /= (составное деление)

  • &= (составное побитовое И)
  • |= (составное побитовое ИЛИ)

Данные

Типы данных

  • void
  • boolean
  • char
  • unsigned char
  • byte
  • int
  • unsigned int
  • word
  • long
  • unsigned long
  • short
  • float
  • double
  • string — массив символов
  • String — объект
  • массивы

sizeof()

Библиотеки

  • EEPROM
  • SD
  • SPI
  • SoftwareSerial
  • Wire

Функции

Цифровой ввод/вывод

  • pinMode()
  • digitalWrite()
  • digitalRead()

Аналоговый ввод/вывод

  • analogReference()
  • analogRead()
  • analogWrite() — PWM

Только для Due

  • analogReadResolution()
  • analogWriteResolution()

Расширенный ввод/вывод

  • tone()
  • noTone()
  • shiftOut()
  • shiftIn()
  • pulseIn()

Время

  • millis()
  • micros()
  • delay()
  • delayMicroseconds()

Математические вычисления

  • min()
  • max()
  • abs()
  • constrain()
  • map()
  • pow()
  • sqrt()
  • sq()

Тригонометрия

  • sin()
  • cos()
  • tan()

Случайные числа

  • randomSeed()
  • random()

Биты и байты

  • lowByte()
  • highByte()
  • bitRead()
  • bitWrite()
  • bitSet()
  • bitClear()
  • bit()

Внешние прерывания

  • attachInterrupt()
  • detachInterrupt()

Прерывания

  • interrupts()
  • noInterrupts()

attachInterrupt()

Описание

Задает функцию, которую необходимо вызвать при возникновении внешнего прерывания. Заменяет предыдущую функцию, если таковая была ранее ассоциирована с прерыванием. В большинстве плат Ардуино существует два внешних прерывания: номер 0 (цифровой вывод 2) и 1 (цифровой вывод 3). Номера выводов для внешних прерываний, доступные в тех или иных платах Ардуино, приведены в таблице ниже:

Плата int.0 int.1 int.2 int.3 int.4 int.5
Uno, Ethernet 2 3
Mega2560 2 3 21 20 19 18
Leonardo 3 2 1 7
Due (см. ниже)

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

Параметры

interrupt: номер прерывания (int)
pin: номер вывода (только для Arduino Due)
function: функция, которую необходимо вызвать при возникновении прерывания; эта функция должна быть без параметров и не возвращать никаких значений. Такую функцию иногда называют обработчиком прерывания.
mode:

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

  • LOW — прерывание будет срабатывать всякий раз, когда на выводе присутствует низкий уровень сигнала
  • CHANGE — прерывание будет срабатывать всякий раз, когда меняется состояние вывода
  • RISING — прерывание сработает, когда состояние вывода изменится с низкого уровня на высокий
  • FALLING — прерывание сработает, когда состояние вывода изменится с высокого уровня на низкий.

В Arduino Due доступно еще одно значение:

HIGH — прерывание будет срабатывать всякий раз, когда на выводе присутствует высокий уровень сигнала (только для Arduino Due).

Примечание

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

Использование прерываний

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

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

Пример

int pin = 13;
volatile int state = LOW;

void setup()
{
  pinMode(pin, OUTPUT);
  attachInterrupt(0, blink, CHANGE);
}

void loop()
{
  digitalWrite(pin, state);
}

void blink()
{
  state = !state;
}

detachInterrupt()

Демонстрация использования прерываний в плате Arduino

1. При нажатии кнопки на левой части схемы будет зажигаться светодиод и на экране ЖК дисплея будет показываться сообщение Interrupt2.

2. При нажатии кнопки на правой части схемы будет выключаться светодиод и на экране ЖК дисплея будет показываться сообщение Interrupt1.

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

  • спидометр для велосипеда на основе платы Arduino и смартфона на Android;
  • измерение скорости, пройденного пути и угла поворота с помощью Arduino и датчика LM393.

Подводя итоги

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

Есть какие-нибудь крутые проекты с прерываниями? Оставляйте комментарии ниже!