Оглавление
- Задержки
- Types of Interrupts in microprocessor and microcontroller
- attachInterrupt
- volatile
- Arduino Interrupt Call Stack
- Step 5: Example 3: DAC
- Как работает RTOS
- Номера прерывания
- Исходный код программы (скетча)
- Зачем отключать прерывания?
- Функция millis вместо delay
- Мы прерываем нашу передачу…
- Примеры использования attachInterrupt
- Дребезг контактов
- Использование прерываний
Задержки
Простейшей с точки зрения использования функцией времени является задержка, их у нас две:
- delay(time) – “Приостанавливает” выполнение кода на time миллисекунд. Дальше функции delay выполнение кода не идёт, за исключением прерываний. Использовать рекомендуется только в самых крайних или тех случаях, когда delay не влияет на скорость работы устройства. time принимает тип данных unsigned long и может приостановить выполнение на срок от 1 мс до
50 суток (4 294 967 295 миллисекунд) с разрешением в 1 миллисекунду. Работает на системном таймере Timer 0delayMicroseconds(time) – Аналог delay(), приостанавливает выполнение кода на time микросекунд. time принимает тип данных unsigned long и может приостановить выполнение на срок от 4 мкс до
70 минут (4 294 967 295 микросекунд) с разрешением 4 микросекунды. Работает на системном таймере Timer 0
Задержки использовать очень просто:
И вот мы можем делать какое-то действие два раза в секунду. А что делать, если нам нужно выполнять одно действие два раза в секунду, а другое – три? А третье – 10 раз в секунду например. Сразу привыкаем к той мысли, что задержки лучше вообще не использовать в реальном коде. Разве что delayMicroseconds, он бывает нужен для генерации каких-то протоколов связи. Нормальным инструментом для тайм-менеджмента своего кода являются функции, которые считают время со старта МК.
Types of Interrupts in microprocessor and microcontroller
In microprocessors and microcontrollers we have two types of interrupts:
Hardware Interrupts:
These interrupts occur only when the microprocessor or microcontroller receives a signal from the external hardware device or a sensor then pin changes its state from High to Low or from Low to High. This can be defined in the programming whether you want to take action when the pin changes from High to Low, or when the pin changes from Low to High and then accordingly execute a function which is attached with the interrupt. Which I will explain in the programming.
Software Interrupts:
These interrupts are generated by the software itself, like for example the timer interrupts are the software interrupts, Serial interrupts, etc.
Interrupts used in Arduino:
Now it’s time to learn how to use interrupts in Arduino Uno. In Arduino or Mega, we have two types of interrupts” Hardware Interrupts”
- External Interrupt
- Pin Change Interrupt
I will talk about each one in detail so that you can have a complete understanding before I discuss the circuit diagram and programming.
External Interrupts:
The number of external interrupts in Arduino Uno and NANO is the same and are available on the same pin numbers 2 and 3. While in Mega we have 6 external interrupts which are assigned to pins 2, 3, 18, 19, 20, and 21. You can clearly see in the following table.
attachInterrupt
Функция attachInterrupt сообщает микроконтроллеру, на какие события он должен реагировать и какой обработчик им соответствует. Функция имеет следующий синтаксис:
attachInterrupt(interrupt, function, mode)
- interrupt — номер внешнего прерывания. Его можно указать явно (таблица соответствия номеров прерываний выводам Ардуино приведена ниже) или воспользоваться функцией digitalPinToInterrupt(pin) — она вернет номер прерывания по номеру пина.
- function — функция-обработчик прерывания. Эта функция будет вызываться каждый раз при появлении запроса прерывания.
-
mode — определяет события какого типа будут рассматриваться как запрос прерывания. Для данного параметра предусмотрены следующие значения:
- LOW — генерировать запрос прерывания при наличии сигнала низкого уровня;
- RISING — генерировать запрос прерывания при изменении уровня сигнала от низкого к высокому;
- FALLING — генерировать запрос прерывания при изменении уровня сигнала от высокому к низкого;
- CHANGE — генерировать запрос прерывания при любом изменении уровня сигнала;
- для DUE и Zero также доступно значение HIGH.
Плата | INT0 | INT1 | INT2 | INT3 | INT4 | INT5 |
UNO и другие на базе ATmega328/P | 2 | 3 | ||||
Leonardo | 3 | 2 | 1 | 7 | ||
Mega2560 | 2 | 3 | 21 | 20 | 19 | 16 |
digitalPinToInterruptattachInterruptattachInterruptmode
#define ledPin 13 #define interruptPin 2 volatile byte state = LOW; void setup() { pinMode(ledPin, OUTPUT); pinMode(interruptPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(interruptPin), blink, FALLING); } void loop() { digitalWrite(ledPin, state); } void blink() { state = !state; }
setupattachInterruptblinkFALLINGblinkstateloopstate
volatile
volatilevolatile
byte A = ; byte B; void loop() { A++; B = A + 1; }
- Загрузить из памяти значение A в регистр Р1
- Загрузить в регистр Р2 константу 1
- Сложить значение Р2 с Р1 (результат в Р2)
- Сохранить значение регистра Р2 в памяти по адресу A
- Сложить содержимое регистра Р1 с константой 2
- Сохранить значение регистра Р1 в памяти по адресу B
loopvolatilevolatilevolatilevolatile
#define interruptPin 2 volatile byte f = ; void setup() { pinMode(interruptPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(interruptPin), buttonPressed, FALLING); } void loop() { while (f == ) { } } void buttonPressed() { f = 1; }
loopvolatilevolatileinterruptsnoInterruptsатомарно исполняемый блок кода
Arduino Interrupt Call Stack
For normal function calls, a stack is used to save and restore the
Program Counter (PC). The PC contains the address of the current
instruction.
When a subroutine is called, the value of the PC is pushed onto the
stack, and when the subroutine is finished, the value is popped back
from the stack. Thus returning the PC to its original address before the
function call. The same process is used for an ISR subroutine.
For a subroutine call, the assembler instruction CALL pushes the PC
onto the stack. To return from the call, RET pops the PC from the stack.
The only difference between a subroutine return and an ISR return is
that the ISR return uses the RETI instruction, which re-enables
interrupts.
Step 5: Example 3: DAC
In this project I used a timer interrupt to output a sine wave of a specific frequency from the Arduino. I soldered a simple 8 bit R2R DAC to digital pins 0-7. This DAC was constructed from 10k and 20k resistors arranged in a multi-leveled voltage divider. I’ll be posting more about the construction of the DAC in another instructable, for now I’ve included the photo’s above.
I connected the output from the DAC up to an oscilloscope. If you need help understanding how to use/read the oscilloscope check out this tutorial. I loaded the following code onto the Arduino:
//63Hz sine wave //by Amanda Ghassaei 2012 //https://www.instructables.com/id/Arduino-Timer-Interrupts/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * */ //sends 63Hz sine wave to arduino PORTD DAC float t = 0; void setup() { //set port/pin mode. see http://www.arduino.cc/en/Reference/PortManipulation for more info DDRD = 0xFF;//port d (digital pins 0-7) all outputs cli();//stop interrupts //set timer2 interrupt at 40kHz TCCR2A = 0;// set entire TCCR2A register to 0 TCCR2B = 0;// same for TCCR2B TCNT2 = 0;//initialize counter value to 0 // set compare match register for 40khz increments OCR2A = 49;// = (16*10^6) / (8*40000)-1 // turn on CTC mode TCCR2A |= (1 << WGM21); // Set CS21 bit for 8 prescaler TCCR2B |= (1 << CS21); // enable timer compare interrupt TIMSK2 |= (1 << OCIE2A); sei();//allow interrupts } ISR(TIMER2_COMPA_vect) { //increment t t+=1; if (t==628){//40kHz/628 =~ 63Hz t=0; } } void loop(){ //sine wave of frequency ~63Hz //send sine values to PORTD between 0 and 255 PORTD=byte(127+127*sin(t/100)); }
I set up a timer interrupt that increments the variable t at a frequency of 40kHz. Once t reaches 627 it resets back to zero (this happens with a frequency of 40,000/628 = 63Hz). Meanwhile, in the main loop the Arduino sends a value between 0 (00000000 in binary) and 255 (11111111 in binary) to digital pins 0 through 7 (PORTD). It calculates this value with the following equation:PORTD=byte(127+127*sin(t/100));
So as t increments from 0 to 627 the sine function moves through one complete cycle. The value sent to PORTD is a sine wave with frequency 63Hz and amplitude 127, oscillating around 127. When this is sent through the 8 bit resistor ladder DAC it outputs an oscillating signal around 2.5V with an amplitude of 2.5V and frequency of 63Hz.
The frequency of the sine wave can be doubled by multiplying the (t/100) term by 2, quadrupled by multiplying by 4, and so on…
Also notice that if you increase the frequency of the timer interrupt too much by decreasing the prescaler or OCR2A the sine wave will not output correctly. This is because the sin() function is computationally expensive, and at high interrupt frequencies it does not have enough time to execute. If you are using high frequency interrupts, instead of performing a computation during the interrupt routine, considering storing values in an array and simply calling these values using some kind of index. You can find an example of that in my arduino waveform generator- by storing 20,000 values of sin in an array, I was able to output sine waves with a sampling frequency of 100kHz.
Как работает RTOS
Прежде чем переходить к изучению принципов работы RTOS (операционных систем реального времени) давайте сначала рассмотрим что такое задача (Task). Задача – это кусок кода программы, которая выполняется микроконтроллером в строго назначенное время (по расписанию). То есть если вы хотите выполнить какую-нибудь задачу, то ей должно быть назначено расписание с помощью задержки на уровне ядра (kernel delay) или с помощью прерываний. Это можно сделать с помощью планировщика, присутствующего в ядре. В одноядерном процессоре планировщик помогает задачам выполняться в заданное время, при этом часто может создаваться впечатление, что различные задачи выполняются одновременно. Каждая задача выполняется в соответствии с приоритетом, который ей назначен.
Теперь давайте посмотрим что происходит в ядре RTOS если мы захотим создать задачу для мигания светодиодом с интервалом в 1 секунду и назначим этой задаче наивысший приоритет.
Но независимо от нашей задачи мигания светодиодом, ядром RTOS всегда создается еще одна дополнительная задача, которая называется «холостой» задачей (idle task). Холостая задача создается когда нет никакой другой задачи для исполнения. Эта задача всегда выполняется с приоритетом 0 (самый низкий приоритет). Если мы проанализируем временной граф, показанный на рисунке выше, мы можем увидеть что выполнение всегда начинается с задачи мигания светодиодом (LED task) и она выполняется определенное время, а в оставшееся время выполняется холостая задача (idle task) до тех пор пока не наступит момент прерывания (tick interrupt). При наступлении этого момента прерывания ядро решает какая задача должна выполняться исходя из приоритета задачи и общего затраченного времени на выполнение задачи мигания светодиодом. Когда интервал времени 1 секунда заканчивается, ядро снова выбирает для выполнения задачу мигания светодиодом (LED task) поскольку она имеет более высокий приоритет чем холостая задача (idle task). Если в конкретный момент времени имеется для выполнения две или более задачи с одинаковым приоритетом, то они выполняются циклически, друг за другом.
На следующем рисунке показана диаграмма состояний, которая показывает переключение с невыполняемой задачи на выполняемую.
Каждая новая созданная задача сначала пребывает в состоянии готовности (Ready state), которое является частью состояния, во время которого не происходит выполнения задачи (not running state). Если созданная задача (Task1) имеет более высокий приоритет, чем все остальные задачи, она переходит в состояние выполнения (running). Если во время ее выполнения поступает задача с более высоким приоритетом чем у нее, то она снова возвращается в состояние Ready state. Если происходит блокировка выполнения этой задачи со стороны API (программный интерфейс приложения), то процессор больше не занимается этой задачей до тех пор, пока не закончится лимит времени, определенный пользователем.
Если выполнение задачи Task1 приостанавливается со стороны API, то задача Task1 переходит в состояние приостановки (Suspended state) и она в этом состоянии будет не видна планировщику (scheduler). Если вы возобновляете (resume) задачу 1 из состояния приостановки, то она снова переходит в состояние Ready (готова к исполнению) как показано на приведенном рисунке.
Итак, мы рассмотрели общие принципы выполнения задач и изменения их состояния в операционной системе реального времени. В этой статье мы рассмотрим выполнение трех задач в плате Arduino Uno используя FreeRTOS API.
Номера прерывания
Как правило, номер прерывания нужно указывать в скетче не напрямую, а при помощи digitalPinToInterrupt(pin). Контакты для прерываний, а также присвоение номеров прерываний к определенным контактам на разных платах может варьироваться. Прямое использование номеров прерывания лишь на первый взгляд кажется простым, но если запустить ваш скетч на другой плате, это может привести к проблемам с совместимостью.
Тем не менее, на старых скетчах часто используются как раз прямые номера прерываний — как правило, номер «0» (для 2-ого цифрового контакта) и номер «1» (для 3-его цифрового контакта). Эта таблица показывает, какие контакты для прерываний имеются у разных плат Arduino:
Плата | Ном. «0» | Ном. «1» | Ном. «2» | Ном. «3» | Ном. «4» | Ном. «5» |
---|---|---|---|---|---|---|
Uno, Ethernet | 2 | 3 | ||||
Mega2560 | 2 | 3 | 21 | 20 | 19 | 18 |
Платы на базе32u4 (Leonardo, Micro и пр.) | 3 | 2 | 1 | 7 | ||
Due, Zero | см. ниже |
На плате Arduino Uno функцию для прерывания можно применить ко всем доступным контактам. Номер контакта можно указать в attachInterrupt() напрямую.
На плате Arduino Zero функцию для прерывания можно применить ко всем доступным контактам, кроме 4-ого. Номер контакта можно указать в attachInterrupt() напрямую.
Исходный код программы (скетча)
Arduino
#include <Arduino_FreeRTOS.h>
void TaskBlink1( void *pvParameters );
void TaskBlink2( void *pvParameters );
void Taskprint( void *pvParameters );
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
xTaskCreate(
TaskBlink1
, «task1»
, 128
, NULL
, 1
, NULL );
xTaskCreate(
TaskBlink2
, «task2»
, 128
, NULL
, 1
, NULL );
xTaskCreate(
Taskprint
, «task3»
, 128
, NULL
, 1
, NULL );
vTaskStartScheduler();
}
void loop()
{
}
void TaskBlink1(void *pvParameters) {
pinMode(8, OUTPUT);
while(1)
{
Serial.println(«Task1»);
digitalWrite(8, HIGH);
vTaskDelay( 200 / portTICK_PERIOD_MS );
digitalWrite(8, LOW);
vTaskDelay( 200 / portTICK_PERIOD_MS );
}
}
void TaskBlink2(void *pvParameters)
{
pinMode(7, OUTPUT);
while(1)
{
Serial.println(«Task2»);
digitalWrite(7, HIGH);
vTaskDelay( 300 / portTICK_PERIOD_MS );
digitalWrite(7, LOW);
vTaskDelay( 300 / portTICK_PERIOD_MS );
}
}
void Taskprint(void *pvParameters) {
int counter = 0;
while(1)
{
counter++;
Serial.println(counter);
vTaskDelay(500 / portTICK_PERIOD_MS); }
}
1 |
#include <Arduino_FreeRTOS.h> voidTaskBlink1(void*pvParameters); voidTaskBlink2(void*pvParameters); voidTaskprint(void*pvParameters); voidsetup(){ // initialize serial communication at 9600 bits per second: Serial.begin(9600); xTaskCreate( TaskBlink1 ,»task1″ ,128 ,NULL ,1 ,NULL); xTaskCreate( TaskBlink2 ,»task2″ ,128 ,NULL ,1 ,NULL); xTaskCreate( Taskprint ,»task3″ ,128 ,NULL ,1 ,NULL); vTaskStartScheduler(); } voidloop() { voidTaskBlink1(void*pvParameters){ pinMode(8,OUTPUT); while(1) { Serial.println(«Task1»); digitalWrite(8,HIGH); vTaskDelay(200portTICK_PERIOD_MS); digitalWrite(8,LOW); vTaskDelay(200portTICK_PERIOD_MS); } } voidTaskBlink2(void*pvParameters) { pinMode(7,OUTPUT); while(1) { Serial.println(«Task2»); digitalWrite(7,HIGH); vTaskDelay(300portTICK_PERIOD_MS); digitalWrite(7,LOW); vTaskDelay(300portTICK_PERIOD_MS); } } voidTaskprint(void*pvParameters){ intcounter=; while(1) { counter++; Serial.println(counter); vTaskDelay(500portTICK_PERIOD_MS);} } |
Зачем отключать прерывания?
Могут быть временные критические фрагменты кода, которые вы не хотите прервать, например, прерыванием таймера.
Кроме того, если многобайтовые поля обновляются с помощью ISR, вам может потребоваться отключить прерывания, чтобы вы получили данные «атомарно». В противном случае один байт может быть обновлен ISR во время чтения другого.
Например:
Временное отключение прерываний гарантирует, что isrCounter (счетчик, установленный внутри ISR) не изменяется, пока мы получаем егозначение.
Предупреждение: , если вы не уверены, что прерывания уже включены или нет, вам необходимо сохранить текущее состояние и восстановить его позже. Например, код из функции millis () выполняет следующее:
Обратите внимание, что указанные строки сохраняют текущий SREG (регистр состояния), который включает флаг прерывания. После того, как мы получили значение таймера (длиной 4 байта), мы вернем регистр состояния, как это было
Функция millis вместо delay
Функция millis() позволит выполнить задержку без delay на ардуино, тем самым обойти недостатки предыдущих способов. Максимальное значение параметра millis такое же, как и у функции delay (4294967295мс или 50 суток). При переполнении значение просто сбрасывается в 0, не забывайте об этом.
С помощью millis мы не останавливаем выполнение всего скетча, а просто указываем, сколько времени ардуино должна просто “обходить” именно тот блок кода, который мы хотим приостановить. В отличие от delay millis сама по себе ничего не останавливает. Данная команда просто возвращает нам от встроенного таймера микроконтроллера количество миллисекунд, прошедших с момента запуска. При каждом вызове loop Мы сами измеряем время, прошедшее с последнего вызова нашего кода и если разница времени меньше желаемой паузы, то игнорируем код. Как только разница станет больше нужной паузы, мы выполняем код, получаем текущее время с помощью той же millis и запоминаем его – это время будет новой точкой отсчета. В следующем цикле отсчет уже будет от новой точки и мы опять будем игнорировать код, пока новая разница millis и нашего сохраненного прежде значения не достигнет вновь желаемой паузы.
Вот пример, наглядно иллюстрирующий работу команды:
Сначала мы вводим переменную timing, в ней будет храниться количество миллисекунд. По умолчанию значение переменной равно 0. В основной части программы проверяем условие: если количество миллисекунд с запуска микроконтроллера минус число, записанное в переменную timing больше, чем 10000, то выполняется действие по выводу сообщения в монитор порта и в переменную записывается текущее значение времени. В результате работы программы каждые 10 секунд в монитор порта будет выводиться надпись 10 seconds. Данный способ позволяет моргать светодиодом без delay.
Мы прерываем нашу передачу…
Как выясняется, существует отличный (но недостаточно часто используемый) механизм, встроенный во все Arduino, который идеально подходит для отслеживания событий в режиме реального времени. Данный механизм называется прерыванием. Работа прерывания заключается в том, чтобы убедиться, что процессор быстро отреагирует на важные события. При обнаружении определенного сигнала прерывание (как и следует из названия) прерывает всё, что делал процессор, и выполняет некоторый код, предназначенный для реагирования на вызвавшую его внешнюю причину, воздействующую на Arduino. После того, как этот код будет выполнен, процессор возвращается к тому, что он изначально делал, как будто ничего не случилось!
Что в этом удивительного, так это то, что прерывания позволяют организовать вашу программу так, чтобы быстро и эффективно реагировать на важные события, которые не так легко предусмотреть в цикле программы. И лучше всего это то, что прерывания позволяют процессору заниматься другими делами, а тратить время на ожидание события.
Примеры использования attachInterrupt
Давайте приступим к практике и рассмотрим простейший пример использования прерываний. В примере мы определяем функцию-обработчик, которая при изменении сигнала на 2 пине Arduino Uno переключит состояние пина 13, к которому мы традиционно подключим светодиод.
#define PIN_LED 13
volatile boolean actionState = LOW;
void setup() {
pinMode(PIN_LED, OUTPUT);
// Устанавливаем прерывание
// Функция myEventListener вызовется тогда, когда
// на 2 пине (прерываниие 0 связано с пином 2)
// изменится сигнал (не важно, в какую сторону)
attachInterrupt(0, myEventListener, CHANGE);
}
void loop() {
// В функции loop мы ничего не делаем, т.к. весь код обработки событий будет в функции myEventListener
}
void myEventListener() {
actionState != actionState; //
// Выполняем другие действия, например, включаем или выключаем светодиод
digitalWrite(PIN_LED, actionState);
}. Давайте рассмотрим несколько примеров более сложных прерываний и их обработчиков: для таймера и кнопок
Давайте рассмотрим несколько примеров более сложных прерываний и их обработчиков: для таймера и кнопок.
Прерывания по нажатию кнопки с антидребезгом
При прерывании по нажатию кнопки возникает проблема дребезга – перед тем, как контакты плотно соприкоснутся при нажатии кнопки, они будут колебаться, порождая несколько срабатываний. Бороться с дребезгом можно двумя способами – аппаратно, то есть, припаивая к кнопке конденсатора, и программно.
Избавиться от дребезга можно при помощи функции millis – она позволяет засечь время, прошедшее от первого срабатывания кнопки.
if(digitalRead(2)==HIGH) { //при нажатии кнопки //Если от предыдущего нажатия прошло больше 100 миллисекунд if (millis() - previousMillis >= 100) { //Запоминается время первого срабатывания previousMillis = millis(); if (led==oldled) { //происходит проверка того, что состояние кнопки не изменилось led=!led; }
Этот код позволяет удалить дребезг и не блокирует исполнение программы, как в случае с функцией delay, которая недопустима в прерываниях.
Прерывания по таймеру
Таймером называется счетчик, который производит счет с некоторой частотой, получаемой из процессорных 16 МГц. Можно произвести конфигурацию делителя частоты для получения нужного режима счета. Также можно настроить счетчик для генерации прерываний при достижении заданного значения.
Таймер и прерывание по таймеру позволяет выполнять прерывание один раз в миллисекунду. В Ардуино имеется 3 таймера – Timer0, Timer1 и Timer2. Timer0 используется для генерации прерываний один раз в миллисекунду, при этом происходит обновление счетчика, который передается в функцию millis (). Этот таймер является восьмибитным и считает от 0 до 255. Прерывание генерируется при достижении значения 255. По умолчанию используется тактовый делитель на 65, чтобы получить частоту, близкую к 1 кГц.
Для сравнения состояния на таймере и сохраненных данных используются регистры сравнения. В данном примере код будет генерировать прерывание при достижении значения 0xAF на счетчике.
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
Требуется определить обработчик прерывания для вектора прерывания по таймеру. Вектором прерывания называется указатель на адрес расположения команды, которая будет выполняться при вызове прерывания. Несколько векторов прерывания объединяются в таблицу векторов прерываний. Таймер в данном случае будет иметь название TIMER0_COMPA_vect. В этом обработчике будут производиться те же действия, что и в loop ().
SIGNAL(TIMER0_COMPA_vect) { unsigned long currentMillis = millis(); sweeper1.Update(currentMillis); if(digitalRead(2) == HIGH) { sweeper2.Update(currentMillis); led1.Update(currentMillis); } led2.Update(currentMillis); led3.Update(currentMillis); } //Функция loop () останется пустой. void loop() { }
Подведение итогов
Прерывание в Ардуино – довольно сложная тема, потому что приходится думать сразу обо всей архитектуре проекта, представлять как выполняется код, какие возможны события, что происходит, когда основной код прерывается. Мы не ставили задачу раскрыть все особенности работы с этой конструкцией языка, главная цель была познакомить с основными вариантами использования. В следующих статьях мы продолжим разговор о прерываниях более подробне.
Дребезг контактов
Очевидно, что при подаче на вход внешнего прерывания сигнала, искаженного дребезгом контактов, обработчик прерывания будет выполнен несколько раз. В некоторых случаях дребезг не является проблемой, например, если мы ожидаем нажатия на кнопку для выполнения в программе каких-либо действий: достаточно отключить отслеживание данного прерывания при входе в обработчик, а после выполнения нашего кода снова включить (подразумевается, что время выполнения наших действий превышает длительность дребезга контактов). Таким образом мы пропустим дребезг и обработчик будет выполнен только один раз. Другое дело, когда прерывания используются для регистрации событий. Хороший пример — механический энкодер вращения. В этом случае мы не можем отключить отслеживание прерываний, поскольку рискуем пропустить событие (импульс от энкодера). Принять решение о том, ложный это импульс или нет, обработчик не может, да и просто это не та задача, которой он должен заниматься. Поэтому единственным рациональным решением является применение аппаратных подавителей дребезга. Данной теме посвящены несколько моих публикаций, ссылки на них ниже:
Устранение дребезга контактов. Часть 1 — триггер ШмиттаУстранение дребезга контактов. Часть 2 — микросхема MC14490Устранение дребезга контактов. Часть 3 — микросхемы MAX6816/MAX6817/MAX6818
Использование прерываний
Прерывания применяются, к примеру, в микроконтроллерных программах — когда вам нужно сделать так, чтобы те или иные процессы выполнялись автоматически. Кроме того, они могут помочь в решении проблем с синхронизацией. Что касается конкретных областей применения, то прерывания могут пригодиться, например, при считывании данных с поворотного регулятора или отслеживания пользовательских данных.
Написать программу, которая постоянно, не упуская ни единого импульса, следила бы за линией, ведущей от поворотного регулятора, и в то же время занималась чем-нибудь еще, не так-то просто. Похожая ситуация обстоит и с другими датчиками — к примеру, со звуковым сенсором, который следит за кликами мышью, или с инфракрасным сенсором (фотопрерывателем), который пытается уловить какое-либо движение (например, падение монеты). Тут-то на помощь и приходит прерывание — оно позволяет сделать так, чтобы микроконтроллер, с одной стороны, продолжал следить за какими-либо сигналами (движениями, звуками и т.д.), а с другой, мог выполнять какую-то другую работу.