Посібник із кнопки Arduino: 6 кроків (із зображеннями)

Посібник із кнопки Arduino: 6 кроків (із зображеннями)

Зміст:

Anonim

Що простіше і сумніше, ніж кнопка, ви запитуєте …

Я кажу: за кнопкою можуть сховатися несподівані речі. І в рамках програми, яка робить різні речі, керування кнопкою може бути складним. Приємно, що цікаві функції можна побудувати за допомогою простої кнопки.

У цьому підручнику розглядаються такі аспекти:

  • підключення та налаштування шпильки, використовуючи потягни вверх/тягнути вниз резистор,
  • прокатки,
  • виявлення держав проти події,
  • виявлення довгі натискання,
  • і деякі об'єктно-орієнтована програмування.

Підхід до програмування ґрунтується на опитуванні, заохоченому принципом Arduino loop (), і який є цілком прийнятним для простих і помірно складних проектів Arduino. Ми будемо вимагати, щоб тривалість кожного циклу () виконувалася "справедливо" кожного разу.

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

Цей підручник призначений для людей з базовим першим досвідом Arduino (тобто з знанням IDE, а також компіляції, мигання та запуску ескізів).

У наступному файлі ZIP можна знайти чотири ескізи, використані в цьому підручнику.

Постачання:

Крок 1: Підключення кнопки

Кнопка

Це миттєвий перемикач, з одним стабільним положенням (відкритим), коли не додається сила, і провідним (закритим) при натисканні. Це один з найпростіших електромеханічних сенсорних пристроїв.

Підключіть кнопку, як показано на фотографії цього кроку.

(Не вражайте розміром мого макета. Малий буде насправді зручнішим.)

Контакт вводу-виводу

AVR (він же Atmega, тобто мікросхема Atmel, що працює на платі Arduino), має кілька контактів вводу / виводу. I / O означає, що вони можуть бути вільно налаштовані програмним забезпеченням як вхідним або вихідним.

Pin 2 буде хорошим вибором для нашого прикладу. Він буде використовуватися для вимірювання стану кнопки: натиснута або відпущена.

Резистор натягу

Штифт повинен бути підключений десь за допомогою кнопки. Питання: де.

Першою ідеєю було б перейти до VCC. VCC є звичайним номіналом для напруги живлення, тут 5V.

Тому, коли кнопка натиснута, шпилька буде підключена до VCC, і програмне забезпечення буде читати HIGH. Але коли кнопка випущена, штифт з'єднаний з ніде, так званий "плаваючий", і буде піддаватися шуму, і програмне забезпечення буде читати ВИСОКО і НИЗЬКИЙ в хибний спосіб.

Таким чином, рішенням є використання так званого підтягуючого або випадаючого резистора. Такий резистор гарантує, що штифт завжди підключається до GND або VCC, безпосередньо або через резистор, залежно від положення кнопки.

На щастя, чіп AVR має внутрішній резистор на 20 кОм, який може бути підключений до штифта (внутрішньо). Висновок повинен бути налаштований як вхідний, і його значення, в цій ситуації, повідомляє, чи підключено підтягування (в іншому випадку значення визначає, коли контур налаштований як вихідний, його вихідний стан).

За допомогою цього підтягування ми під'єднаємо штифт до GND за допомогою кнопки, і у таких ситуаціях, коли кнопка відпущена, відповідно натиснута:

Кнопка не натиснута: VCC | 20K | | внутрішній | | підтягування | _ | | | _____ вхід –––– * –––––– o ––––––––– o o ––––– кнопка GND

Вхід ізольований від GND, тому тільки підключений до VCC через резистор. Немає поточних потоків.

Без підтягуючого резистора, вхід буде "плаваючим".

Кнопка натиснута: VCC |: 20K | |: внутрішній | |: підтягування | _ |: деякі поточні потоки | `- - - - - - - - - -> | вхід –––– * –––––– o ––––––––– o–––––o ––––– Кнопка натискання штиря GND

Вхід тепер безпосередньо підключений до GND. Через резистор протікає деякий струм.

В обох випадках ми маємо чітко визначену ситуацію.

Споживання

При натисканні кнопки резистор отримує різницю напруг, що дорівнює VCC, і струм I тече:

I = VCC / R

= 5 / 20,000 = 0,25 мА

Відповідає споживанню енергії P:

P = VCC2 / R

= 52 / 20,000 = 1,25 мВт

Це не багато, і споживається тільки при натисканні кнопки. Часто резистори, що підтягуються і випадають, мають ще більші значення, споживаючи тим самим меншу потужність. Якщо у вас немає особливих причин, скористайтеся цим зручним внутрішнім підтягувачем на 20 км.

Полярність

Якби ми мали розвантаження, ми б підключили штифт до VCC замість GND, і прочитали HIGH при натисканні, що є більш логічним. Але так як у нас є тільки підтягування, нам доведеться змінити полярність за допомогою програмного забезпечення, при виборі pin.

Додаткову інформацію про висновки вводу-виводу див. На веб-сторінці

Програмування

Конфігурація штифта AVR (як вхідного, так і з підтягуванням) описана в коді нижче.

Код

––––––––––8<––––––––––

#define BUTTON_PIN 2 void setup () {… pinMode (BUTTON_PIN, INPUT); digitalWrite (BUTTON_PIN, HIGH); // підключення внутрішнього підтягування …} void loop () {…} ––––––––––>8––––––––––

Етап 2: Вибірка, вилучення і стани читання

Глюки

Кнопки подібні до багатьох речей: недосконалі. Навіть коли вони дають тверде механічне відчуття, вони генерують пару коливань, коли змінюється положення кнопки.

Ці коливання називаються глюки або відскакує. Вони можуть бути усунені шляхом додавання конденсатора (що вносить деяку затримку) або програмного забезпечення.

Фільтрація за допомогою вибірки

Часто ваша програма Arduino заснована на циклі (), тобто робить щось, а потім багато разів спить. Код вибірки стану кнопки може виглядати так:

void loop () {// кнопка ручки boolean button_pressed = read_button (); // робити інші речі do_stuff (button_pressed); // сплять момент перед наступною затримкою ітерації (DELAY); } Це означає, що принаймні DELAY мілісекунди проходять між послідовними вибірковими кнопками. Залежно від DELAY, це зробить нас нечутливими до глюків. Реагуюча здатність не є повністю точною, але в значній мірі достатньою: існуватиме деякий джиттер у величині DELAY. Необхідно бути впевненим, що речі, які ми робимо під час кожного циклу, приймають "мале" і порівнянне час для кожної ітерації.

Перше зображення цього кроку показує збої та періодичні вибірки (позначені синім кольором).

Читання держави

Наведений нижче код показує, як виконується періодична вибірка. Коли клавіша вважається не натиснутою, на послідовну лінію надсилається точка; коли кнопка видно як натиснута, відправляється каретка.

Друге зображення цього кроку показує серійний висновок, для одного тривалого натискання слідують два коротких натискання.

Програмування

У наведеному нижче коді зауважте, що я вибрав ім'я змінної (button_pressed), що представляє високий рівень абстракції (стан кнопки), а не електричний стан (кнопка, провідник чи ні, а також електричне значення, що читається на шпильці), було б заплутаним через те, що, через підтягуючого резистора, штифт читає HIGH, коли кнопка відпущена.

Pro / Cons

Дуже проста реалізація. Призначений для безперервного контролю (наприклад, педаль розриву).

Непридатний для додаткового керування (наприклад, регулятор гучності звуку), який буде пояснено на наступному етапі.

Код

––––––––––8<––––––––––

#define BUTTON_PIN 2 // Кнопка #define DELAY 20 // Затримка в циклі в ms void setup () {pinMode (BUTTON_PIN, INPUT); digitalWrite (BUTTON_PIN, HIGH); // підтягування Serial.begin (9600); } boolean handle_button () {int button_pressed =! digitalRead (BUTTON_PIN); // pin low -> натиснута кнопка return_pressed; } void loop () {// кнопка ручки boolean button_pressed = handle_button (); // робити інші речі Serial.print (button_pressed? "^": "."); // іноді додаємо новий рядок статичного int counter = 0; if ((++ counter & 0x3f) == 0) Serial.println (); затримка (DELAY); } ––––––––––>8––––––––––

Крок 3: Виявлення країв

Краї

Коли ми хочемо схопити ребра або переходи, нам потрібно трохи покращити програму попереднього кроку.

Введемо глобальну змінну (button_was_pressed), яка запам'ятовує стан останнього читання, щоб ми могли виявити зміну стану.

У цьому прикладі ми виявляємо переходи від не натиснутого до натиснутого, і будемо сигналізувати їх за подією, як показано на першому знімку цього кроку.

Програмування

На кожній ітерації, якщо ми маємо подію, ми надішлемо каретку в послідовну лінію, інакше крапку. Див. Друге зображення цього кроку. Знову ж таки, для одного довгого натискання і двох коротких натискань. Зверніть увагу, що тривале натискання створило лише одну подію.

У наведеному нижче коді також зауважимо, що я вибрав імена змінних (button_now_pressed), що представляють високий рівень абстракції (стан кнопки), а не електричний стан (кнопка, провідник чи ні, або електричне значення, прочитане на шпильці).

Pro / Cons

Все ще проста реалізація. Впровадження глобальних змінних починає піддавати ризику масштабованість програми. Для однієї кнопки, все ще цілком нормально, хоча.

Код

––––––––––8<––––––––––

#define BUTTON_PIN 2 // Кнопка #define DELAY 20 // Затримка в циклі в ms boolean button_was_pressed; // попередній стан void setup () {pinMode (BUTTON_PIN, INPUT); digitalWrite (BUTTON_PIN, HIGH); // підтягування Serial.begin (9600); button_was_pressed = false; } boolean handle_button () {boolean event; int button_now_pressed =! digitalRead (BUTTON_PIN); // pin low -> натиснута подія = button_now_pressed &&! button_was_pressed; button_was_pressed = button_now_pressed; подія повернення; } void loop () {// кнопка ручки boolean raising_edge = handle_button (); // робити інші речі Serial.print (raise_edge? "^": "."); // іноді додаємо новий рядок статичного int counter = 0; if ((++ counter & 0x3f) == 0) Serial.println (); затримка (DELAY); } ––––––––––>8––––––––––

Крок 4: Відмітка коротких від довгих натискань

Тривалість імпульсу

Багато пристроїв з декількома елементами керування (наприклад, цифровими годинниками) пакують кілька функцій на кожну кнопку.

Це дуже корисно, заощаджуючи дорогоцінний об'єм, але має бути використане розумно, інакше пристрій може стати невтішним у використанні.

Відмінність коротких від довгих натискань - це вимірювання довжини імпульсу.

Подія більше не випускається при натисканні кнопки, але після її випуску. Це може вплинути на відчуття чуйності. Але поки не можна придбати причинно-наслідкові пристрої, ми не можемо передбачити тривалість і, отже, повинні продовжувати так.

Це комбінована обробка подій і станів: ми виявляємо зміну стану (подія) і тривалість тривалості отриманого стану.

Програмування

Таким чином, ми повинні ввести іншу глобальну змінну (button_pressed_counter). Зауважте також, що подія більше не має булевого значення (подія присутня чи ні, як на попередньому кроці), але три стани. Вони визначаються перерахуванням. (У мене були проблеми з визначенням типу перерахування; Arduino-специфічна проблема? Тому я використав перерахування для визначення констант).

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

На другому знімку показаний послідовний вихід для одного тривалого натискання, за яким слідують два коротких натискання.

Pro / Cons

Приємно підвищує функціональність кнопок (якщо розумно використовувати цю функцію, щоб забезпечити гарну зручність у використанні). Але код починає зростати і забруднюватися глобальними змінними … Наступний крок показує варіант OO, зроблений для масштабування.

Код

––––––––––8<––––––––––

#define BUTTON_PIN 2 // Кнопка #define LONGPRESS_LEN 25 // Мінімальне число циклів для тривалого натискання #define DELAY 20 // Затримка в циклі в ms enum {EV_NONE = 0, EV_SHORTPRESS, EV_LONGPRESS}; boolean button_was_pressed; // попереднє стан int button_pressed_counter; // натиснути тривалість запуску void setup () {pinMode (BUTTON_PIN, INPUT); digitalWrite (BUTTON_PIN, HIGH); // підтягування Serial.begin (9600); button_was_pressed = false; button_pressed_counter = 0; } int handle_button () {int подія; int button_now_pressed =! digitalRead (BUTTON_PIN); // pin low -> натиснута, якщо (! button_now_pressed && button_was_pressed) {if (button_pressed_counter <LONGPRESS_LEN) подія = EV_SHORTPRESS; else event = EV_LONGPRESS; } else event = EV_NONE; if (button_now_pressed) ++ button_pressed_counter; інакше button_pressed_counter = 0; button_was_pressed = button_now_pressed; подія повернення; } void loop () {// кнопка ручки boolean event = handle_button (); // робити інші речі (event) {case EV_NONE: Serial.print ("."); перерву; випадок EV_SHORTPRESS: Serial.print ("S"); перерву; випадок EV_LONGPRESS: Serial.print ("L"); перерву; } // додаємо новий рядок інший статичний int counter = 0; if ((++ counter & 0x3f) == 0) Serial.println (); затримка (DELAY); } ––––––––––>8––––––––––

Крок 5: Додавання кнопок і перехід на OO

Проблеми масштабування

Як ми бачили на попередньому кроці, стає важко розширюватися. Існують глобальні змінні стану, на які посилаються деякі функції. Уявіть собі додавання другої кнопки: починається кошмар; не говорять про третю кнопку …

Програмування: OO (об'єктно-орієнтоване)

Arduino використовує Обробка мова, побудована на вершині C ++, так чому б не використовувати деякі функції OO?

Ми повинні упакувати ці глобальні змінні в структури, які ми можемо створювати за бажанням. Такі структури, з відповідними функціями обробки, називаються класи і методів в світі ОО. Клас - це просто опис об'єкта, тоді як ефективна пам'ять, виділена для кожного об'єкта, називається наприклад.

Перше зображення цього кроку показує, що ми додали другу кнопку. На другому знімку показані традиційні довгі / короткі натискання, які дуже добре працюють незалежно. Друга кнопка має, до речі, довший поріг довгого натискання.

Pro / Cons

Цей код стає більш складним, що є нормальним, оскільки при масштабуванні складність підвищується на один значний крок. Доброю новиною є те, що він може підтримувати будь-яку кількість додаткових кнопок без збільшення складності.

Обробка встановлює деякі обмеження (наприклад, немає динамічного створення: новий і видалити операторів немає). Що не погано, тому що ми також не хочемо заблукати в деякі з особливо гарячих сторін C ++, які є (можливо) непотрібними в невеликих вбудованих системах, таких як Arduino.

Звичайно ж, це може бути зроблено в чистому C, так що якщо вам потрібно / воліють реалізацію C, просто запитайте мене.

Код

––––––––––8<––––––––––

#define BUTTON1_PIN 2 // Кнопка 1 #define BUTTON2_PIN 3 // Кнопка 2 #define DEFAULT_LONGPRESS_LEN 25 // Min nr циклів для тривалого натискання #define DELAY 20 // Затримка в циклі в ms ///////// ////////////////////////////////////////////////// /////////////////// enum {EV_NONE = 0, EV_SHORTPRESS, EV_LONGPRESS}; ////////////////////////////////////////////////// //////////////////////////// // Клас визначення класу ButtonHandler {public: // Конструктор ButtonHandler (int pin, int longpress_len = DEFAULT_LONGPRESS_LEN); // Ініціалізація зроблена після побудови, щоб дозволити статичні екземпляри void init (); // Обробник, який буде викликаний в loop () int handle (); protected: boolean was_pressed; // попереднє стан int pressed_counter; // натиснути на тривалість дії const int pin; // прикріпіть до якої кнопки підключено const int longpress_len; // тривалість довготривалості}; ButtonHandler:: ButtonHandler (int p, int lp): pin (p), longpress_len (lp) {} недійсний ButtonHandler:: init () {pinMode (pin, INPUT); digitalWrite (pin, HIGH); // pull-up was_pressed = false; pressed_counter = 0; } int ButtonHandler:: handle () {int подія; int now_pressed =! digitalRead (pin); if (! now_pressed && was_pressed) {// обробляє подію випуску, якщо (pressed_counter <longpress_len) подія = EV_SHORTPRESS; else event = EV_LONGPRESS; } else event = EV_NONE; // Оновити тривалість роботи преси, якщо (now_pressed) ++ pressed_counter; else pressed_counter = 0; // згадаємо стан, і ми зробили was_pressed = now_pressed; подія повернення; } ///////////////////////////////////////////////// ///////////////////////////// // Активувати кнопки кнопки ButtonHandler button1 (BUTTON1_PIN); Кнопка ButtonHandler2 (BUTTON2_PIN, DEFAULT_LONGPRESS_LEN * 2); void setup () {Serial.begin (9600); // init pin-кнопки; Я вважаю, що найкраще тут робити button1.init (); button2.init (); } void print_event (const char * button_name, int подія) {if (event) Serial.print (button_name); Serial.print (". SL" подія); } void loop () {// обробляти кнопку int event1 = button1.handle (); int event2 = button2.handle (); // робити інші речі print_event ("1", event1); print_event ("2", event2); // іноді додаємо новий рядок статичного int counter = 0; if ((++ counter & 0x1f) == 0) Serial.println (); затримка (DELAY); } ––––––––––>8––––––––––

Крок 6: Висновок

Якщо ви новачок у програмуванні і прочитали цей підручник дотепер, слава! не соромтеся вивчати наданий код, бути критичним і задавати питання. Повний архів коду можна знайти на етапі вступу, і ось посилання знову: arduino--button_tut.zip

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

Інші посилання

  • Коротка та основна стаття про програмовані вхідні та вихідні контакти Arduino:

    www.arduino.cc/en/Tutorial/DigitalPins

  • Поглиблена стаття на вхідних контактах, резисторах підтягування / зменшення, глюках:

    www.ladyada.net/learn/arduino/lesson5.html

  • Отримання швидкого уявлення про OO та C ++:

    www.mactech.com/articles/mactech/Vol.09/09.10/CPPBasics/index.html