События и прерывания в библиотеке RepkaPi.GPIO SysFS

В предыдущей главе мы успешно считывали состояние кнопки, но делали это внутри бесконечного цикла while True, постоянно опрашивая пин с помощью GPIO.input(). Этот метод, известный как опрос (polling), имеет серьезный недостаток: он непрерывно загружает ядро процессора, даже когда ничего не происходит.

Для создания эффективных и отзывчивых программ применяется более совершенный подход — обработка событий, основанная на механизме аппаратных прерываний.

От опроса к событиям: Философия подхода

  • Модель опроса: Ваша программа постоянно и активно спрашивает у системы: «Состояние изменилось? А сейчас? А сейчас?». 99.9% ответов будут "нет", но процессор все равно тратит на это ресурсы.
  • Событийная модель: Ваша программа говорит системе: «Пожалуйста, следи за этим пином. Когда на нем произойдет нужное мне событие, сообщи мне об этом. А до тех пор я буду заниматься другими делами (или просто спать)».

Этот подход кардинально снижает нагрузку на процессор и позволяет системе реагировать на внешние сигналы практически мгновенно.

Как это реализовано в RepkaPi.GPIO

Библиотека RepkaPi.GPIO использует стандартные и высокоэффективные механизмы ядра Linux для реализации событийной модели.

  1. Интерфейс sysfs: Когда вы просите библиотеку отслеживать событие, она записывает тип нужного триггера (например, rising или falling) в специальный файл /sys/class/gpio/gpioX/edge.
  2. Системный вызов epoll: Вместо бесконечного цикла, библиотека использует системный вызов epoll. Он позволяет процессу "уснуть" и передать ядру Linux задачу по наблюдению за файловым дескриптором, связанным с GPIO.
  3. Аппаратное прерывание: Когда на пине происходит физическое событие, аппаратура SoC генерирует прерывание. Ядро Linux перехватывает его и, видя, что за этим пином велось наблюдение, "будит" вашу программу.
  4. Функция обратного вызова (Callback): Пробудившись, библиотека вызывает предоставленную вами Python-функцию — обработчик, в котором и находится логика реакции на событие.

Весь этот сложный процесс происходит в фоновом потоке, что позволяет вашему основному коду выполняться, не блокируясь в ожидании.

Практическое применение: Реагируем на нажатие кнопки

Давайте модернизируем наш предыдущий пример. Теперь программа не будет постоянно проверять кнопку. Вместо этого мы "попросим" систему сообщить нам, когда кнопка будет нажата, и автоматически вызовем функцию для переключения светодиода.

1. Аппаратная часть и схема: Используем ту же самую схему, что и в предыдущей главе, с внешним стягивающим (pull-down) резистором.

  • Светодиод подключен к пину №11.
  • Кнопка подключена к пину №7.
  • Состояние кнопки по умолчанию — LOW, при нажатии — HIGH. Следовательно, мы будем отслеживать нарастающий фронт (GPIO.RISING).

2. Ключевые функции API:

  • GPIO.add_event_detect(pin, edge, callback, bouncetime): Основная функция. Настраивает отслеживание события edge (RISING, FALLING, BOTH) на пине pin и привязывает к нему функцию callback.
  • bouncetime (в миллисекундах): Крайне полезный параметр для подавления "дребезга контактов" — ложных срабатываний, возникающих при физическом замыкании/размыкании механической кнопки. Библиотека будет игнорировать последующие события в течение указанного времени после первого срабатывания.

3. Программный код (event_driven.py)

# -*- coding: utf-8 -*-

import RepkaPi.GPIO as GPIO
from time import sleep

# --- 1. Настройка ---
GPIO.setmode(GPIO.BOARD)

BUTTON_PIN = 7
LED_PIN = 11

# Настраиваем пины на вход и выход
GPIO.setup(LED_PIN, GPIO.OUT)
GPIO.setup(BUTTON_PIN, GPIO.IN)

# --- 2. Функция обратного вызова (Callback) ---
# Эта функция будет автоматически вызвана в фоновом потоке, когда произойдет событие.
def toggle_led(channel):
    """
    Переключает состояние светодиода.
    Параметр 'channel' обязателен - библиотека передает в него номер пина,
    на котором произошло событие.
    """
    print(f"Событие обнаружено на канале {channel}!")
    # Считываем текущее состояние светодиода и инвертируем его
    current_state = GPIO.input(LED_PIN)
    GPIO.output(LED_PIN, not current_state)

# --- 3. Регистрация события и основной цикл ---
try:
    print("Программа запущена. Нажимайте на кнопку, чтобы переключать светодиод.")
    print("Для выхода нажмите CTRL+C.")

    # Регистрируем отслеживание события:
    # - на пине BUTTON_PIN
    # - по нарастающему фронту (RISING)
    # - с вызовом функции toggle_led
    # - с защитой от дребезга в 200 мс
    GPIO.add_event_detect(BUTTON_PIN, GPIO.RISING, callback=toggle_led, bouncetime=200)

    # Основной поток программы теперь свободен.
    # Он может выполнять другие задачи или просто спать, не тратя ресурсы.
    while True:
        # Здесь могла бы быть другая полезная работа...
        sleep(1) # Программа просто ждет, пока ее не прервут.

# --- 4. Очистка ---
except KeyboardInterrupt:
    print("\nЗавершение работы.")
finally:
    # Эта команда не только сбросит настройки пинов, но и корректно
    # остановит фоновый поток отслеживания событий.
    GPIO.cleanup()

4. Запуск и результат

Запустите скрипт: python3 event_driven.py. Программа выведет сообщение и "затихнет". Теперь каждое нажатие на кнопку будет вызывать функцию toggle_led, которая переключит состояние светодиода. При этом основной цикл программы не выполняет никаких проверок — вся работа происходит асинхронно, по событию от аппаратуры.


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

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

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

Навигация

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