Программирование, радиоэлектроника,
саморазвитие и частичка из моей жизни здесь...

RPi.GPIO - работа с входами, выходами и прерываниями в Raspberry Pi, примеры на Python

Познакомимся с возможностями модуля RPi.GPIO - разберемся с использованием входов и выходов GPIO в Raspberry Pi, узнаем как использовать прерывания и широтно-импульсную модуляцию (PWM). Приведу простые примеры программ на языке Python, которые помогут выполнить первые эксперименты и понять как все это работает.

Кроме того, из статьи вы узнаете много другой полезной информации, которая может быть полезна для понимания принципов построения программ, написанных специально для Raspberry Pi.

Во всех примерах программ будет использован Python 3-й версии (python3). Для его установки достаточно одной команды в консоли Raspbian:

sudo apt-get install python3

Содержание:

  1. Возможности RPi.GPIO
  2. Установка модуля, узнаем его текущую версию
  3. Режимы нумерации пинов, настройка каналов
  4. Подтягивающие резисторы (Pull-Up, Pull-Down resistors)
  5. Установка и чтение состояний каналов
  6. Сброс (очистка) состояний каналов
  7. Чтение состояния канала (пример с кнопкой)
  8. Установка состояния канала (пример со светодиодом)
  9. Прерывания - что это такое и зачем они нужны
  10. Обнаружение порога срабатывания (Rising/falling edge)
  11. Мультипоточный обратный отклик (threaded callback)
  12. Что такое "дребезг контактов" и как его перебороть
  13. Широтно-импульсная модуляция (PWM)
  14. Узнаем текущий установленный режим для GPIO каналов
  15. В завершение

Возможности RPi.GPIO

RPi.GPIO - модуль на языке Python, который предназначен для управления каналами GPIO в мини-компьютере Raspberry Pi. Важно заметить, что данный модуль нельзя использовать в приложениях реального времени и там, где время выполнения и реакции является одним из важнейших факторов.

Операционная система GNU/Linux является мультизадачной, поэтому какой-либо другой запущенный процесс может отобрать приоритет по ресурсам микропроцессора (CPU) и таким образом, повлиять на появление задержек в выполнении работающей с GPIO программы на Python. Это нужно учитывать при проектировании времязависимых устройств и приложений.

Для проектов, где временный фактор является критичным, лучше использовать AVR микроконтроллеры или что-то в этом духе. К тому же, выполненный на основе микроконтроллера модуль, можно связать с Raspberry Pi и тем самым получить или переслать ему какие-то данные.

Основные возможности модуля RPi.GPIO:

  • Считывание состояния каналов, сконфигурированных на вход (input);
  • Реакция на прерывания, инициируемые любым из каналов в режиме input;
  • Управление состоянием каналов, сконфигурированных на выход (output);
  • PWM (Pulse-Width Modulation) - широтно-импульсная модуляция на каналах в режиме output.
  • Получение информации о платформе и конфигурации пинов.

В одной из своих статей я уже рассказывал об использовании GPIO, там приведено расположение выводов (пинов, pins) на гребенке малинки, а также было продемонстрировано как правильно подключить кнопку и светодиод. В описанных там программах также был использован модуль RPi.GPIO. Здесь мы рассмотрим его очень подробно.

Расположение пинов GPIO на платформе Raspberry Pi

Рис. 1. Расположение пинов GPIO на платформе Raspberry Pi.

Установка модуля, узнаем его текущую версию

Свежую версию модуля RPi.GPIO для Python всегда можно найти на страничке - https://pypi.python.org/pypi/RPi.GPIO

Там же есть описание всех изменений (Change Log) для разных версий пакета, список текущих возможностей и другие полезные данные.

Получим свежий список доступных к установке пакетов из репозитория для Raspbian:

sudo apt-get update

Установим модуль RPi.GPIO для Python версий 3.х и 2.х (если нужно):

sudo apt-get install python3-rpi.gpio
sudo apt-get install python-rpi.gpio

Чтобы узнать текущую установленную версию модуля достаточно выполнить следующую команду:

dpkg --status python3-rpi.gpio | grep Version

Результат будет выглядеть примерно вот так:

Version: 0.6.3~stretch-1

Также, версию модуля можно узнать используя консольный командный интерпретатор Python:

python3

В командном интерфейсе Python вводим следующие команды:

import RPi.GPIO
print(RPi.GPIO.VERSION)

Получим версию модуля одной строчкой:

0.6.3

Для получения информации о платформе из модуля RPi.GPIO введите в интерпретаторе следующую команду:

RPi.GPIO.RPI_INFO

Из вывода можно узнать ревизию и тип платы, количество оперативной памяти, название процессора (пример для Raspberry Pi 3 model B):

{'P1_REVISION': 3, 'TYPE': 'Pi 3 Model B', 'MANUFACTURER': 'Sony', 'RAM': '1024M', 'REVISION': 'a02082', 'PROCESSOR': 'BCM2837'}

Еще одна полезная команда в интерпретаторе, которая позволит вывести на экран содержимое объекта RPi.GPIO:

dir(RPi.GPIO)

Эти данные помогут понять содержание модуля, здесь можно увидеть доступные к использованию константы и методы.

Пример вывода команды:

['BCM', 'BOARD', 'BOTH', 'FALLING', 'HARD_PWM', 'HIGH', 'I2C', 'IN',
'LOW', 'OUT', 'PUD_DOWN', 'PUD_OFF', 'PUD_UP', 'PWM', 'RISING', 
'RPI_INFO', 'RPI_REVISION', 'SERIAL', 'SPI', 'UNKNOWN', 'VERSION', 
'__builtins__', '__cached__', '__doc__', '__file__', '__loader__', 
'__name__', '__package__', '__path__', '__spec__', 'add_event_callback', 
'add_event_detect', 'cleanup', 'event_detected', 'getmode', 
'gpio_function', 'input', 'output', 'remove_event_detect', 'setmode', 
'setup', 'setwarnings', 'wait_for_edge']

Для выхода из интерпретатора Python 3 вводим команду "exit()".

Все команды, которые мы вводили в консоли интерпретатора, можно использовать в будущих программах на Python.

Режимы нумерации пинов, настройка каналов

Поскольку модуль уже установлен в системе, для его использования в программе достаточно сделать импорт:

import RPi.GPIO as GPIO

Теперь нужно определиться с предпочитаемой системой нумерации пинов GPIO, здесь есть два варианта:

  1. по названию каналов, например: 7 (IO7), 21 (IO21)
  2. по номеру пина на гребенке коннектора GPIO, например: 26 (IO7), 40 (IO21)

Для первого варианта инициализация выполняется следующей строчкой кода:

GPIO.setmode(GPIO.BCM)

Для второго варианта:

GPIO.setmode(GPIO.BOARD)

 Получить текущий режим нумерации пинов можно методом "getmode":

pin_mode = GPIO.getmode()   # значением может быть "GPIO.BOARD", "GPIO.BCM" или None

Прежде чем приступить к считыванию и установке уровней на каналах и соответствующих им пинах малинки, нужно выполнить их изначальную конфигурацию (setup). Установка режимов работы каналов выполняется одноименным методом "setup", например установим 7-й канал на вход, а 21-й на вывод:

GPIO.setup(7, GPIO.OUT)
GPIO.setup(21, GPIO.IN)

"GPIO.OUT" и "GPIO.IN" - это константы модуля "RPi.GPIO", содержащие следующие значения: GPIO.OUT = 0, GPIO.IN = 1.

Проверить значения этих констант можно запустив интерпретатор "python3" (как в примере выше) и набрав в его консоли следующую последовательность команд:

import RPi.GPIO as GPIO
GPIO.IN
GPIO.OUT

Зная значения констант можно переписать пример с установкой режимов для пинов 7 и 21 вот так:

GPIO.setup(7, 0)
GPIO.setup(21, 1)

Согласитесь, с такой записью очень легко запутаться, особенно при работе с большим количеством пинов и в большой программе. Поэтому использование констант "GPIO.OUT" и "GPIO.IN" - это более удобное и верное решение.

Для установки начального состояния сконфигурированного на вывод канала (высокий или низкий уровень) можно добавить параметр "initial" с указанием нужного уровня. Например:

GPIO.setup(7, GPIO.OUT, initial=GPIO.HIGH)
GPIO.setup(8, GPIO.OUT, initial=GPIO.LOW)

"GPIO.HIGHT" и "GPIO.LOW" - это константы модуля "RPi.GPIO", которые содержат эквивалентные значения для состояний каналов:

  • HIGH - число 1, высокий уровень (+3,3В);
  • LOW - число 0, низкий уровень (0В).

Также есть возможность установить один и тот же режим работы для списка из нескольких каналов:

GPIO.setup([7, 21, 8], GPIO.OUT)

Подтягивающие резисторы (Pull-Up, Pull-Down resistors)

К каждому из пинов GPIO, сконфигурированным на вход, могут быть подключены внутренние подтягивающие резисторы (Pull-Up, Pull-Down resistors). Они могут пригодиться для установки значения по умолчанию на канале, который соответствует нужному пину. Этот пин может быть никуда не подключен и иметь неопределенное состояние (плавающее, float state).

На таком "плавающем" пине могут хаотично возникать разные состояния (то 0, то 1), что недопустимо в случае если к нему подключена какая-то кнопка или другое подобное устройство, состояние которого планируется отслеживать.

Чтобы "приземлить" этот пин (установить по умолчанию значение канала в 0) или же подтянуть его к высокому уровню (значение 1) можно воспользоваться параметром "pull_up_down".

Например:

GPIO.setup(7, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

Допустим что к пину, который соответствует каналу 7, подключена кнопка, другой же вывод кнопки подключен через резистор к земле, как на схеме:

Схема подключения к GPIO кнопки, используя внутренний подтягивающий резистор (Pull-Up)

Рис. 2. Схема подключения к GPIO кнопки, используя внутренний подтягивающий резистор (Pull-Up).

Для отслеживания состояния кнопки SW1, канал GPIO IO7 установим в режим ввода (input) и включим подтягивающий к высокому уровню (Pull-Up) резистор Rb. Пока кнопка SW1 не нажата, на пин соответствующий каналу IO7, через подтягивающий резистор поступает напряжение 3,3В и при считывании состояния этого канала мы получим 1 (высокий уровень).

После нажатия кнопки, уровень напряжения (которое поступает через подтягивающий резистор) на пине канала IO7 очень сильно упадет, состояние канала станет - 0 (низкий уровень).

Возможно кто-то спросит: Зачем здесь резистор R1, ведь можно обойтись и без него? - можно, но я считаю что это не безопасно. Сам встречал уже не раз примеры, в которых кнопку подключают напрямую к пинам GPIO и GND - не рекомендую так поступать.

В момент включения малинки и загрузки ОС, а также при не верно сконфигурированном канале для пина GPIO, при переходных процессах и в других случаях, этот пин может быть кратковременно выставлен как выходной (output) и на нем может появиться как высокий, так и низкий уровень.

Если же в этот момент кнопка нажата (такое совпадение мало вероятно, но все же) или же замкнут переключатель (вполне вероятно), то вывод пина GPIO, сконфигурированный на выход (output), будет напрямую подключен к земле (GND). В данном случае достаточно кратковременного появления на этом пине высокого уровня, чтобы спалить GPIO и повредить плату Raspberry Pi.

Если же подобное случится с включенным последовательно кнопке резистором на 300 Ом, то через пин GPIO потечет ток, равный:

I = U/R = 3,3V/300R = 0,011A = 11mA.

Такой ток не причинит никакого вреда каналу GPIO. После загрузки ОС, исправления возможных ошибок в конфигурации и других переходных процессах, все будет работать исправно и канал GPIO останется исправным.

Также можно подсчитать напряжение на входе IO7 (рисунок 2) в момент, когда кнопка SW1 нажата и включен Pull-UP резистор Rb. Используем формулу для расчета резистивного делителя напряжения, который состоит в данном случае из резисторов Rb и R1 и с входным напряжением 3,3В. 

Искомое значение "x" - это напряжение между GND и IO7 в момент, когда включен резистор Rb и нажата кнопка SW1.

R1 = Rb / (3,3V/x - 1), 
300 Ом = 10000 Ом / (3,3V/x - 1),
3,3V/x - 1 = 10000/300 = 33.33333,
3,3V/x = 33.33333 + 1 = 34.33333,
x = 3.3/34.33333 =~ 0.096V.

Как видим, сопротивления резистора R1 в 300 Ом должно хватить чтобы напряжение 3,3В, приходящее через резистор Rb, просело примерно до 0,1В и таким образом, удалось получить низкий уровень на IO7.

Установка и чтение состояний каналов

Для получения состояния сконфигурированного на вход канала служит метод "input". Считать текущее значение 7-го канала в переменную ch7_state можно вот так:

ch7_state = GPIO.input(7)

В зависимости от уровня сигнала на канале, переменная будет содержать одно из чисел:

  • 0 - низкий уровень, соответствует 0В на пине GPIO;
  • 1  - высокий уровень, +3,3В на пине GPIO.

Для проверки состояния канала можно использовать одну из следующих, идентичных по результату конструкций (второй вариант более предпочтителен):

if GPIO.input(21):
    ... ваш код
if GPIO.input(21) == GPIO.HIGH:
    ... ваш код

А теперь установим высокий уровень на 7-м канале, который сконфигурирован на вывод (output):

GPIO.output(7, GPIO.HIGH)

Установку одного и того же уровня на множестве каналов  можно выполнить с использованием списка:

GPIO.output([7, 21, 8], GPIO.HIGH)

Или так (используя список, вынесенный в отдельный объект):

leds_rgb = [7, 21, 8]
GPIO.output(leds_rgb, GPIO.HIGH)

Сброс (очистка) состояний каналов

По завершению работы программы, в которой были установлены значения каналов GPIO, желательно выполнить сброс состояний каналов к значениям по умолчанию. Выполнить очистку (cleanup) состояний пинов можно следующим методом:

GPIO.cleanup()

Данная команда выполнит сброс всех использованных в программе (скрипте) каналов в режим по умолчанию - Input (Вход). Важно заметить, что также будет сброшен текущий режим нумерации пинов и будут отключены все подтягивающие резисторы (Pull-UP, Pull-Down) для каналов, которые были использованы в программе. Очистку состояний каналов желательно выполнять по завершению работы каждой программы.

Для очистки состояния одного или нескольких указанных каналов можно воспользоваться следующими вариантами команды:

GPIO.cleanup(7)
GPIO.cleanup([7, 21])

В процессе работы скрипта при выполнении какого-то блока программы можно наткнуться на исключение (Exception). В случае его возникновения, выполнение скрипта прервется и соответственно очистка состояний пинов выполнена не будет, программа не дойдет до завершающих операторов и в конечном итоге к вызову "cleanup". В такой программе нужно учитывать возможность возникновения исключений и постараться отловить их с помощью операторов "try + except".

Допустим что выполнение программы может быть прервано нажатием сочетания клавиш CTRL+C на клавиатуре, в таком случае для уверенного сброса состояний каналов GPIO можно использовать следующую конструкцию:

try:
    #... какой-то код программы
except KeyboardInterrupt:
    GPIO.cleanup()

# Конец программы
GPIO.cleanup()

Чтение состояния канала (пример с кнопкой)

В этом эксперименте мы будем проверять состояние кнопки в бесконечном цикле, нажимать ее и наблюдать за изменением вывода программы. Для этого примера подключим кнопку к каналу IO12, в Raspberry Pi 3 model B ему соответствует вывод (пин) номер 32.

Также, для безопасного подключения, используем два внешних резистора, как на схеме (рисунок 6) из моей первой статьи про GPIO в Raspberry Pi.

Схема соединений для эксперимента с чтением состояния GPIO канала, к которому подключена кнопка

Рис. 3. Схема соединений для эксперимента с чтением состояния GPIO канала, к которому подключена кнопка.

Откроем для редактирования будущий файл скрипта:

nano ~/rpi-gpio-input-switch-test.py

Скопируем в редактор следующий исходный код:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 
# Raspberry Pi RPi.GPIO test: input mode + switch
# https://ph0en1x.net

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM) 
GPIO.setup(12, GPIO.IN)

try:
    while True:
        if GPIO.input(12):
            print('IO12 = HIGH')
        else:
            print('IO12 = LOW')
        sleep(0.1)
except KeyboardInterrupt:
    GPIO.cleanup()

GPIO.cleanup()

Для выхода из редактора "nano" в GNU/Linux нужно нажать сочетание клавиш CTRL+X и потом подтвердить сохранение изменений в файле клавишей "Y".

Запускаем скрипт:

python3 ~/rpi-gpio-input-switch-test.py

В консоли начнут появляться строчки "IO12 = HIGH", с частотой 10 раз в секунду (с задержкой 0,1с). При нажатии кнопки появится строчка "IO12 = LOW", которая свидетельствует о том, что на канале IO12 появился низкий уровень.

Для выхода из программы достаточно прервать ее выполнение комбинацией клавиш CTRL+C.

Если активировать внутренний Pull-UP резистор для канала IO12, то резистор с сопротивлением 10К можно исключить из схемы. Для активации внутреннего подтягивающего к высокому уровню резистора нужно изменить строчку "GPIO.setup(12, GPIO.IN)" следующим образом:

GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_UP)

Установка состояния канала (пример со светодиодом)

Суть эксперимента - заставить светодиод мигать, поочередно меняя состояния напряжения на пине (0В и 3,3В), который закреплен за каналом IO12. Подключение светодиода выполним через гасящий резистор сопротивлением 330 Ом (можно также использовать 470 Ом, 620 Ом).

 gpio-on-raspberry-pi-output-test-circuit

Рис. 4. Подключение светодиода к каналу GPIO12.

Готовим новый файл для скрипта:

nano ~/rpi-gpio-output-switch-test.py

Код программы:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 
# Raspberry Pi RPi.GPIO test: output mode + LED
# https://ph0en1x.net

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.OUT)

try:
    while True:
        GPIO.output(12, GPIO.HIGH)
        sleep(0.1)
        GPIO.output(12, GPIO.LOW)
        sleep(0.1)
except KeyboardInterrupt:
    GPIO.cleanup()

GPIO.cleanup()

Запускаем программу:

python3 ~/rpi-gpio-output-switch-test.py

Светодиод должен замигать. Частоту его мигания можно изменить, указав другое время задержки (в секундах) в командах "sleep(0.1)".

Прерывания - что это такое и зачем они нужны

Из примера с кнопкой, который был показан выше, видно что программа ожидает изменения состояния на пине, при этом постоянно и с некоторой задержкой по времени считывая значение состояния его канала командой "input".

Такой метод контроля состояния объекта еще часто называют "polling" (в переводе с английского "опрос"), по сути это процесс продолжительной проверки чего-то, в данном случае - постоянная проверка состояния канала, к которому подключена кнопка.

Это далеко не лучший метод контроля событий на пинах GPIO, он подходит для несложных программ, где зачастую выполняются 1-2 какие-то простые операции, в которых нет конкуренции за приоритет исполнения.

Дело в том, что на поллинг тратятся ресурсы микропроцессора (CPU), также теряется ценное время, которое можно использовать на обработку других задач и событий в программе.

К тому же, кратковременное нажатие кнопки может быть проигнорировано, если этот момент совпадет по времени с выполняющейся между опросами состояния канала командой задержки "sleep". Можно конечно уменьшить таймаут между запросами, но это в свою очередь увеличит количество вызовов команды "input" и соответственно потребует дополнительных ресурсов CPU.

Зачем выполнять сотни и тысячи запросов в ожидании нажатия кнопки, теряя при этом время на задержки между запросами? Как и чем заменить поллинг? - здесь на помощь приходят прерывания.

Прерывание (Interrupt) - это специальный аппаратный или программный сигнал, который позволяет быстро и достаточно точно обнаружить момент наступления какого-то события. При этом затраты ресурсов системы минимальны.

При обработке синхронного прерывания, программа приостанавливает свое выполнение и переключается на обработку программного кода для связанного события, а после этого продолжает свое выполнение с места где была остановлена.

При обработке асинхронного прерывания, реакция на событие и выполнение какого-то полезного программного кода выполняется в отдельном потоке, без приостановки выполнения основной программы, при этом обмениваясь данными с основной программой.

Использование прерываний для GPIO - это способ ожидания и реакции на изменение состояния канала, без необходимости постоянной и многократной проверки его состояния.

Механизм прерываний был добавлен в библиотеку RPi.GPIO начиная с версии 0.5.0а, появились новые доступные функции:

  • add_event_detect();
  • remove_event_detect();
  • add_event_callback();
  • wait_for_edge().

Каждый пин GPIO в Raspberry Pi, установленный в режиме входа (input), может сконфигурирован как источник для вызова прерывания.

Теперь перейдем описанию и примерам использования прерываний для GPIO в Raspberry Pi с применением модуля RPi.GPIO.

Обнаружение порога срабатывания (Rising/falling edge)

Данный метод основан на использовании низкозатратной по ресурсам функции, которая ожидает смены уровня сигнала на канале, применяя для этого систему прерываний. В примере будет использован специальный метод модуля "RPi.GPIO" - "wait_for_edge".

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

Для этого примера используем ту же самую схему подключения кнопки, которая показана на рисунке 3.

nano ~/rpi-gpio-interrupt-edge-test.py

Код программы:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 
# Raspberry Pi RPi.GPIO test: interrupt wait-for-edge
# https://ph0en1x.net

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.IN)

try:
    print('Waiting for IO12 state cahnges ...')
    GPIO.wait_for_edge(12, GPIO.FALLING)  
    print('State change detected.')
except KeyboardInterrupt:
    GPIO.cleanup()

GPIO.cleanup()

 Запуск скрипта:

python3 ~/rpi-gpio-interrupt-edge-test.py

Скрипт выведет на экран сообщение об ожидании изменения состояния IO12. Как только кнопка, подключенная к этому каналу, будет нажата - на экране появится сообщение о том, что зафиксировано изменение состояния канала - "state change detected".

Waiting for IO12 state cahnges...
State change detected.

В этом примере мы ожидаем наступления события по смене уровня сигнала с высокого на низкий - "FALLING" (с англ. - "падение"). Если нужно отслеживать событие со сменой уровня с низкого на высокий (Rising, возрастание), то нужно изменить вызов метода "GPIO.wait_for_edge" вот так:

GPIO.wait_for_edge(12, GPIO.RISING)

В таком случае, прерывание будет активировано если на пине, который соответствует каналу 12, напряжение сменится с низкого уровня на высокий. В примере с кнопкой - это будет момент ее отпускания.

В данном случае приведен пример использования синхронного прерывания. Согласитесь, уже лучше чем поллинг, но вот если бы еще избавиться от блокировки программы то было бы вообще отлично. Здесь на помощь приходит многопоточность - асинхронная обработка прерывания в отдельном потоке.

Мультипоточный обратный отклик (threaded callback)

Threaded callback (обратная связь в потоке) - специальная функция, которая выполняется в отдельном потоке и следит за наступлением ожидаемого события.

Эта функция запускается основной программой (первый поток), с помощью этой функции осуществляется обратная связь (callback) и обмен данными между двумя потоками, причем работа программы в первом потоке не блокируется в момент выполнения функции во втором.

Для демонстрации здесь также будет использована схема подключения кнопки, приведенная на рисунке 3.

nano ~/rpi-gpio-interrupt-threaded-callback.py

Программный код:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 
# Raspberry Pi RPi.GPIO test: interrupt threaded-callback
# https://ph0en1x.net

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.IN)

def test_callback(channel):
    print('Event detected.')

print('Waiting for IO12 state cahnges ...')
GPIO.add_event_detect(12, GPIO.FALLING, callback=test_callback)

try:
    while True:
        sleep(2)
        print('.')
except KeyboardInterrupt:
    GPIO.cleanup()

GPIO.cleanup()

Запуск:

python3 ~/rpi-gpio-interrupt-threaded-callback.py

Рассмотрим код программы. После установки канала 12 на вход, создается небольшая функция, при вызове которой в консоль будет выведено сообщение "Event detected" (событие наступило).

С помощью метода "GPIO.add_event_detect" мы как бы приставляем к каналу 12 сторожевого песика (watchdog) и даем ему следующую инструкцию: "если заметишь смену уровня сигнала с высокого на низкий (FALLING) на этом канале - дай об этом знать, запусти функцию указанную в параметре callback - test_callback".

Дальше, мы оставляем песика наедине с его работой (поток 2) и идем заниматься своими делами (поток 1) - будем в бесконечном цикле "while True" через каждые 2 секунды выводить на консоль символ точки ".".

Заметьте, что поток 2 создается методом "add_event_detect", вызванным из потока 1, где мы перед выполнением своей работы (вывод символа точки) выполняем вызов этого метода.

Запустив программу, попробуйте понажимать на кнопочку - сможете наблюдать примерно следующую картину:

.
Event detected.
.
Event detected.
Event detected.
Event detected.
.
.
Event detected.
.
Event detected.
Event detected.
Event detected.
Event detected.
.
Event detected.
Event detected

Как видите, в момент ожидания (2 секунды) между выводом символа точки появляются сообщения о фиксации события "Event detected", при этом "точки" как выводились через две секунды, так и продолжают это делать без какой-либо блокировки выполнения.

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

Теперь, давайте сделаем так, чтобы "песик" посторожил некоторое время, а потом ушел со своего поста. По сути, нам нужно с некоторой задержкой прекратить обработку прерывания на канале 12, для этого будем использовать метод "GPIO.remove_event_detect" с указанием нужного канала.

Откроем новый файл:

nano ~/rpi-gpio-interrupt-threaded-callback-remove.py

Исходный код программы для этого примера:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 
# Raspberry Pi RPi.GPIO test: interrupt threaded-callback-rm
# https://ph0en1x.net

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.IN)

def test_callback(channel): 
    print('Event detected on IO=' + str(channel))

print('Waiting for IO12 state cahnges ...')
GPIO.add_event_detect(12, GPIO.FALLING, callback=test_callback)

try:
    for i in range(0,10):
        sleep(2)
        print('.')
        if i == 3:
            GPIO.remove_event_detect(12)
except KeyboardInterrupt:
    GPIO.cleanup()

GPIO.cleanup()

 Запуск скрипта на исполнение:

python3 ~/rpi-gpio-interrupt-threaded-callback-remove.py

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

  • Функция "test_callback" выводит сообщение с номером канала, для которого она была вызвана;
  • Выводим символ точки с задержкой 2 секунды в цикле ровно 10 раз;
  • После вывода трех точек отключаем обработку раньше установленного прерывания на канале 12.

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

.
.
Event detected on IO=12
Event detected on IO=12
.
.
.

К каждому обработчику прерывания на определенном канале можно подвязать не одну, а несколько функций обратной связи. Например, при нажатии кнопки будут вызываться две функции обратной связи: первая - выведет сообщение что событие наступило, вторая - изменит значение глобальной переменной.

Для реализации подобного сценария нужно использовать связку из методов "add_event_detect" + "add_event_callback".

nano ~/rpi-gpio-interrupt-threaded-callback-2-func.py

Исходный код для примера:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.IN)

x = 0

def callback_func_1(channel):
    print('Interupt on ' + str(channel))

def callback_func_2(channel):
    global x 
    x = x + 1

GPIO.add_event_detect(12, GPIO.FALLING)
GPIO.add_event_callback(12, callback_func_1)
GPIO.add_event_callback(12, callback_func_2)

try:
    while True:
        sleep(1)
        print(str(x))
except KeyboardInterrupt:
    GPIO.cleanup()

GPIO.cleanup()

 Запуск:

python3 ~/rpi-gpio-interrupt-threaded-callback-2-func.py

В начале программы объявляем переменную х, а также две новые функции, одна из которых печатает на экран сообщение "Interrupt on IO=X" (где Х-номер канала), а вторая - изменяет ранее объявленную глобальную переменную "x".

Методом "add_event_detect" запускаем механизм работы с прерываниями на канале 12, а с помощью методов "add_event_callback" выполняется привязка ранее объявленных функций к прерыванию на том же указанном канале.

Пример вывода программы:

0
Interupt on IO=12
Interupt on IO=12
2
2
Interupt on IO=12
Interupt on IO=12
4
Interupt on IO=12
Interupt on IO=12
Interupt on IO=12
Interupt on IO=12
Interupt on IO=12
Interupt on IO=12
10
Interupt on IO=12
Interupt on IO=12
12
12
Interupt on IO=12
Interupt on IO=12
14

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

Здесь можно еще более четко увидеть проблему "дребезга контактов", когда одно нажатие порождает хаотично изменяющееся число ложных срабатываний. Пора уже разобраться с этой проблемой...

Что такое "дребезг контактов" и как его перебороть

Дребезг контактов (switch bounce) - это процесс возникновения многократных, кратковременных неконтролируемых замыканий/размыканий в момент соединения контактов кнопок, переключателей и других механических коммутационных устройств.

Причина дребезга контактов - упругость материалов, в следствии чего случается кратковременное отталкивание одного из контактов в момент соприкосновения с другим. Как пример подобного явления можно привести попытку быстро столкнуть вместе два металлических шара.

Способы борьбы с дребезгом контактов:

  • использование RS-триггера;
  • программная задержка;
  • установка RC-цепочки;
  • установка RC-цепочки + программная задержка.

Применение RS-триггера (Reset-Set trigger)  при борьбе с дребезгом контактов целесообразно если используется кнопка с тремя контактами. Здесь же рассмотрим использование программной задержки по времени, а также попробуем установить дополнительный керамический конденсатор к уже имеющейся схеме с кнопкой и резисторами.

Схема подключения кнопки к GPIO (нажатие кнопки - смена уровня с высокого на низкий) с дополнительным конденсатором для подавления дребезга контактов

Рис. 5. Схема подключения кнопки к GPIO (нажатие кнопки - смена уровня с высокого на низкий) с дополнительным конденсатором для подавления дребезга контактов.

Схема подключения кнопки к GPIO (нажатие кнопки - смена уровня с низкого на высокий) с дополнительным конденсатором для подавления дребезга контактов

Рис. 6. Схема подключения кнопки к GPIO (нажатие кнопки - смена уровня с низкого на высокий) с дополнительным конденсатором для подавления дребезга контактов.

Думаю вы заметили что на схеме (рисунок 6) резистор R1 теперь установлен на сопротивление 10К, а не на 1К как на рисунке 5 (хотя и там можно установить на 10К и все будет работать). Это сделано не ошибочно. Дело в том, что нам нужно стараться не превышать рекомендуемый возможный уровень тока, протекающего через сконфигурированный в режиме входа (INPUT) пин - 0.5мА.

С разомкнутыми контактами кнопки (рисунок 6) через пин не протекает ток, поскольку вывод GPIO через резисторы R1+R2 подключен к земле - GND. Когда контакты кнопки замкнуты, напряжение +3.3В начинает поступать на пин через резистор R1, возможный максимальный ток в цепи будет ограничен этим резистором (по закону Ома):

I = U/R = 3.3V/10000Ohm = 0.33mA.

Также в момент нажатия на кнопку ток потечет и через резистор R2, который в отжатом состоянии кнопки подключает пин к GND.

Теперь перейдем к практическому примеру.

Если рассматривать пример из рисунка 3, то керамический конденсатор емкостью 100 нФ (0,1мкФ) нужно установить между входом GPIO и GND (земля, минус). Соберем схему соединений по рисунку 5 (нажатие кнопки - смена уровня с высокого на низкий) и подключим ее к GPIO.

Схема подключения конденсатора к GPIO для подавления дребезга контактов в кнопке

Рис. 7. Схема подключения конденсатора к GPIO для подавления дребезга контактов в кнопке.

При вызове метода "add_event_detect" дополнительно укажем ему параметр "bouncetime", которому присвоим числовое значение в миллисекундах. Теперь, при изменении уровня на канале 12, на протяжении указанного отрезка времени будет выполняться подавление дребезга контактов.

nano ~/rpi-gpio-interrupt-threaded-callback-bouncetime.py

Код скрипта:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 
# Raspberry Pi RPi.GPIO test: interrupt threaded-callback + bouncetime
# https://ph0en1x.net

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.IN)

def test_callback(channel):
    print('Event detected.')

print('Waiting for IO12 state cahnges ...')
GPIO.add_event_detect(12, GPIO.FALLING, callback=test_callback, bouncetime=300)

try:
    while True:
        sleep(2)
        print('.')
except KeyboardInterrupt:
    GPIO.cleanup()

GPIO.cleanup()

Старт:

python3 ~/rpi-gpio-interrupt-threaded-callback-bouncetime.py

Теперь, кратковременное нажатие на кнопку будет иметь уверенную реакцию. В примере вывода, что ниже, кнопка была нажата 3 раза на протяжении 2х секунд, потом два раза, а после этого еще по разу.

Waiting for IO12 state cahnges ...
.
.
Event detected.
Event detected.
Event detected.
.
Event detected.
Event detected.
.
Event detected.
.
Event detected.
.

Широтно-импульсная модуляция (PWM)

Широтно-импульсная модуляция (ШИМ) или PWM (Pulse-Width Modulation) -  способ управления подводимой к нагрузке мощностью, при котором питание подается импульсами с постоянной частотой, но при этом с переменной длительностью этих импульсов.

Для ШИМ характерны такие величины как "скважность" и "коэффициент заполнения".

  • Скважность - отношение периода импульса к его длительности.
  • Коэффициент заполнения (Duty Cycle) - обратная к скважности величина.

Данные величины являются безразмерными и очень часто при использовании обозначаются в процентах (%).

Примеры различного коэффициент заполнения для импульсов с одинаковой частотой

Рис. 8. Примеры различного коэффициента заполнения для импульсов с одинаковой частотой.

Диаграмма прямоугольного сигнала, которая изображена по середине рисунка (Duty Cycle = 50% = 0,5), в электронике имеет специальное название - "меандр".

Метод ШИМ очень часто применяется в импульсных преобразователях напряжения, автомобильных и сварочных инверторах напряжения, в управлении скоростью электродвигателей, яркостью свечения мощных светодиодов и т.п.

Например, ШИМ используется в системах подсветки дисплеев LED-телевизоров, ноутбуков и смартфонов - частота импульсов при подаче питания на светодиоды достаточно большая, поэтому человеческий глаз не видит мерцания, в зависимости от длительности этих импульсов свечение делается темнее или ярче.

Для реализации ШИМ в модуле RPi.GPIO существует специальный метод "GPIO.PWM", с помощью которого можно создать управляемый объект для генерации ШИМ-импульсов на выбранном канале GPIO.

# Создаем объект 'pwm' для работы с PWM на 7-м канале,
# частота импульсов - 100Гц.
pwm = GPIO.PWM(7, 100)

# Запускаем генерацию импульсов на канале 
# с начальным коэффициентом заполнения 50%.
pwm.start(50)

# Изменяем частоту импульсов на 150Гц.
pwm.ChangeFrequency(150)

# Изменяем коэффициент заполнения на 90%.
pwm.ChangeDutyCycle(90)

# Останавливаем генерацию импульсов на канале.
pwm.stop()

Чтобы поэкспериментировать с PWM в Raspberry Pi вы можете собрать схему со светодиодом, подключенным к одному из выводов GPIO - например для 12-го канала, как на рисунке 4.

Откроем файл для создания нового скрипта:

nano ~/rpi-gpio-pwm-test.py

Исходный код скрипта:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 
# Raspberry Pi RPi.GPIO test: PWM with LED
# https://ph0en1x.net

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.OUT)

pwm = GPIO.PWM(12, 50)
pwm.start(0)

try:
    pwm.ChangeDutyCycle(50)
    input('F=50Hz, DC=50%. Press Enter...')
    pwm.ChangeDutyCycle(20)
    input('F=50Hz, DC=20%. Press Enter...')
    pwm.ChangeFrequency(10)
    pwm.ChangeDutyCycle(80)
    input('F=10Hz, DC=80%. Press Enter...')
    pwm.ChangeDutyCycle(10)
    input('F=10Hz, DC=10%. Press Enter to exit...')
except KeyboardInterrupt:
    pass

pwm.stop()
GPIO.cleanup()

Запускаем программу:

python3 ~/rpi-gpio-pwm-test.py

После запуска программы светодиод, подключенный к каналу IO12, начнет мигать с частотой (F) - 50Гц. Если смотреть на этот светодиод и потом медленно переместить поле зрения на какой-то объект, находящийся не далеко от светодиода, то вы возможно заметите небольшое мелькание.

Нажав "ENTER" яркость светодиода понизится (DC=20%), а частота (F) останется такой же как и раньше. При последующем нажатии этой же клавиши, будет изменена частота (на 10Гц) и коэффициент заполненности (на 80%) - светодиод начнет заметно мигать, при этом яркость его свечения будет увеличена.

Еще раз нажав "ENTER" мы уменьшим коэффициент заполненности до 10%, яркость свечения резко упадет. Ну и последнее нажатие этой же клавиши завершит выполнение программы - светодиод погаснет (канал IO12 будет сконфигурирован на вход благодаря "GPIO.cleanup").

Вместо светодиода можно подключить собранный на транзисторе мощный электронный ключ или же H-мост для управления электродвигателем постоянного тока. Таким образом можно будет управлять скоростью вращения двигателей, которые приводят в движение какой-то объект или систему, например - самодельного робота и т.п.

Узнаем текущий установленный режим для GPIO каналов

Может случиться, что в процессе работы программы конфигурация каналов GPIO будет динамически меняться - какой-то из пинов сначала будет задействован как вход, потом как выход, а потом может и вовсе как один из выводов шины I2С (SCL или SDA).

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

Для демонстрации я использовал свой самодельный роутер на базе Raspberry Pi, в котором задействованы пины шины I2C. Запустил на нем в консоли интерпретатор python3, выполнил следующие команды и получил соответствующие им результаты:

>>> import RPi.GPIO as GPIO
>>> GPIO.setmode(GPIO.BOARD)
>>> GPIO.gpio_function(3)
42
>>> GPIO.gpio_function(5)
42
>>> GPIO.I2C
42
>>> GPIO.gpio_function(40)
1

Перед вызовами метода "gpio_function" здесь произведена инициализация нумерации пинов по номеру на гребенке платы (GPIO.BOARD).

Как видим, переданные методу "gpio_function" номера пинов 3 и 5 заставили его вернуть число 42. Если попробовать отыскать соответствующую этому числу константу из модуля "RPi.GPIO", то увидим что это "GPIO.I2C". Из этого можно понять, что данные пины задействованы для шины I2C.

Для пина с номером 40 метод вернул число 1, которое соответствует константе модуля "GPIO.IN", а это в свою очередь означает что пин установлен в режиме на вход (input).

Список некоторых констант модуля "RPi.GPIO" и их значения:

  • GPIO.IN = 1
  • GPIO.OUT = 0
  • GPIO.SPI = 41
  • GPIO.I2C = 42
  • GPIO.HARD_PWM = 43
  • GPIO.SERIAL = 40
  • GPIO.UNKNOWN = -1.

Полученное от метода значение можно присвоить переменной, пример для пина 5:

pin_config = GPIO.gpio_function(5)

Аналогичным способом можно проверить конфигурацию пина на соответствие какому-то из значений в константах, например проверим пин номер 24 на предмет участия его в интерфейсе SPI:

if GPIO.gpio_function(24) == GPIO.SPI:
    print('Used for SPI interface.')

В завершение 

Как видите, нет ничего сложно в использовании модуля RPi.GPIO - все очень просто и удобно. Теперь вы с уверенностью и пониманием сможете строить самые разные проекты на основе Raspberry Pi, в которых используются выводы GPIO. С помощью модуля "RPi.GPIO" и не сложной программы можно генерировать различные по параметрам сигналы, а также выполнять анализ сигналов на любом из доступных каналов GPIO.

Полезные ссылки:

Если статья оказалась полезной - помочь проекту можно тут: 👍 ПОМОЩЬ, 🎁 DONATE
14 50180 Железо
Комментарии к публикации (17):
Дмитрий #1Дмитрий
10 Сентябрь 2019 19:23

Здравствуйте, подскажите пожалуйста, можно ли вместо кнопки (рис.7) подключить фоточувствительный элемент (фотодиод /транзистор /резистор) чтобы малина реагировала на изменение освещения?
Вообще идея в том, чтобы малинка входила / выходила из спящего режима при выключении/включении света в комнате.
Я, после прочтения ваших статей, не вижу проблемы в том, чтобы заменить кнопку фотодиодом(может плохо читал Smile) вот только непонятно хватит ли одного диода, чтобы уловить изменение освещения в комнате и как настроить его чувствительность.
Если вам попадалась статья, где такой функционал был реализован, буду очень признателен, если вы ей поделитесь.

+4
Денис #2Денис
11 Сентябрь 2019 19:30

Хорошая статья, в рунете их всего-ничего!
Автор продолжайте, хочется еще на пальцах узнать как это дело интегрировать с НА.

+2
ph0en1x #3ph0en1x
11 Сентябрь 2019 21:10

Всем привет!

Денис, спасибо за отзыв.

Дмитрий, в малинке нет встроенного АЦП (Аналогово-Цифровой Преобразователь) и ЦАП (Цифро-Аналоговый Преобразлователь), подключенного к GPIO, поэтому без дополнительных схем с электроникой не удастся контролировать изменение сопротивления фоторезистора, напряжения батареи или аккумулятора и т.п.

На входе и выходе GPIO может быть лишь два состояния: высокий (логическая 1) и низкий (логический 0) уровни. Порог напряжения для высокого уровня у GPIO малинки - приблизительно 1,8В. Соответственно низкий уровень - это 0В.

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

Схема делителя напряжения:

Схема эксперимента - подключение фоторезистора к GPIO raspebrry Pi

На входе мы имеем 3,3В, на выходе нужно 1,8В, также имеем сопротивление фото-резистора, останется рассчитать и подобрать сопротивление R1 (на схеме рассчитано под R1=20K). Раньше я уже писал о расчете делителя напряжения на резисторах. Для более точной подстройки в качестве R2 можно применить переменный резистор, для контроля напряжения на выходе делителя используйте мультиметр.

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

К тому же, на пороге срабатывания кратковременно будут наблюдаться множественные срабатывания по причине отсутствия гистерезиса - появление уровней 1 и 0 в хаотичном порядке. Поэтому использование прерываний здесь может привести к неожиданным результатам. В основной программе нужно будет периодически с некоторой задержкой проверять состояние канала GPIO, без использования прерываний.

Увеличить точность срабатывания можно, например, собрав компаратор на операционном усилителе, который умеет работать от напряжения питания +3,3В.

Для контроля освещенности фото-электронных компонентов, наблюдением за изменением напряжений в цепях лучше применить ADC-DAC PCF8591 - это микросхема или модуль, подключаемый к малинке по двухпроводной шине I2C. В одной из статей я подробно описал как работать с PCF8591 и шиной I2C. PCF8591 содержит 4 входа на которых можно наблюдать за изменением напряжения, а также один выход на котором можно изменять уровень выдаваемого напряжения.

+2
Gecxjo #4Gecxjo
14 Ноябрь 2019 06:16

СпасиБО! Попробовал поработать с прерываниями - опс! Функция не распознана. А из Вашей статьи мне стало ясно, что похоже пример взял из старой реализации RPIO, да к тому же для питона вер.2 ;-)
Ну что ж, первый блинчик комком.
А с Вашей подробнейшей статьёй постараюсь разобраться.
(уже "сваял" внешнюю платку с кучей релюшек с PCF8574, shifter 3,35V, осталось с программированием разобраться. Pyton для меня достаточно новый, долго программил на Фортране ;-) )

+1
Gecxjo #5Gecxjo
14 Ноябрь 2019 06:22

ph0en1x -- всё верно, но для начала можно попробовать простенькую схемку. А затем - 554СА3 (разумеется, внимательно с питаниями, т.к. на пины малины более 3,3В -- НИ-НИ! - я по крайней мере, к сему стараюсь вдумчиво. Даже шифтер 3--5В на i2c сваял, мелкосхема стоит очень недорого, только паять тяжковато)

0
ph0en1x #6ph0en1x
14 Ноябрь 2019 11:45

Gecxjo, пожалуйста!
В версиях языка Python сейчас есть два основных разветвления с отличиями (разнятся некоторые функции, в каждой ветке свой набор библиотек) - 2.х (python, python 2) и 3.х (python3). Поэтому при установке каких-то пакетов через pip, pip3 или apt-get нужно быть внимательным и проверять доступен ли пакет в нужной версии питона.

Pyton для меня достаточно новый, долго программил на Фортране ;-) )

Думаю вам понравится Pyton с его дружелюбным синтаксисом и возможностями.

Даже шифтер 3--5В на i2c сваял, мелкосхема

Можно на микросхеме, а можно на двух транзисторах. Эту тему я рассматривал в статье о шине I2C в Raspberry Pi и ADC-DAC PCF8591, в разделе "Подключение по I2C нескольких устройств с разным напряжением питания". Пример можно увидеть в статье о самодельном роутере на основе малинки.

мелкосхема стоит очень недорого, только паять тяжковато)

Если проблема в толстом жале паяльника, то это можно исправить покупкой тонкого жала или сделать временное решение: намотать на жало паяльника медного проводника диаметром 1-2мм, очистив его перед этим от изоляции, а потом заточить конец этого проводника напильником - вот вам и маленькое жало для пайки разных мелких деталей.

0
Димаn #7Димаn
23 Ноябрь 2019 20:25

Спасибо, хорошая статья! Good

+3
Jarek #8Jarek
26 Декабрь 2019 15:59

Не очень понятно как подключать и как работает подтягивающий резистор: как только мы его включаем через функцию GPIO.PUD_UP на нем автоматически появляется напряжение или нужно подавать с пина +5В или +3,3В?
Что считается высоким уровнем: любой уроaвень выше 0В ?

0
ph0en1x #9ph0en1x
27 Декабрь 2019 00:23

"Подтягивающие" резисторы (рисунок 2, Ra и Rb), уже находятся внутри микросхемы, управление прохождением тока через них выполняется внутренними электронными ключами (транзисторами, на рисунке 2 они упрощенно обозначены как кнопки), которые открываются/закрываются логическими сигналами с регистров микроконтроллера (программно).

В функции установки режима работы порта "GPIO.setup" можно дополнительно указать к какой из линий питания (0 или +3.3 Вольта) подключить вывод GPIO через соответствующий резистор:

  • pull_up_down=GPIO.PUD_UP - пин GPIO через внутренний резистор будет подключен к плюсу питания - 3.3В;
  • pull_up_down=GPIO.PUD_DOWN - пин GPIO через внутренний резистор будет подключен к общему - 0.

Пин GPIO, сконфигурированный на вход (IN) но без активации внутренних подтягивающих резисторов, нужно принудительно "подтянуть" к какой-то из линий питания, используя для этого внешнее сопротивление (резистор).

Например, на рисунке 5 пин GPIO подтянут к линии 3.3В резисторами R1+R2 (через них течет ток), замыкание контактов кнопки обрывает прохождение тока, поступающего с R2 на R1 и спускает его в землю GND. Таким образом, в любом из положений кнопки уровни напряжения на пине четко определены.

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

Использовать внутренние подтягивающие резисторы или внешние - каждый решает сам при проектировании схемы и программы для своего устройства.

Какой уровень напряжения на пине GPIO Raspberry Pi в режиме входа (IN, INPUT) считается высоким (логическая 1)? - примерно от 1.8 и до 3.3 (Вольта). Нижнее значение напряжения не является крайним, поскольку еще установлен гистерезис. Об этом рассказано в моей первой статье - "Изучаем GPIO в Raspberry Pi", также там приведены другие правила и технические нюансы использования пинов малинки.

+1
Вл #10Вл
03 Август 2021 11:49

Всем привет. А подскажите, защитные (антизвонные) диоды (вверх/вниз) на выходах GPIO имеются?

0
ph0en1x #11ph0en1x
03 Август 2021 16:13

Здравствуйте. Нигде не встречал такой информации. В даташите на ARM микропроцессор BCM2835 также нет указаний на то, что выводы GPIO защищены диодами.

Удалось найти статью, в которой сделана попытка собрать информацию об электрических параметрах GPIO для "малинки". Там для примера показана эквивалентная схема пинов GPIO Raspberry Pi:

эквивалентная схема пинов GPIO Raspberry Pi

Под ней написано что: показанные на схеме входные диоды на самом деле являются "паразитными", представленными как процессы в переходах MOSFET транзисторов (подключенных к пину внутри чипа), они не способны выдержать какой либо значительный ток.

Поэтому я здесь на схеме и обозначил подключение этих диодов пунктирными линиями - чтобы это вызывало вопрос и кто-то не подумал что присутствуют реальные полноценные диоды.

+1
Вл #12Вл
22 Сентябрь 2021 14:48

Наверное в питоне3 не работает функция подавления дребезга bouncetime=300. Ставлю 300мСек, а импульс от кнопки на осциллографе контролирую. Его длина - 100 мСек. Но событие (falling) все равно происходит.

0
Рафа #13Рафа
24 Январь 2022 09:29

Добрый день! Что то я совсем запутался) Помогите пожалуйста. Нужно подключить к малине пять сухих контактов, тобишь пять кнопок. Пины 25, 28 29, это GPIO 26, 20 и 21, если не ошибаюсь, заняты релейным модулем. Куда можно подцепить пять кнопок с минимальным обвесом?

0
ph0en1x #14ph0en1x
24 Январь 2022 12:52

Добрый день! Указанные вами пины и порты не соответствуют, похоже что где-то ошиблись или не разобрались.

Если использовать вариант безопасного подключения кнопки с двумя резисторами, то для трех кнопок схема будет выглядеть вот так:
/uploads/Image/comments/raspberry-pi-3-switches-connection-gpio.png
Она была приведена в комментариях к статье Изучаем GPIO в Raspberry Pi, эксперимент со светодиодом и кнопкой.

Нумерация выводов GPIO будет указана для Raspberry Pi A+, B+, 2, 3. Тут для подключения кнопок использованы 5 выводов гребенки GPIO:

  • 3,3В - вывод (пин, pin) номер 1;
  • IO17 - 11-й;
  • IO27 - 13-й;
  • IO22 - 15-й;
  • GND - 20-й.

Чтобы подключить еще одну кнопку - собираете еще один сегмент схемы с кнопкой и двумя резисторами, подключаете его к 3,3В и GND. Оставшийся вывод резистора на 1К подключаете к любому свободному пину GPIO, который способен работать в режиме цифрового входа, например:

  • IO23 - 16-й;
  • IO24 - 18-й;
  • IO12 - 32-й;
  • IO16 - 36-й.

Если нужно еще кнопки - ищем свободный IO и собираем к нему еще одну схему с кнопкой и резисторами. А дальше уже дело за программированием.

+1
Рафа #15Рафа
25 Январь 2022 08:22

Добрый день! Да, эту схему я видел) Спасибо за ответ! С выводами тоже разобрался. Это пины 37, 38, 40. А то что я написал в начале это оказывается нумерация по классификации какого то ардуино подобного пакета.

+1
sciensys #16sciensys
11 Февраль 2022 21:50

Доброе. Огромное спасибо за материал!
планирую в ближайшее время что-то попробовать на базе Raspberry Pi4
Ранее не занимался проектированием цепей.

Возникло пару вопросов:
1. какие лучше использовать конденсаторы для схем на 3.3 Вольтах, в схемах, где есть управление силовыми нагрузками в 220В ?
есть конденсаторы разных типов и разного вольтажа, видел промышленные схемы на 220В, где используют конденсаторы 25В, где-то берут запас на 4-20 вольт и выше, где-то на 100-400 Вольт.
2. у резисторов дают ещё параметр W (Ватт), и он варьируется от 0.5 до 600W и возможно выше.

с какими параметрами конденсаторов и резисторов лучше использовать в Ваших схемах ?

Заранее благодарю.

0
ph0en1x #17ph0en1x
12 Февраль 2022 13:10

Здравствуйте!

В приведенных на рисунках схемах протекают совсем небольшие токи: от единиц до десятков миллиампер. Напряжение в этих цепях будет не более 3.3 Вольта (максимум на входах и выходах GPIO).

Чтобы разобраться используем известную формулу расчета мощности:

P = U * I

где:

  • P - мощность в Ваттах (Вт, W);
  • U - напряжение в Вольтах (В, V);
  • I - ток в Амперах (А).

Выполним расчет мощности для цепи с напряжением 3.3В и при токах 1мА (P1) и 20мА (P2):

P1 = 3.3V * 0.001A = 0.003 W
P2 = 3.3V * 0.02A = 0.066 W

Итак, имеем 3 милливатта (мВт, mW) и 66 милливатт.

В таких цепях можно применять резисторы практически любой мощности: 0.125Вт (125мВт), 0.25Вт (250 мВт), 0.5Вт (500 мВт)...и больше. Тут нет больших мощностей и нет надобности использовать мощные и громоздкие резисторы.

Насчет конденсаторов на схемах из статьи - керамические, слюдяные или любые другие миниатюрные. В данном случае можно не обращать внимания на напряжение, у нас оно небольшое - всего лишь 3.3В. Поэтому если нет миниатюрных но есть побольше (например на 160В), то их также можно использовать для экспериментов.

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

Изучение таких вещей лучше начать с каких-то книжечек на тему "электроника и радиоэлектроника для начинающих", а потом перейти к более специализированной литературе.

управление силовыми нагрузками в 220В

В таких схемах своя специфика применения различных компонентов, в этой статье это не рассматривается. Для управления нагрузками (с питанием 220В) через Raspberry Pi можно использовать готовые модули с электромагнитными или твердотельными реле. При необходимости схему электронного ключа для реле можно собрать на транзисторах самостоятельно.

видел промышленные схемы на 220В, где используют конденсаторы 25В

Опять же, специфика каждой отдельно взятой схемы. Например в каком то приборе на 220В может быть собрана схема стабилизатора напряжения для питания логической микросхемы от +12В, на выходе его установлен сглаживающий конденсатор на напряжение с запасом - 16В или 25В. Все делается из расчета и понимания того, как все будет работать.

Резюме: для экспериментов из данной статьи можете применять резисторы и конденсаторы на любую мощность и напряжение, какие будут в наличии.

0