Skip to main content

Логические уровни и подтяжки

Почему Arduino и Raspberry Pi иногда не дружат? 🤔 Почему кнопка без резистора «глючит»? И как не сжечь вход микроконтроллера?

Всё дело в логических уровнях.

🎓 В курсе nand2cpu
Все наши схемы работают на 5 В — это классические TTL-уровни: Урок 1: Вентиль NAND

Что такое 0 и 1 на самом деле

Цифровая электроника не видит «0» и «1» — она видит напряжение:

5В ─────┬───────────────────── VCC
        │   ████████  ← Это "1" (HIGH)
        │   ████████
 ~2.5В ─┼───────────── граница (не точная!)
        │   ████████  ← Это "0" (LOW)  
        │   ████████
0В ─────┴───────────────────── GND

Но граница — не ровно посередине! У разных микросхем разные пороги.

5 В vs 3.3 В — главная проблема

        5V логика         3.3V логика
        
5.0 В ─── HIGH           ─── 💥 ОПАСНО!
                         
3.3 В ─── HIGH           ─── HIGH (VCC)

2.0 В ─── ?              ─── ?

0.8 В ─── LOW            ─── LOW

0 В   ─── LOW            ─── LOW

Arduino (5 В) → Raspberry Pi (3.3 В)

Arduino         Raspberry Pi
  5 В ───────────► 3.3 В вход
  
  ⚠️ ПЛОХО! Может сжечь вход Pi!

Raspberry Pi (3.3 В) → Arduino (5 В)

Raspberry Pi    Arduino
  3.3 В ─────────► 5 В вход
  
  ✅ Обычно OK — Arduino видит 3.3 В как "1"
  (но проверь datasheet!)

Как подружить 5 В и 3.3 В

Способ 1: Делитель напряжения (самый простой)

Два резистора делят напряжение:

5 В ────[10k]────┬────► ~3.3 В
               [20k]
GND ─────────────┘

Формула: $U_{out} = U_{in} \times \frac{R_2}{R_1 + R_2} = 5 \times \frac{20}{30} ≈ 3.3$ В

Популярные комбинации:

R1R2Результат
10 кОм20 кОм3.33 В
1 кОм2 кОм3.33 В
10 кОм22 кОм3.44 В

✅ Плюс: очень просто, 2 детали
❌ Минус: только для медленных сигналов (кнопки, датчики)

Способ 2: Модуль level shifter

Для быстрых сигналов (I²C, SPI, UART) используй готовый модуль:

┌─────────────────────┐
│   Level Shifter     │
│                     │
│  HV ──── 5 В        │  HV = High Voltage
│  LV ──── 3.3 В      │  LV = Low Voltage
│                     │
│  HV1 ◄───► LV1      │  ← двунаправленный!
│  HV2 ◄───► LV2      │
│  HV3 ◄───► LV3      │
│  HV4 ◄───► LV4      │
└─────────────────────┘

Ищи на AliExpress: «logic level converter» или «level shifter 3.3v 5v»

Типы входов и выходов

Вход (INPUT)

                ┌───────┐
Сигнал ─────────┤ ВХОД  │
                │       │  "Слушает" напряжение
                └───────┘

Вход имеет очень большое сопротивление — почти не потребляет ток.

Выход Push-Pull (обычный)

        VCC
        [T1] ← включается для "1"
OUT ─────┼──► активно выдаёт 0 или 1
        [T2] ← включается для "0"
        GND

Может толкать ток наружу (1) и тянуть ток внутрь (0).

Выход Open-Drain (открытый сток)

        VCC
       [pull-up] ← внешний резистор!
OUT ─────┼──► 
        [T]  ← только тянет вниз
        GND

Может только притягивать к GND. Для «1» нужна внешняя подтяжка.

Где используется: I²C (SDA, SCL), кнопки, много устройств на одной линии.

Подтяжка — зачем она нужна

Проблема: вход без подключения «висит в воздухе» и ловит помехи.

НЕПРАВИЛЬНО:           ПРАВИЛЬНО:

    Кнопка               VCC
       │                  │
       │                [10k] ← pull-up
       │                  │
МК ────┴── ?           МК ┼──── = 1 (покой)
   "Плавает"           Кнопка
   случайные 0/1          │
                        GND ── = 0 (нажата)

Pull-up (к питанию)

VCC ────[R]────┬──── МК вход
            Кнопка
GND ───────────┘

Покой: вход = 1
Нажата: вход = 0

Pull-down (к земле)

VCC ───────────┐
            Кнопка
МК вход ──┬────┘
GND ────[R]

Покой: вход = 0
Нажата: вход = 1

Типичные номиналы подтяжки

ПрименениеРезисторПочему
Кнопка10 кОмСтандарт, низкий ток
I²C4.7 кОмБыстрее, стандарт
Длинные провода2.2–4.7 кОмСильнее, против помех
Экономия батареи47–100 кОмМинимальный ток

Встроенные подтяжки

У большинства микроконтроллеров есть внутренние pull-up:

// Arduino
pinMode(2, INPUT_PULLUP);

// STM32 / ESP32
gpio_set_pull_mode(GPIO_NUM_2, GPIO_PULLUP_ONLY);

✅ Удобно — не нужен внешний резистор
❌ Слабые (20–50 кОм) — для длинных проводов лучше внешний

Практическая шпаргалка

Кнопка — как подключить

Вариант 1 (pull-up):       Вариант 2 (внутренний):

VCC                        VCC
 │                          │
[10k]                    [внутр.]
 │                          │
 ├───► МК                   ├───► МК (INPUT_PULLUP)
 │                          │
[BTN]                     [BTN]
 │                          │
GND                        GND

Нажата = 0                 Нажата = 0
Отпущена = 1               Отпущена = 1

Датчик (5 В) → МК (3.3 В)

Датчик                     МК 3.3В
  │                          │
  ├──[10k]──┬──[20k]──GND    │
  │         │                │
  └─────────┴────────────────┘
            ~3.3 В

I²C — два устройства с разным питанием

3.3V ──────┬──────────┬───── LV (level shifter)
           │          │
         [4.7k]    [4.7k]
           │          │
  SDA ─────┼──────────┼───── LV1 ◄──► HV1 ───┬─── SDA (5V устройство)
           │          │                      │
  SCL ─────┼──────────┴───── LV2 ◄──► HV2 ───┼─── SCL
           │                                 │
5V ────────┴──────────────────────────── HV ─┘

Опасные ошибки

ОшибкаПоследствиеКак избежать
5 В → 3.3 В вход💥 Сгоревший пинДелитель или level shifter
Вход «в воздухе»Случайные 0/1Всегда подтяжка!
Слишком слабый pull-up для I²CНе работаетИспользуй 4.7 кОм
Забыл общий GNDНичего не работаетСоедини GND всех устройств

Связь с другими темами

  • Антидребезг — кнопки и подтяжки: debouncing
  • Логические элементы — TTL/CMOS уровни: logic_gates
  • Резисторы — делители напряжения: резисторы
  • nand2cpu — работаем на 5 В: Акт I

Мини-задания

  1. Arduino выдаёт 5 В, а ESP32 принимает 3.3 В. Какие резисторы взять для делителя?

  2. Почему кнопка без подтяжки иногда «нажимается сама»?

  3. Какой резистор pull-up взять для I²C?

  4. В чём разница между push-pull и open-drain выходом?

  5. Можно ли подать 3.3 В сигнал на 5 В вход Arduino?

Details
  1. R1 = 10 кОм, R2 = 20 кОм (или 1 кОм и 2 кОм) — получится ~3.3 В

  2. Вход «плавает» и ловит электромагнитные помехи — показывает случайные значения

  3. 4.7 кОм — стандарт для I²C

  4. Push-pull активно выдаёт и 0, и 1. Open-drain только притягивает к 0, для 1 нужна внешняя подтяжка

  5. Обычно да — Arduino видит 3.3 В как логическую «1» (порог ~2 В), но лучше проверить datasheet