Використання переривань на Arduino
Оптимізуйте ваші програми для Arduino за допомогою переривань – простого способу реагування на події в режимі реального часу!
Ми перериваємо нашу передачу.
Як з'ясовується, існує відмінний (але недостатньо використовується) механізм, вбудований у всі Arduino, який ідеально підходить для відстеження подій в режимі реального часу. Цей механізм називається перериванням. Робота переривання у тому, щоб переконатися, що процесор швидко відреагує на важливі події. При виявленні певного сигналу переривання (як і слідує з назви) перериває все, що робив процесор, і виконує деякий код, призначений для реагування на зовнішню причину, що викликала його, що впливає на Arduino. Після того, як цей код буде виконано, процесор повертається до того, що спочатку робив, ніби нічого не трапилося!
Що в цьому дивовижного, так це те, що переривання дозволяють організувати вашу програму так, щоб швидко та ефективно реагувати на важливі події, які не так легко передбачити у циклі програми. І найкраще це те, що переривання дозволяють процесору займатися іншими справами, а витрачати час на очікування події.
Переривання по кнопці
Почнемо з простого прикладу використання переривання для відстеження натискання кнопки. Для початку, ми візьмемо скетч, який ви, ймовірно, вже бачили: приклад "Button", включений в Arduino IDE (ви можете знайти його в каталозі "Приклади", перевірте меню Файл → Приклади → 02. Digital → Button).
У тому, що ви бачите тут, немає нічого шокуючого та дивовижного: все, що програма робить знову і знову, це проходження через цикл loop() та читання значення buttonPin . Припустимо на секунду, що ви хотіли б зробити в loop() що-те ще щось більше, ніж просто читання стану висновку. Ось тут і знадобиться переривання. Замість того, щоб постійно спостерігати за станом висновку, ми можемо доручити цю роботу перериванню та звільнити loop() для виконання того часу, що нам необхідно! Новий код буде виглядати так:
Цикли та режими переривань
Тут ви помітите кілька змін. Першим і найочевиднішим є те, що loop() тепер не містить жодних інструкцій! Ми можемо обійтися без них, тому що вся робота, яка раніше виконувалася в операторі if/else тепер виконується в новій функції pin_ISR() . Цей тип функцій називається обробником переривання: його робота полягає в тому, щоб швидко запуститися, обробити переривання і дозволити процесору повернутися назад до основної програми (тобто вмісту loop() ). При написанні обробника переривання слід враховувати кілька важливих моментів, відображення яких ви можете побачити у наведеному вище коді:
- обробники повинні бути короткими та лаконічними. Адже ви не хочете переривати основний цикл надовго!
- у обробників немає вхідних параметрів і значень, що повертаються. Усі зміни мають бути виконані на глобальних змінних.
Вам, певно, цікаво: звідки ми знаємо, коли запуститься переривання? Що його викликає? Третя функція, викликана функції setup() , встановлює переривання для всієї системи. Ця функція, attachInterrupt() , приймає три аргументи:
І резюмуючи, наше налаштування attachInterrupt() відповідає відстеженню вектора переривання 0 (висновок 2), щоб відреагувати на переривання за допомогою pin_ISR() , і викликати pin_ISR() щоразу, коли відбудеться зміна стану на виведенні 2.
Ще один момент, на який стоїтьВказати: наш обробник переривання використовує змінну buttonState для зберігання стану виведення. Перевірте визначення buttonState : замість типу int ми визначили його, як тип volatile int . У чому тут справа? volatile є ключовим словом мови C, яке застосовується до змінних. Воно означає, що значення змінної не під повним контролем програми. Тобто значення buttonState може змінитися і змінитися на щось, що сама програма не може передбачити - в цьому випадку введення користувача.
Ще одна корисна річ у ключовому слові volatile полягає у захисті від будь-якої випадкової оптимізації. Компілятори, як з'ясовується, виконують ще кілька додаткових завдань при перетворенні вихідного коду програми в машинний код, що виконується. Однією з цих завдань є видалення змінних, що не використовуються у вихідному коді, з машинного коду. Оскільки змінна buttonState не використовується або не викликається безпосередньо в функціях loop() або setup() , існує ризик того, що компілятор може видалити її, як змінну, що не використовується. Очевидно, що це неправильно – нам потрібна ця змінна! Ключове слово volatile має побічний ефект, повідомляючи компілятору, що цю змінну необхідно дати спокій.
Видалення змінних з коду, що не використовуються, - це функціональна особливість, а не баг компіляторів. Люди іноді залишають у коді змінні, що не використовуються, які займають пам'ять. Це не така велика проблема, якщо ви пишете програму C для комп'ютера з гігабайтами оперативної пам'яті. Однак, на Arduino оперативна пам'ять обмежена, і ви не бажаєте витрачати її марно! Навіть C компілятори для комп'ютерів будуть чинити так само, незважаючи на масу доступної системної пам'яті. Навіщо? З тієї ж причини, через яку людиприбирають за собою після пікніка - це гарна практика, не залишати після себе сміття.
Підбиваючи підсумки
Переривання – це простий спосіб змусити вашу систему швидше реагувати на чутливі до часу завдання. Вони також мають додаткову перевагу - звільнення головного циклу loop() , що дозволяє зосередити в ньому виконання основного завдання системи (я вважаю, що використання переривань, як правило, дозволяє зробити мій код трохи більш організованим: простіше побачити, для чого розроблений основний шматок коду , та які періодичні події обробляються перериваннями). Приклад, показаний тут, - це найбільший випадок використання переривань; Ви можете використовувати для читання даних з I2C пристрою, бездротової передачі і прийому даних, або навіть для запуску або зупинки двигуна.