Подтягивающий резистор: что читает вход, к которому ничего не подключено

Висящий вход, INPUT_PULLUP, выбор номинала и почему нажатая кнопка читается как LOW

Кнопка «нажимается сама», стоит поднести руку к проводу. Это не брак платы, а вход, который ни к чему не подключён. Разбираем, зачем нужна подтяжка, как выбрать её номинал и когда хватает внутренней.

Первый скетч с кнопкой нередко ведёт себя странно. Кнопка не нажата, а светодиод мигает. Поднесёшь руку к проводу — в мониторе порта сыплются «нажата, отпущена, нажата». Отодвинешь — затихает. Плата исправна, провода целы. Дело в том, что вход, к которому ничего не подключено, не читает ни ноль, ни единицу. Он читает то, что на него навелось.

Почему вход «висит»

Цифровой вход микроконтроллера устроен так, чтобы почти не потреблять тока. Его входное сопротивление — десятки и сотни мегаом, ток утечки порядка микроампера. Это хорошо: вход не нагружает то, что к нему подключено. Но у этого есть следствие. Если к входу ничего не подключено, его напряжение ничем не задано.

У входа и провода, который к нему идёт, есть паразитная ёмкость — единицы-десятки пикофарад. На такую ёмкость наводится всё подряд: поле проводки 230 В в стене, соседние выводы, переключающиеся на мегагерцах, заряд с руки. При входном сопротивлении в сотни мегаом этого хватает, чтобы напряжение на выводе плавало от нуля до питания. Контроллер сравнивает его с порогами и выдаёт то LOW, то HIGH.

Пороги для ориентира:

КонтроллерПитаниеГарантированный LOWГарантированный HIGH
ESP323,3 Вниже 0,25 × VDD ≈ 0,8 Ввыше 0,75 × VDD ≈ 2,5 В
ATmega328P (Uno)5 Вниже 0,3 × VCC = 1,5 Ввыше 0,6 × VCC = 3 В

Между порогами — зона, где вход может прочитать что угодно. Висящий вход проводит там большую часть времени.

Кнопка сама по себе проблему не решает. Когда она нажата, вывод соединён с землёй, и уровень определён. Когда отпущена — вывод снова ни к чему не подключён. Поэтому кнопка «срабатывает сама» именно в отпущенном состоянии.

Как работает подтяжка

Решение — резистор с входа на питание. Пока кнопка разомкнута, ток через резистор течёт только в вход, то есть почти нулевой, и падение на резисторе ничтожно. Вход видит питание — логическую единицу. Когда кнопка замкнута, она соединяет вход с землёй напрямую. Резистор оказывается между питанием и землёй, через него идёт небольшой ток, а вход видит ноль.

Отсюда та самая перевёрнутая логика: нажато — LOW, отпущено — HIGH. Её не нужно запоминать как правило, она следует из схемы.

Так собрана схема Кнопка и подтягивающий резистор: источник 3,3 В, резистор 10 кОм, кнопка на землю, вход контроллера между ними.

Кнопка и подтягивающий резистор

Перед запуском ответьте, что прочтёт вход при отпущенной кнопке: 0 В, 1,65 В или 3,3 В. Затем нажмите кнопку и посмотрите, как напряжение уходит к нулю, а после уберите резистор и прочтите разбор про висящий вход.

Открыть схему

После запуска вход показывает почти 3,3 В: мешает только внутреннее сопротивление источника и утечка входа. При нажатии — ноль. Если убрать резистор совсем, разбор схемы сообщит, что вход ни к чему не подключён и его показания не определены.

Бывает и обратное включение: резистор на землю, кнопка на питание. Это подтяжка вниз (pull-down), и логика там прямая: нажато — HIGH. На ESP32 внутренняя подтяжка вниз есть, режим называется INPUT_PULLDOWN. У ATmega328P внутренней подтяжки вниз нет, только вверх.

Расчёт номинала

Номинал подтяжки — компромисс между тремя вещами.

Ток при нажатой кнопке. Через резистор течёт I = U / R. Для 10 кОм от 3,3 В:

I = 3,3 / 10 000 = 0,33 мА

Для кнопки, которую нажимают на секунду, это неважно. Для устройства на батарейке, где контакт замкнут часами (датчик открытия двери, геркон), — важно. При 1 кОм ток был бы 3,3 мА — в сотни раз больше, чем потребляет ESP32 в глубоком сне.

Падение от утечки при отпущенной кнопке. Ток утечки входа около 1 мкА течёт через подтяжку. На 10 кОм это 10 мВ — ничто. На 1 МОм это уже 1 В, и уровень 3,3 В превращается в 2,3 В, что ниже порога 2,5 В для ESP32. Слишком слабая подтяжка перестаёт подтягивать.

Устойчивость к наводкам и скорость. Резистор вместе с ёмкостью входа и провода образует RC-цепь. Чем больше сопротивление, тем медленнее вход возвращается к единице после отпускания и тем легче наводке его сдвинуть. Прикинем для провода длиной полметра с ёмкостью около 50 пФ:

τ = R × C
10 кОм × 50 пФ = 0,5 мкс
1 МОм × 50 пФ  = 50 мкс

Для кнопки обе цифры незаметны, но наводка с частотой сети и её гармоники легче «пробивают» высокоомную цепь. На длинных проводах подтяжку берут жёстче: 2,2–4,7 кОм.

Итог — практические диапазоны:

НоминалКогда
1–4,7 кОмДлинный провод, шумное окружение, шина I²C
10 кОмОбычная кнопка на плате — стандартный выбор
47–100 кОмБатарейное питание, контакт замкнут подолгу
больше 100 кОмНе рекомендуется: утечка и наводки начинают решать за вас

Внутренняя подтяжка: INPUT_PULLUP

В большинстве контроллеров подтягивающий резистор уже встроен в каждый вывод, и включается он одной строкой. Номинал у него не точный: у ESP32 порядка 45 кОм, у ATmega328P — от 20 до 50 кОм по документации. Для кнопки на плате этого хватает.

Пример Кнопка со внутренней подтяжкой для Uno использует ровно это: кнопка между выводом 2 и землёй, ни одного внешнего резистора.

const int BUTTON = 2;
const int LED = 8;

void setup() {
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  bool pressed = digitalRead(BUTTON) == LOW;
  digitalWrite(LED, pressed ? HIGH : LOW);
  delay(50);
}

Строка bool pressed = digitalRead(BUTTON) == LOW; — хорошая привычка. Она один раз переводит электрический уровень в смысл «нажата», и дальше в коде не нужно помнить об инверсии. Тот же приём стоит в примере Кнопка: чтение состояния для ESP32 с кнопкой на D15 — в мониторе порта печатается «нажата» или «отпущена» раз в 200 мс.

Когда внутренней подтяжки недостаточно:

  • провод к кнопке длиннее метра или идёт рядом с силовыми кабелями — нужен внешний резистор 2,2–4,7 кОм;
  • вывод не имеет внутренних резисторов: у ESP32 это GPIO 34–39, они работают только на вход и подтяжки не имеют;
  • нужна подтяжка к другому напряжению, чем питание контроллера;
  • шина I²C — там подтяжки нужны на обеих линиях, и внутренних 45 кОм для нормальной скорости шины мало.

Когда подтяжка не нужна

Подтяжка нужна там, где вход в каком-то состоянии остаётся ни к чему не подключённым. Переключатель на три вывода — другой случай. В примере Переключатель режимов на Uno средний вывод идёт на плату, а крайние — на землю и на 5 В. В любом положении вход к чему-то прижат, поэтому в setup() стоит обычный INPUT.

Строго говоря, в момент переключения, когда подвижный контакт уже оторвался от одного неподвижного и ещё не коснулся другого, вход на долю миллисекунды висит. Для выбора режима это неважно. Если бы такой переключатель считал импульсы, слабая подтяжка всё равно пригодилась бы.

То же касается выхода другой микросхемы с двухтактным выходом: он сам задаёт и ноль, и единицу. А вот выход с открытым коллектором или открытым стоком умеет только притягивать к земле, и без подтяжки его единицу не прочитать.

Типичные ошибки

  • INPUT вместо INPUT_PULLUP с кнопкой на землю. Самая частая. Работает «почти всегда», пока рядом нет наводок, и отказывает, когда рядом включили сварку или положили руку на провод. В симуляторе наводок нет: цифровой вход без подтяжки и без цепи читается нулём, и кнопка выглядит нажатой всё время.
  • Кнопка на питание при подтяжке вверх. Вывод подтянут к 3,3 В, кнопка замыкает его на те же 3,3 В — нажатие ничего не меняет. Кнопку с INPUT_PULLUP подключают к земле.
  • Забыли инверсию. Код if (digitalRead(BUTTON) == HIGH) при подтяжке вверх срабатывает, когда кнопка отпущена. Светодиод горит всё время и гаснет при нажатии.
  • Подтяжка 1 МОм «для экономии». Утечка входа съедает запас по порогу, наводки проходят.
  • Кнопка на 5 В к входу ESP32. Подтяжку к 5 В на вход контроллера с питанием 3,3 В ставить нельзя: напряжение на выводе превысит допустимое. Подтягивают к 3,3 В.

Что попробовать в симуляторе

В аналоговой схеме с подтяжкой поменяйте номинал резистора с 10 кОм на 1 кОм и посчитайте ток при нажатой кнопке: 3,3 мА вместо 0,33 мА. Затем верните 10 кОм и посмотрите, как мало меняется напряжение на входе в отпущенном состоянии.

В кнопке со светодиодом скетч умещается в одну строку digitalWrite(LED, digitalRead(BUTTON) == LOW);. Уберите == LOW — и светодиод будет гореть, пока кнопка отпущена. Так инверсия видна наглядно.

Коротко

Цифровой вход почти не потребляет ток, поэтому ни к чему не подключённый вывод принимает любое наведённое напряжение. Подтягивающий резистор задаёт уровень по умолчанию: вход подтянут к питанию, кнопка притягивает его к земле, поэтому нажатие читается как LOW. Обычный номинал — 10 кОм, на длинных проводах меньше, на батарейках больше. Для кнопки на плате хватает встроенной подтяжки INPUT_PULLUP; переключателю, который всегда к чему-то прижат, подтяжка не нужна.

Попробуйте в симуляторе

Arduino Uno: кнопка со внутренней подтяжкой

Замените == LOW на == HIGH в строке с pressed: светодиод на выводе 8 будет гореть при отпущенной кнопке — так выглядит забытая инверсия

Кнопка на Uno

Кнопка: чтение состояния

Сократите delay(200) до delay(20) и понажимайте кнопку на D15: в мониторе порта строки пойдут вдесятеро чаще, а нажатие по-прежнему читается как LOW

Чтение кнопки

Кнопка зажигает светодиод

Добавьте вторую кнопку между D4 и GND с INPUT_PULLUP и зажигайте светодиод на D2, только когда нажаты обе — оба условия пишутся через == LOW

Кнопка и светодиод

Arduino Uno: переключатель режимов

Посмотрите на схему: средний вывод переключателя всегда прижат к GND или 5V, поэтому в setup() стоит обычный INPUT — сравните с кнопкой

Переключатель без подтяжки

← Все статьи