Кнопка и цифровой ввод: pull-up резистор
До сих пор Arduino только командовала миром. Пора научить её слушать — и первое, что она услышит, это нажатие кнопки.
Кнопка кажется простой: нажал — есть сигнал, отпустил — нет. Но без одной хитрости (pull-up резистора) пин ловит призраков и срабатывает сам по себе.
Сейчас подключим кнопку и заставим встроенный светодиод загораться, пока она нажата. Заодно поймём, почему «пустой» пин ведёт себя непредсказуемо.
Проблема висящего пина
Если пин настроен на вход и никуда не подключён, на нём нет чёткого напряжения. Он ловит наводки из воздуха и читается то как HIGH, то как LOW — случайно. Такой пин называют «плавающим» (floating). Чтобы этого не было, пин надо «притянуть» к известному уровню резистором.
Схема с внутренним pull-up
У Arduino есть встроенные подтягивающие резисторы. Включаем их режимом INPUT_PULLUP — тогда внешний резистор не нужен:
5V (внутри чипа через pull-up) --- пин 2
|
[КНОПКА]
|
GND
Логика: не нажата -> пин = HIGH (1)
нажата -> пин = LOW (0)
Внимание: с INPUT_PULLUP логика перевёрнута. Не нажата — читается HIGH, нажата — LOW. Это сбивает с толку поначалу, но так схема надёжнее.
Код
const int BUTTON = 2;
void setup() {
pinMode(BUTTON, INPUT_PULLUP); // включаем внутренний pull-up
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
int state = digitalRead(BUTTON);
if (state == LOW) { // LOW = нажата!
digitalWrite(LED_BUILTIN, HIGH);
} else {
digitalWrite(LED_BUILTIN, LOW);
}
}
Как работает под капотом
digitalRead(пин) измеряет напряжение на пине и возвращает HIGH (если ближе к 5 В) или LOW (если ближе к 0 В). Внутренний pull-up — это резистор (~20–50 кОм) внутри чипа, который тихонько подтягивает пин к 5 В. Пока кнопка не нажата, пин «висит» на 5 В через этот резистор — читается HIGH. Нажал кнопку — пин напрямую соединился с GND, напряжение упало до 0 — читается LOW. Резистор при этом ограничивает ток, чтобы не было короткого.
# Та же логика на Python: читаем кнопку с pull-up
def read_button(button_connected_to_gnd):
# с INPUT_PULLUP: не нажата -> HIGH, нажата -> LOW
return "LOW (нажата)" if button_connected_to_gnd else "HIGH (отпущена)"
print(read_button(False)) # кнопка отпущена
print(read_button(True)) # кнопка нажата
Частые ошибки
- Забыли INPUT_PULLUP и внешний резистор — пин плавает, светодиод мигает сам.
- Ждут HIGH при нажатии. С pull-up нажатие — это LOW. Проверяй
== LOW. - Кнопку с 4 ножками вставили не по той оси. У тактовой кнопки ножки попарно соединены — используй ножки по диагонали.
Best practices
- Предпочитай INPUT_PULLUP внешнему резистору — меньше деталей, меньше ошибок.
- Заведи переменную
buttonPressed = (state == LOW)— код читается понятнее, чем сравнения с LOW повсюду. - Проверяй кнопку через монитор порта (Serial), если не уверен в схеме (об этом — позже).
Внешний pull-up и pull-down
Мы использовали внутренний подтягивающий резистор (INPUT_PULLUP) — это удобно, потому что не нужны лишние детали. Но полезно знать и про внешние варианты, ведь в чужих схемах ты их встретишь. Внешний pull-up — это обычный резистор (~10 кОм) от пина к 5 В: пин по умолчанию HIGH, нажатие тянет его к GND (LOW) — та же логика, что у внутреннего. Pull-down — наоборот: резистор от пина к GND, пин по умолчанию LOW, а нажатие подаёт на него 5 В (HIGH). Тогда логика «прямая»: нажата = HIGH.
Зачем вообще резистор, почему нельзя «просто кнопку»? Потому что без него в одном из состояний пин оказался бы напрямую соединён 5 В с GND или просто болтался в воздухе. Подтягивающий резистор решает обе проблемы: он задаёт пину чёткий уровень по умолчанию и ограничивает ток при нажатии, не давая случиться короткому замыканию. Это маленькая, но фундаментальная деталь грамотных цифровых схем.
Итоги
Пустой пин плавает — его надо подтянуть. INPUT_PULLUP делает это бесплатно, переворачивая логику: нажата = LOW. digitalRead возвращает HIGH/LOW по напряжению. Но есть коварная проблема — кнопка «дребезжит». Её и победим дальше.