Подтягивающие резисторы

Введение #

В этой документации мы разберём, почему пины «плавают», как резисторы это исправляют, как рассчитать нужный номинал с помощью концепции делителя напряжения, и как легко управлять этим с помощью библиотеки RepkaPi.GPIO и напрямую через регистры Repka Pi.


Теоретические основы #

Проблема: «плавающее» состояние пина

Когда вы настраиваете пин GPIO в режим входа (GPIO.IN), он превращается в чувствительный вольтметр. Он постоянно измеряет напряжение, чтобы определить его логический уровень:

  • ВЫСОКИЙ (HIGH, 1): На пин подано напряжение, близкое к напряжению питания (3.3В).
  • НИЗКИЙ (LOW, 0): На пин подано напряжение, близкое к земле (0В).

А что, если к пину ничего не подключено? Или подключена кнопка, но она не нажата? В этом случае пин остаётся «висеть в воздухе». Он не соединён ни с 3.3В, ни с GND. Это состояние называется высокоимпедансным (Z-состояние) или «плавающим». Пин в таком состоянии становится крайне уязвимым для любых электромагнитных помех, превращаясь в антенну. В результате микроконтроллер считывает случайный «мусор», что и приводит к хаотичным срабатываниям.

Решение: даём пину точку опоры

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

1. Подтягивающий резистор (Pull-Up) Мы подключаем резистор между пином GPIO и питанием (3.3В).

  • Когда кнопка не нажата: Резистор «подтягивает» напряжение на пине к 3.3В. Микроконтроллер уверенно читает HIGH.
  • Когда кнопка нажата: Кнопка замыкает цепь, соединяя пин напрямую с землёй (GND). Ток выбирает путь наименьшего сопротивления (через кнопку, а не через резистор), и напряжение на пине падает до 0В. Микроконтроллер читает LOW.

2. Стягивающий резистор (Pull-Down) Работает по обратному принципу: мы подключаем резистор между пином GPIO и землёй (GND).

  • Когда кнопка не нажата: Резистор «стягивает» напряжение на пине к 0В. Микроконтроллер читает LOW.
  • Когда кнопка нажата: Кнопка замыкает цепь, соединяя пин с питанием (3.3В). Микроконтроллер читает HIGH.

Почему именно 10 кОм? Разбираем делитель напряжения

Номинал резистора в 10 кОм встречается чаще всего. Это не случайность, а инженерный компромисс, который легко понять через концепцию делителя напряжения. Делитель напряжения — это простая схема из двух последовательных резисторов (R1 и R2), которая позволяет получить на выходе напряжение, являющееся частью входного.

Представим нашу схему со стягивающим резистором (pull-down) в виде делителя, где R1 — это сопротивление кнопки, а R2 — наш резистор. Формула для расчёта выходного напряжения: U_out = U_in * (R2 / (R1 + R2)).

Расчет при нажатой кнопке (R1 ≈ 0 Ом): V_pin = 3.3 * (10000 / (0 + 10000)) = 3.3 * 1 = 3.3В (чёткий ВЫСОКИЙ уровень).

Вывод: Резистор в 10 кОм идеален для кнопок, так как он достаточно велик, чтобы ток при нажатии был мизерным (I = 3.3В / 10000Ом = 0.33мА), но при этом достаточно мал, чтобы обеспечить стабильный логический уровень.


Практическая реализация #

Способ 1: Программное управление (рекомендуемый)

К счастью, вам не всегда нужно возиться с внешними резисторами. В процессор Repka Pi уже встроены программно управляемые подтягивающие и стягивающие резисторы.

Схема:

  • Pin 15 (GPIO) <-> один контакт кнопки
  • Второй контакт кнопки <-> 3.3V (Pin 1)
  • Pin 12 (GPIO) <-> анод светодиода (+)
  • Катод светодиода (-) <-> резистор 220 Ом <-> GND (Pin 6)

Код:

import RepkaPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BOARD)

button_pin = 15
led_pin = 12

# Настраиваем пин кнопки как ВХОД и включаем внутренний СТЯГИВАЮЩИЙ резистор.
# Теперь по умолчанию на пине будет LOW.
GPIO.setup(button_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(led_pin, GPIO.OUT)

print("Скрипт запущен. Нажмите CTRL+C для выхода.")

try:
    while True:
        # Поскольку у нас стягивающий резистор (pull-down),
        # нажатие кнопки подаст 3.3В на пин, и его состояние станет HIGH.
        if GPIO.input(button_pin) == GPIO.HIGH:
            GPIO.output(led_pin, GPIO.HIGH)
        else:
            GPIO.output(led_pin, GPIO.LOW)
        sleep(0.05)
finally:
    print("\nЗавершение работы. Гасим светодиод и сбрасываем настройки GPIO.")
    GPIO.cleanup()

Если бы мы хотели использовать подтягивающий резистор (pull_up_down=GPIO.PUD_UP), нам бы пришлось поменять схему (кнопка должна замыкать на GND) и логику в коде (if GPIO.input(button_pin) == GPIO.LOW:).

Способ 2: Прямое управление регистрами (продвинутый)

Библиотека RepkaPi.GPIO абстрагирует пользователя от сложностей. На самом деле, каждая команда преобразуется в низкоуровневые операции с аппаратными регистрами процессора. В процессоре Allwinner за конфигурацию подтяжки отвечают регистры Px_PULLn. На каждый пин отводится по 2 бита, которые работают как переключатель:

  • 00: Подтяжка выключена (Z-состояние).
  • 01: Включена подтяжка к питанию (Pull-Up).
  • 10: Включена стяжка к земле (Pull-Down).

Давайте сделаем то же самое, что и GPIO.setup(..., pull_up_down=GPIO.PUD_UP), но своими руками.

Внимание! Следующий код требует прав суперпользователя (sudo) и работает с физической памятью напрямую. Ошибка в адресе или значении может привести к зависанию системы.

Задача: Настроить пин PL10 как вход и включить на нём внутреннюю подтяжку к питанию (Pull-Up).

import mmap
import os
import time

# --- Константы из документации на процессор Allwinner H5 ---
GPIO_BASE = 0x01C20800
PORTL_CONF_OFFSET = 0x240 # Уточненный адрес для Port L
PORTL_PULL_OFFSET = 0x25C # Уточненный адрес для Port L
PORTL_DATA_OFFSET = 0x250 # Уточненный адрес для Port L
PIN_NUM = 10

mem_fd = os.open('/dev/mem', os.O_RDWR | os.O_SYNC)
gpio_map = mmap.mmap(fileno=mem_fd, length=4096, offset=GPIO_BASE)

try:
    # --- Шаг 1: Настраиваем пин PL10 на ВХОД ---
    conf_reg_val = int.from_bytes(gpio_map[PORTL_CONF_OFFSET : PORTL_CONF_OFFSET + 4], 'little')
    conf_reg_val &= ~(0b1111 << (PIN_NUM * 4)) # Очищаем и ставим 0000 (Input)
    gpio_map[PORTL_CONF_OFFSET : PORTL_CONF_OFFSET + 4] = conf_reg_val.to_bytes(4, 'little')
    print("Пин PL10 настроен как ВХОД.")

    # --- Шаг 2: Включаем подтяжку (Pull-Up) ---
    pull_reg_val = int.from_bytes(gpio_map[PORTL_PULL_OFFSET : PORTL_PULL_OFFSET + 4], 'little')
    pull_reg_val &= ~(0b11 << (PIN_NUM * 2)) # Очищаем
    pull_reg_val |=  (0b01 << (PIN_NUM * 2)) # Устанавливаем 01 (Pull-Up)
    gpio_map[PORTL_PULL_OFFSET : PORTL_PULL_OFFSET + 4] = pull_reg_val.to_bytes(4, 'little')
    print("Для пина PL10 включена подтяжка Pull-Up.")
    
    # --- Шаг 3: Проверяем результат ---
    print("\nНачинаем чтение состояния пина (нажмите CTRL+C для выхода):")
    while True:
        data_reg_val = int.from_bytes(gpio_map[PORTL_DATA_OFFSET : PORTL_DATA_OFFSET + 4], 'little')
        pin_state = (data_reg_val >> PIN_NUM) & 1
        print(f"Состояние пина PL10: {pin_state} (1 = HIGH)")
        time.sleep(1)

except KeyboardInterrupt:
    print("\nЗавершение работы.")
finally:
    gpio_map.close()
    os.close(mem_fd)

326 просмотров0 комментариев

Комментарии (0)

Для участия в обсуждении Вы должны быть авторизованным пользователем
Разделы

Навигация

ВойтиРегистрация