Кнопочный переключатель
В этом эксперименте мы делаем из тактовой кнопки триггер, борясь с «дребезгом».
Список деталей для эксперимента
1 плата Arduino Uno
1 беспаечная макетная плата
1 тактовая кнопка
1 резистор номиналом 220 Ом
1 светодиод
5 проводов «папа-папа»
Для дополнительного задания:
еще 1 кнопка
еще 2 провода
Принципиальная схема

Схема на макетке

Обратите внимание
Мы могли бы один из контактов кнопки соединить проводом напрямую с одним из входов
GND
, но мы сначала «раздали» «землю» на длинную рельсу макетки. Если мы работаем с макетной платой, так поступать удобнее, т.к. в схеме могут появляться новые участки, которые тоже нужно будет соединить с «землей»Также полезно руководствоваться соображениями аккуратности изделия, поэтому катод светодиода мы соединяем с другим входом
GND
отдельным проводом, который не мешает нам работать в середине макетки.
Скетч
// p100_led_toggle.ino
#define BUTTON_PIN 3
#define LED_PIN 13
boolean buttonWasUp = true; // была ли кнопка отпущена?
boolean ledEnabled = false; // включен ли свет?
void setup()
{
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop()
{
// определить момент «клика» несколько сложнее, чем факт того,
// что кнопка сейчас просто нажата. Для определения клика мы
// сначала понимаем, отпущена ли кнопка прямо сейчас...
boolean buttonIsUp = digitalRead(BUTTON_PIN);
// ...если «кнопка была отпущена и (&&) не отпущена сейчас»...
if (buttonWasUp && !buttonIsUp) {
// ...может это «клик», а может и ложный сигнал (дребезг),
// возникающий в момент замыкания/размыкания пластин кнопки,
// поэтому даём кнопке полностью «успокоиться»...
delay(10);
// ...и считываем сигнал снова
buttonIsUp = digitalRead(BUTTON_PIN);
if (!buttonIsUp) { // если она всё ещё нажата...
// ...это клик! Переворачиваем сигнал светодиода
ledEnabled = !ledEnabled;
digitalWrite(LED_PIN, ledEnabled);
}
}
// запоминаем последнее состояние кнопки для новой итерации
buttonWasUp = buttonIsUp;
}
Пояснения к коду
Поскольку мы сконфигурировали вход кнопки как
INPUT_PULLUP
, при нажатии на кнопку на данном входе мы будем получать 0. Поэтому мы получим значениеtrue
(«истина») в булевой переменнойbuttonIsUp
(«кнопка отпущена»), когда кнопка отпущена.Логический оператор
&&
(«и») возвращает значение «истина» только в случае истинности обоих его операндов. Взглянем на так называемую таблицу истинности для выраженияbuttonWasUp && !buttonIsUp
(«кнопка была отпущена и кнопка не отпущена»):buttonWasUp
buttonIsUp
!buttonIsUp
buttonWasUp && !buttonIsUp
0
0
1
0
0
1
0
0
1
0
1
1
1
1
0
0
Здесь рассмотрены все возможные сочетания предыдущего и текущего состояний кнопки и мы видим, что наш условный оператор
if
сработает только в случае, когда кнопка нажата только что: предыдущее состояние 1 («была отпущена»), а текущее 0 («не отпущена»).Через 10 миллисекунд мы проверяем еще раз, нажата ли кнопка: этот интервал больше, чем длительность «дребезга», но меньше, чем время, за которое человек успел бы дважды нажать на кнопку. Если кнопка всё еще нажата, значит, это был не дребезг.
Мы передаем в
digitalWrite
не конкретное значениеHIGH
илиLOW
, а просто булеву переменнуюledEnabled
. В зависимости от того, какое значение было для нее вычислено, светодиод будет зажигаться или гаситься.Последняя инструкция
buttonWasUp = buttonIsUp;
сохраняет текущее состояние кнопки в переменную предыдущего состояния, ведь на следующей итерацииloop
текущее состояние уже станет историей.
Вопросы для проверки себя
В каком случае оператор
&&
возвращает значение «истина»?Что такое «дребезг»?
Как мы с ним боремся в программе?
Как можно избежать явного указания значения уровня напряжения при вызове
digitalWrite
?
Задания для самостоятельного решения
Измените код так, чтобы светодиод переключался только после отпускания кнопки.
Добавьте в схему еще одну кнопку и доработайте код, чтобы светодиод зажигался только при нажатии обеих кнопок.