Урок 6. Кнопка — digitalRead и подтяжка INPUT_PULLUP
Светодиод — это выход: плата сама решает, подать на него напряжение или нет. Кнопка — вход: плата читает, есть ли напряжение на выводе. В этом уроке кнопка будет включать светодиод, а потом — переключать его каждым нажатием.
Как плата читает вход
digitalRead(вывод) возвращает HIGH или LOW. На Uno и Nano напряжение выше примерно 3 В читается как HIGH, ниже примерно 1,5 В — как LOW. Прежде чем читать, вывод назначают входом в setup.
Подвох в том, что вход, который никуда не подключён, висит в воздухе: на нём наводки от проводов и даже от руки, и digitalRead возвращает то HIGH, то LOW случайным образом. Кнопка, пока не нажата, ни к чему вывод не подключает, — значит, нужен резистор, который в это время держит вход в определённом состоянии. Его называют подтягивающим (pull-up, к плюсу) или стягивающим (pull-down, к GND).
INPUT_PULLUP — подтяжка внутри платы
В микроконтроллере уже есть подтягивающие резисторы (около 20–50 кОм) — их включает режим INPUT_PULLUP. Тогда хватает одной кнопки без резисторов:
- одна ножка кнопки — к выводу
2; - другая ножка — к
GND.
Пока кнопка отпущена, внутренний резистор держит вход на 5 В — digitalRead возвращает HIGH. Нажали — вывод соединился с GND, читается LOW. Логика получается обратной: нажата — LOW.
У обычной тактовой кнопки четыре ножки, соединённые попарно. Чтобы не гадать, какие ножки замыкаются нажатием, поставьте кнопку поперёк канавки макетной платы и используйте две ножки по диагонали.
Светодиод горит, пока кнопка нажата
const int BUTTON = 2;
void setup() {
pinMode(BUTTON, INPUT_PULLUP); // вход с внутренней подтяжкой к 5 В
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
if (digitalRead(BUTTON) == LOW) { // нажата: вывод соединён с GND
digitalWrite(LED_BUILTIN, HIGH);
} else {
digitalWrite(LED_BUILTIN, LOW);
}
}if (…) { … } else { … } — условие: если кнопка нажата, выполняется первый блок, иначе второй. == — сравнение, не путайте с = (присваивание). Подробнее об условиях — в курсе C++.
Переключение по нажатию
Часто нужно другое: нажал — включилось, нажал ещё раз — выключилось. Для этого надо ловить не состояние кнопки, а момент нажатия — переход из HIGH в LOW. Скетч помнит прошлое состояние кнопки и сравнивает с новым.
Есть и вторая трудность — дребезг контактов. Металлические контакты кнопки в момент нажатия несколько раз быстро смыкаются и размыкаются, за несколько миллисекунд. Плата успевает увидеть это как несколько нажатий, и светодиод переключается случайное число раз. Простое лекарство — заметив изменение, подождать 50 мс и прочитать кнопку ещё раз.
const int BUTTON = 2;
bool ledOn = false; // горит ли светодиод сейчас
int lastState = HIGH; // прошлое состояние кнопки: HIGH — отпущена
void setup() {
pinMode(BUTTON, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
}
void loop() {
int state = digitalRead(BUTTON);
if (state != lastState) { // состояние изменилось
delay(50); // переждать дребезг
state = digitalRead(BUTTON); // и прочитать снова
if (state != lastState) {
lastState = state;
if (state == LOW) { // это нажатие, а не отпускание
ledOn = !ledOn; // поменять на противоположное
digitalWrite(LED_BUILTIN, ledOn ? HIGH : LOW);
Serial.println(ledOn ? "Включено" : "Выключено");
}
}
}
}Что нового:
bool— переменная, у которой два значения:true(истина) иfalse(ложь).!ledOn— «не ledOn», противоположное значение.ledOn ? HIGH : LOW— короткое условие: еслиledOnистинно, тоHIGH, иначеLOW.- Переменные
ledOnиlastStateобъявлены вне функций. Такие переменные сохраняют значение между повторамиloop. Переменная, объявленная внутриloop, создаётся заново при каждом повторе.
Откройте монитор порта — каждое нажатие печатает одну строку. Если строк при одном нажатии две, увеличьте паузу до 80–100 мс.
Подтяжка внешним резистором
Можно обойтись без INPUT_PULLUP и поставить резистор самостоятельно. Вариант со стягивающим резистором даёт привычную логику «нажата — HIGH»:
5V— к одной ножке кнопки;- другая ножка — к выводу
2; - от вывода
2резистор 10 кОм — кGND.
В скетче тогда pinMode(BUTTON, INPUT);, а нажатие — это digitalRead(BUTTON) == HIGH. Ток через резистор при нажатии: 5 / 10 000 = 0,0005 А = 0,5 мА — совсем немного.
Частые ошибки
- Кнопка «нажимается сама» — вход висит в воздухе: забыт
INPUT_PULLUPили резистор. - Нажатие не видно совсем — кнопка стоит так, что используются ножки, соединённые всегда, или обе ножки в одной пятёрке макетной платы. Поверните кнопку на 90° или возьмите ножки по диагонали.
- Логика наоборот — с
INPUT_PULLUPнажатая кнопка даётLOW, а неHIGH. - Вывод 13 как вход — к нему на плате подключён светодиод
L, который может мешать чтению. Для кнопок берите другие выводы, например 2–12.
Коротко
- Кнопка — вход:
pinMode(вывод, INPUT_PULLUP)иdigitalRead(вывод). - Вход без подтяжки «висит» и читается случайно;
INPUT_PULLUPвключает резистор внутри платы. - С
INPUT_PULLUPкнопку подключают между выводом и GND, нажатие читается какLOW. - Переключение ловят по переходу состояния, дребезг гасят паузой около 50 мс.
- Переменные, которые должны помнить значение между повторами
loop, объявляют вне функций.