GPIO-пины Raspberry Pi и их использование из Python

14 декабря 2016

В уже довольно не новом посте, посвященном Raspberry Pi, это устройство рассматривалось исключительно, как маленький и очень дешевый компьютер. Бесспорно, Raspberry Pi им и является. Но, помимо этого, у Raspberry Pi есть еще и 26 пинов GPIO (General Purpose Input Output), что очень кстати в свете моего недавнего увлечения электроникой. Почему? Давайте разберемся.

Отмечу, что все написанное ниже справедливо для Raspberry Pi 2 Model B. Если у вас другая малина, то расположение пинов и другие детали могут отличаться. Поэтому обязательно сверьтесь с официальной документацией. В качестве операционной системы я использовал релиз Raspbian от 2016-09-28, который можно скачать здесь.

Итак, расположение пинов описано на официальном сайте:

Номера GPIO-пинов Raspberry Pi

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

Допустим, мы хотим программно управлять напряжением, подаваемым на 2-й пин. Проще всего это сделать через sysfs.

Первым делом «экспортируем» пин, без этого шага им не получится управлять:

echo 2 > /sys/class/gpio/export

Делаем его out-пином, то есть, он будет либо подавать, либо не подавать напряжение в 3.3 вольта:

echo out > /sys/class/gpio/gpio2/direction

Подаем напряжение:

echo 1 > /sys/class/gpio/gpio2/value

Перестаем подавать напряжение:

echo 0 > /sys/class/gpio/gpio2/value

Узнаем, подается ли сейчас напряжение:

cat /sys/class/gpio/gpio2/value

По завершении работы пину можно сделать unexport:

echo 2 > /sys/class/gpio/unexport

Есть мнение, что 3.3 вольта — это как-то маловато. Кроме того, было бы неплохо не только включать и выключать напряжение, но и изменять его в некотором диапазоне. Увы, насколько мне известно, ничего этого Raspberry Pi не умеет. Поговаривают, что умеет Arduino, но опыта использования этого устройства на момент написания этих строк у меня нет.

Можете подключить пин к цепи из светодиода и резистора с сопротивлением 470 Ом и проверить, что описанным выше образом светодиод можно включать и выключать. Для земли (то есть, минуса) можно использовать любой пин, изображенный на приведенной выше схеме черным цветом. Понятно, что работу с sysfs можно автоматизировать на любом языке программирования по вкусу.

Важно! После загрузки Raspberry Pi некоторые пины по дэфолту могут подавать напряжение. Возможно, это не то, чего вы хотите для вашей цепи. У меня по дэфолту напряжение было на пинах 2, 3 и 14. Я бы советовал перепроверить эту информацию на вашей конкретной Raspberry Pi и конкретной версии Raspbian. Имейте также в виду, что написанная вами же программа может оставить пины в неизвестном состоянии (например, если вы прибьете ее kill’ом).

До сих пор были рассмотрены out-пины. Они как бы «пишут» на макетную плату, но не позволяют узнать ее текущее состояние. Например, нажата ли в настоящих момент какая-то кнопка. Даже если цепь разомкнута, out-пины об этом не знают. Поэтому есть еще и in-пины.

Экспортируем 5-ый пин и делаем его in-пином:

echo 5 > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio5/direction

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

«Прочитать» кнопку можно так:

cat /sys/class/gpio/gpio5/value

Считывается 1, если кнопка не нажата, то есть, цепь разомкнута, и 0, если кнопка нажата, то есть, цепь замкнута.

В итоге получилась такая конструкция (иллюстрация про кнопку взята отсюда):

Raspberry Pi, пример использования GPIO

А вот и скрипт на Python, который при нажатии на кнопку тушит текущий светодиод и зажигает следующий за ним:

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

# Use "logical" pin numbers
GPIO.setmode(GPIO.BCM)

# Disable "This channel is already in use" warnings
GPIO.setwarnings(False)

# Setup LED's: 2 - green, 3 - yellow, 4 - red
for i in range(2,5):
    GPIO.setup(i, GPIO.OUT)
    GPIO.output(i, False)

current_led = 2
GPIO.output(current_led, True)

# Prepare to read button state
BUTTON = 5
PRESSED_CODE = 0
GPIO.setup(BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)

while True:
#    GPIO.wait_for_edge(BUTTON, GPIO.FALLING)
#    print("Button pressed")
#    GPIO.wait_for_edge(BUTTON, GPIO.RISING)
#    print("Button released")
    time.sleep(0.05)
    if GPIO.input(BUTTON) == PRESSED_CODE:
        GPIO.output(current_led, False)
        current_led = max(2, (current_led + 1) % 5)
        GPIO.output(current_led, True)
        time.sleep(0.1)

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

Насколько мне известно, это по большому счету все, что можно сделать с помощью GPIO на Raspberry Pi.

Дополнение: Еще один пример использования GPIO в одноплатном компьютере описывает заметка Реверс-инжиниринг роутера на примере GL.iNet GL-AR750. Также вас может заинтересовать статья Модули ядра Linux: таймеры и GPIO.

Метки: , , .


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