Кнопка и цифровой ввод: 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 по напряжению. Но есть коварная проблема — кнопка «дребезжит». Её и победим дальше.

Проверьте себя
1. Почему «висящий» пин на входе читается случайно?
AОн сломан
BНа нём нет чёткого напряжения, он ловит наводки
CСлишком высокая частота
DТак задумано в коде
2. Что читает пин с INPUT_PULLUP, когда кнопка НЕ нажата?
ALOW
BHIGH
CСлучайно
D0 вольт