Raspberry Pi. Работаем с GPIO на Python - Часть 1

Как-то я писал о том, что планирую использовать Raspberry Pi в качестве веб-сервера, управляющего моим устройством. Пришло время задуматься над интерфейсом подключения. У разработчика, желающего подключить свое железо к Raspberry есть два пути: использовать для этого USB или выводы общего назначения (GPIO). С возможностями USB все приблизительно понятно (в будущем постараюсь написать пост о работе с внешним железом через USB). О GPIO же информации не так много, да и опыта его использования нет. В общем я заинтересовался GPIO и его возможностями. В статье речь пойдет о выводах общего назначения Raspberry Pi - GPIO:
  • возможности GPIO
  • распиновка GPIO
  • как работать с GPIO на Python
  • характеристики GPIO (прежде всего показатели скорости)
  • пример использования GPIO Raspberry

Порт GPIO - выводы общего назначения Raspberry Pi


Предварительно я наладил удаленную работу с Raspberry Pi через SSH, чтобы каждый раз не подключать  монитор и клавиатуру. Поэтому в исходном состоянии у меня к Raspberry подключено всего два "провода": питание и сетевой кабель для соединения с роутером. Хотя принципиального значения это не имеет: все нижеописанное будет справедливо, как в случае работы с консолью Raspberry через SSH, так и при использовании подключенной клавиатуры.

Raspberry Pi GPIO
Порт GPIO (сокращение от General Purpose Input Output) - это программно управляемые выводы общего назначения, которые могут работать как входы (для считывания сигнала) или как выходы (для передачи сигнала). На Raspberry Pi они выполнены в виде двух рядов штырьков с шагом в 2,54 мм (разъем PLD). Выражение "общего назначения" означает, что эти выводы пользователь может использовать в своих целях так, как ему захочется, поскольку они не выполняют каких-то определенных жестко заданных функций. При работе с GPIO следует только иметь ввиду, что это цифровые выводы с максимальным уровнем напряжения 3,3 В, соответствующим логической единице. Поэтому программно выводить на GPIO аналоговый сигнал или подавать на него аналоговый сигнал извне для последующий оцифровки нельзя.

Итак, что представляет собой порт GPIO и каковы его возможности? GPIO объединяет в себе 26 выводов, среди которых присутствуют следующие:
  • 2 вывода с постоянным уровнем напряжения 5 В
  • 2 вывода с постоянным уровнем напряжения 3,3 В
  • 5 общих выводов (земля)
  • 17 цифровых программно управляемых выводов

Каждый из этих 17 выводов может работать как вход, либо как выход. Однако помимо этого, некоторые из этих выводов могут выполнять альтернативные необязательные функции. Что это значит? Каждый из таких выводов в зависимости от программной настройки могут работать либо как обычный вывод, либо как вывод одного из последовательных интерфейсов. С помощью таких выводов GPIO альтернативно могут реализовываться последовательные интерфейсы I2C, SPI и другие последовательные интерфейсы посредством UART.

Примечание: Это знакомые понятия для тех, кто сталкивался с программированием микроконтроллеров. Для тех, кто не сталкивался и на данном этапе пока желает просто программно выводить "1" и "0" на нужном выводе - знания этих интерфейсов не понадобится. Поэтому не буду здесь уделять этому внимание. Скажу только, что среди микроконтроллеров и других устройств (различные датчики, АЦП, ЦАП, микросхемы памяти) это очень распространенные интерфейсы, поэтому наличие выводов этих интерфейсов в GPIO Raspberry при необходимости позволяет очень легко и с минимумом программного кода "научить" Raspberry "общаться" с вашим устройством.

Как понять где какой вывод находится? Для этого необходима распиновка (цоколевка) GPIO. В официальной документации приведена распиновка разных версий GPIO Raspberry Pi. Здесь я приведу самую последнюю на данный момент распиновку GPIO - для Raspberry Pi Model B Rev.2:

Raspberry Pi GPIOРаспиновка Raspberry Pi GPIO

На схеме в скобках указана альтернативная функция каждого вывода:
  • SDA, SCL - выводы интерфейса I2C
  • TXD, RXD - выводы UART
  • MOSI, MISO, SCLK, CE0, CE1 - выводы интерфейса SPI
  • GPCLK0 - (General Purpose Clock) вывод для формирования варьируемой тактовой частоты для внешних устройств
  • PCM_CLK, PCM_DOUT - выводы аудио-интерфейса I2S

Что ВАЖНО знать перед работой с GPIO Raspberry Pi


Пожалуй, это самый важный раздел. Нужно помнить о некоторых особенностях GPIO и соблюдать определенные меры предосторожности, чтобы не залететь еще на 35-50$ не повредить Raspberry. Ниже приведены такие "критические особенности", а также несколько нюансов, которые просто могут быть полезны, и которые желательно помнить при разработке.
  • Максимальный суммарный ток обоих выводов 3.3 В равен 50 мА! Поэтому эти выводы могут использоваться для питания внешних устройств, только если их потребляемый ток меньше 50 мА.
  • Максимальный суммарный ток обоих выводов 5 В равен 300 мА! Эти выводы также могут использоваться для питания внешних устройств только в том случае, если их потребляемый ток меньше 300 мА.
  • Нельзя на GPIO подавать напряжение больше 3,3 В! Цифровые выводы GPIO имеют уровни напряжения 0 - 3,3 В и не совместимы с традиционными уровнями напряжения 0 - 5В! Поэтому нельзя напрямую соединять Raspberry Pi и цифровые устройства, работающие с TTL-уровнями 5 В. Если подать на GPIO вывод Raspberry логическую единицу, представляющую собой 5 В, а не 3,3 В - вывод может выйти из строя.
  • Выводы GPIO 14 и GPIO 15 по-умолчанию выполняют альтернативную функцию и являются выводами UART - RXD и TXD. Поэтому после включения на них присутствует высокий уровень 3,3 В. Программно их можно переконфигурировать в обычные выводы. Все остальные GPIO после включения Raspberry выполняют основную функцию и работают как обычные цифровые выводы.
  • Все настраиваемые пины GPIO по-умолчанию являются входами. И поэтому имеют высокое входное сопротивление. При этом подтяжка логического уровня у них не включена, выводы "висят в воздухе", поэтому после включения Raspberry напряжение на них может "плавать". Это нормально. Исключением является только 2 следующих вывода:
  • Выводы GPIO 0 (SDA) и GPIO 1 (SCL) по-умолчанию "подтянуты" к питанию. Поэтому после включения Raspberry на них присутствует напряжение логической единицы (3,3 В).
  • Сигнал на любом из цифровых выводов может служить источником внешнего прерывания. Кто раньше сталкивался с микроконтроллерами поймет, насколько это может быть полезно. Как использовать прерывания в Raspberry Pi - пока это идея для следующего поста.
Пожалуй, все.. Ну и в целом нужно помнить, что GPIO - это выводы, непосредственно подключенные к процессору Raspberry Pi, они являются инструментом для взаимодействия с ним. Поэтому неосторожное обращение с GPIO может привести к необратимым последствиям для процессора. На этом с "пугалками" заканчиваю. Главное и простое правило - не подавать больших напряжений и не потреблять большой ток. Переходим к делу.

    Как работать с GPIO на Python


    Примечание: Работать с GPIO, по сути, можно двумя способами:
    1) Используя bash и файловую систему Rasbian
    Raspbian является одним из дистрибутивов Linux, а концепция Linux предполагает, что любой объект является файлом. Именно это позволяет выводить и считывать сигналы с GPIO обычными командами оболочки bash прямо в терминале! Вывод логической единицы при этом выглядит как команда записи "1" в файл, соответствующий нужному выводу. Подробные примеры даны здесь

    2) Используя языки программирования (самые разные от C до Бэйсика)
    Это более гибкий и более производительный вариант, поскольку он не требует обращения к файловой системе. При этом взаимодействовать с GPIO Raspberry можно на самых разных языках, внушительный список которых приведен здесь вместе с примерами. Ниже разберем пример работы с GPIO на Python.

    Предположим нам нужно вывести логическую "1" или "0" на GPIO 7 и считать сигнал с GPIO 8.

    0) Для работы с GPIO на Python нужна специальная библиотека RPi.GPIO. Сейчас ее можно установить прямо с репозиториев, а не качать архив и устанавливать вручную, как было раньше. Воспользуемся этой возможностью:
         sudo apt-get install python-rpi.gpio (или python3-rpi.gpio для 3-й версии Питона)

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

    0.5)  GPIO является системным элементом Raspbian, поэтому работать с ним нужно только под суперпользователем. Будем писать программу прямо в консоли, поэтому запускаем python
         sudo python

    1) Импортируем библиотеку для работы с GPIO:
         import RPi.GPIO as GPIO


    2) Устанавливаем способ нумерации выводов GPIO.
    Зачем? Дело в том, что во многих функциях этой библиотеки необходимо указывать номер вывода, над которым мы хотим произвести какую-либо манипуляцию.  Однако указываемый номер можно интерпретировать по-разному: либо это номер GPIO, либо это номер пина (P1-26) на плате Raspberry (см. распиновку). Чтобы не возникало путаницы, сразу после импорта желательно "указать библиотеке", какую нумерацию мы будем использовать в программе.
         GPIO.setmode(GPIO.BCM)   #GPIO.BCM - будет использоваться нумерация GPIO 
                                                         #GPIO.BOARD - будет использоваться нумерация пинов P1-26

    3) Конфигурируем выводы
    Поскольку мы будем выводить сигналы на GPIO 7, конфигурируем его как выход, а GPIO 8 - как вход:
         GPIO.setup(7, GPIO.OUT)    #конфигурируем GPIO 7 как выход
         GPIO.setup(8, GPIO.IN)        #конфигурируем GPIO 8 как вход

    Примечание: именно после выполнения этой команды на GPIO 7 установится ровно 0 В, поскольку этот вывод больше не является входом и на нем нет "плавающего" потенциала.

    С помощью необязательного параметра pull_up_down функции setup можно также настроить "подтяжку" вывода к питанию или к земле:
         GPIO.setup(8, GPIO.IN, pull_up_down=GPIO.PUD_UP)           #подтяжка к питанию 3,3 В
         GPIO.setup(8, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)   #подтяжка к земле 0 В
         GPIO.setup(8, GPIO.IN, pull_up_down=GPIO.PUD_OFF)        #режим по-умолчанию

    4) Формируем и считываем сигналы
    Формируем "1" и "0" на GPIO 7 и считываем сигнал с GPIO 8:
         GPIO.output(7, True)             #выводим на GPIO 7 логическую "1" (3.3 V)
         GPIO.output(7, False)            #выводим на GPIO 7 логический "0"
         signal = GPIO.input(8)           #считываем сигнал с GPIO 8 в переменную signal

    5) Завершаем работу
    После всех нужных операций корректно завершаем работу:
         GPIO.cleanup()

    Выполнение этой операции приведет к возвращению всех выводов GPIO в первозданное состояние.

    Примечание: если не выполнить эту операцию, то даже после закрытия программы и выхода из python, выводы GPIO останутся в том, состоянии, в котором они были на момент завершения. Это может быть чревато тем, что при попытке повторно поработать с этими выводами будет возникать сообщение, о том, что вывод уже используется: "RuntimeWarning: This channel is already in use, continuing anyway."


    Весь пример целиком:
         import RPi.GPIO as GPIO       #подключаем библиотеку
         GPIO.setmode(GPIO.BCM)    #устанавливаем режим нумерации
         GPIO.setup(7, GPIO.OUT)     #конфигурируем GPIO 7 как выход
         GPIO.setup(8, GPIO.IN)         #конфигурируем GPIO 8 как вход
         GPIO.output(7, True)               #выводим на GPIO 7 логическую "1" (3.3 V)
         GPIO.output(7, False)              #выводим на GPIO 7 логический "0"
         signal = GPIO.input(8)             #считываем сигнал с GPIO 8 в переменную signal
         GPIO.cleanup()                        #завершаем работу с GPIO

    Характеристики GPIO


    На мой взгляд, из всех характеристик наиболее интересны временные параметры GPIO, а именно - насколько быстро может меняться состояние цифрового вывода из "1" в "0" и обратно, если управлять портом программно. Для проверки этого использовался следующий код:
         import RPi.GPIO as GPIO
         GPIO.setmode(GPIO.BCM)
         GPIO.setup(7, GPIO.OUT)
         while (1):
              GPIO.output(7, True) 
              GPIO.output(7, False)

    Смотрим, что получается на осциллографе, подключенного к выводу 7, при выполнении этого кода:

    Raspberry GPIO частота Raspberry GPIO частота
    Raspberry GPIO время нарастания Raspberry GPIO время спада

    Итак, программное циклическое переключение цифровых выводов GPIO осуществляется с максимальной частотой ~27 кГц. На второй картинке видно, что в каждом из устойчивых состояний ("1" и  "0") вывод может находится в течение ~ 18-20 мкс. Получить импульсы меньшей длительности с помощью используемых в статье программных средств нельзя.

    Примечание: частота импульсов, изображенных на верхних картинках, может незначительно колебаться. Это связано с тем, что Linux на Raspberry многозадачна и не всегда уделяет нашей программе все процессорное время. Также следует иметь ввиду, что добавление дополнительных команд внутрь цикла while может существенно увеличить время каждого импульса.

    По нижним картинкам можно оценить время переключения пина GPIO из одного состояния в другое (другими словами длительность переходного процесса) - оно составляет приблизительно 50 нс. Надо сказать, довольно неплохие результаты, однако и конкурировать по скорости с  ПЛИС GPIO Raspberry Pi не может.

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

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

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


    Как можно применить все это на практике? Зачем выводить "1" или "0" на GPIO? Например, можно помигать светодиодами! Например, можно управлять силовой нагрузкой и включать / выключать любые бытовые приборы, работающие от сети 220 В. Для этого понадобится Raspberry Pi и всего 7 деталей. Схема такого программного "выключателя" приведена ниже:

    Raspberry Pi GPIO выключатель


    Примечание: микросхема-оптодрайвер MOC3041M имеет гальваническую развязку силовых цепей от слаботочных, поэтому она является "барьером безопасности" между Raspberry и сетью 220 В, выход ее из строя не повредит Raspberry. Конденсатор C8 должен быть высоковольтным и выдерживать напряжение ~400 В.

    Данная схема может коммутировать токи до 16А. Она полностью отлажена, проверена на практике и занимает очень мало места (к сожалению, у меня не сохранились фото устройства, где она используется). Подача "1" на GPIO 7 Raspberry Pi приведет к срабатыванию оптодрайвера и открытию симистора V2, который начнет пропускать через себя ток, идущий от сети 220 В к силовой нагрузке. Прибор включается. Как только на GPIO 7 возникает "0" - симистор V2 закрывается и цепь нагрузки размыкается. Прибор выключается. Все просто!

    Применение же именно Raspberry Pi позволяет управлять нагрузкой программно, по расписанию, при наступлении каких-либо внешних событий или подаче сигналов (к примеру, сигнал с пульта для открытия ворот) и даже через Интернет (например, посредством собственного веб-сайта, работающего на том же Raspberry). Одним словом, Raspberry помогает развивать фантазию.

    На этом все. В следующем посте о Raspberry постараюсь описать работу с UART и SPI.

    35 комментариев:

    1. >Характеристики GPIO
      интересно было бы посмотреть, как изменятся характеристики переписав программу на C и установив алгоритм плакирования SCHED_FIFO, тут правда надо обязательно избавится от бесконечного цикла на конечный, иначе "зависнем" :)

      ОтветитьУдалить
      Ответы
      1. Да, я тоже задумывался, насколько большой прирост скорости даст C по сравнению с Питоном, как будет время - попробую замерять.

        Ну а если пришлете готовый код - смогу это сделать быстрей, поснимаю осциллограммы и выложу :)

        Удалить
      2. как-то вот http://pastebin.com/sgPHK7rm
        используется библиотека http://www.open.com.au/mikem/bcm2835/
        ну тогда надо сравнивать питон, С с SCHED_FIFO и С без SCHED_FIFO

        Удалить
      3. Ок, завтра доберусь до осциллографа - попробую

        Удалить
      4. Опубликовал результаты: http://hexvolt.blogspot.com/2013/03/raspberry-pi-gpio-2.html

        Удалить
    2. Не силен в электронике, но поиграться хочется..)) Вопрос - на схеме кондер С8 - его номинал 10 нано фарад или пико?

      ОтветитьУдалить
      Ответы
      1. 10 нано. И кондер должен быть высоковольтным, не меньше, чем на 400 В.

        Удалить
    3. А если вместо 220 В использовать аккумуляторы, то схема тоже будет работать ?

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

        Удалить
      2. Спасибо !
        А вы продолжаете электротехнические эксперименты с Raspberry Pi ?

        Удалить
      3. Ну не то, чтобы эксперименты, но что-то продолжаю :) Отрабатываю связь микроконтроллера XMega с Raspberry Pi через UART.

        Удалить
      4. Будете это описывать ?
        А вот интересно, что можно еще сделать такого любопытного используя Python (недавно начал активно изучать ветку 2.x) и Raspberry Pi ?

        Удалить
      5. В ближайшее время планирую посты о django (организация работы, основные инструменты, порядок действий и т.д.). Что любопытного на Python - да вот, например, веб-сайт на Django + поставить на Распберри веб-сервер и управлять чем-угодно через интернет, и многое др.

        Удалить
    4. Можете помочь, необходимо каким-либо образом узнать есть ли в цепи ток или нет, какие датчики необходимо использовать, можно использовать все это напрямую? Спасибо

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

        Удалить
    5. Не подскажите, как безопасно испытать схему до включения ее в сеть ?

      ОтветитьУдалить
      Ответы
      1. Ммм.. Да, наверное никак, если в домашних условиях. Аккумулятор вместо 220В не пойдет, т.к. схема рассчитана именно на переменное напряжение. Если есть какой-то источник переменного напряжения низкой амплитуды (меньше 220) - можно вначале подключить его, но сомневаюсь что найдется что-то подобное под рукой. Поэтому перед включением главное убедиться, что MOC3041 подключена в схеме правильно (нигде не перепутаны ножки) и что конденсатор стоит именно высоковольтный (до 400В). Если микросхема подключена правильно, то Raspberry уже защищен. Кондер должен выдерживать сетевое напряжение. Больше здесь "не работать" нечему..

        Удалить
    6. Спасибо за очень полезную инфу. Если есть голова то можно и не такое соорудить.
      К стати аккумуляторы для сотовых тоже оказывается очень интересно устроены. Кто нить пробовал их разбирать : )

      ОтветитьУдалить
    7. Здравствуйте. помогите пожалуйста написать скрипт который открывал бы страницу в браузере. Прогаммирую на Python входы/выходы GPIO на Raspberry PI. Задача такова, чтобы скрипт слушал вход и если на нем 1 то он просто открыл страницу сайта. На чем остановился я:
      import RPi.GPIO as GPIO #Импортируем библиотеку для работы с GPIO:
      GPIO.setmode(GPIO.BCM) #GPIO.BCM - будет использоваться нумерация GPIO
      GPIO.setup(8, GPIO.IN) #конфигурируем GPIO 8 как вход
      GPIO.setup(8, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #подтяжка к земле 0 В
      signal = GPIO.input(8) #считываем сигнал с GPIO 8 в переменную signal
      Не знаю не знаю правильно или нет делаю, но на этом остановился))))) Подскажите как сделать дальше или что не правильно до этого.

      ОтветитьУдалить
    8. Привет, я так понял Резистор R1 - служит для защиты raspberry?, т.к. МОС3041М - работает от тока 60 мА?

      ОтветитьУдалить
      Ответы
      1. Да, все правильно. 60 мА - это максимальный ток, для работы ему достаточно гораздо меньше. Резистор этот ток и ограничивает.

        Удалить
    9. Спасибо. Попробую собрать. А просадка напряжения (3.3v) большая? У меня до 0.9 упало, это нормально?

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

        Удалить
    10. На самом выходе распберри. Наверно я понял в чем ошибка. Подключал через usb компьютера. Хотя на выходе usb 2.0 можно получить до 500 мА. Пойду покупать внешее питание, ампера на 1,5-2. По результатам отпишусь.

      ОтветитьУдалить
    11. Хотя, когда измеряю вольтметром - на выходах 0 вольт :-)

      ОтветитьУдалить
    12. Уважаемый автор статьи, а есть ли разница подключать на тиристоре ножку 1 или 2 к 220 в?

      ОтветитьУдалить
      Ответы
      1. V2 - симистор, на симисторе вообще разницы быть не должно. Хотя у меня с некоторыми моделями симисторов бывали случаи, когда при изменении местами силовых контактов коммутация происходила лучше :)

        Удалить
    13. Я бы попробовал обычной лампой поуправлять, возможно какие-то особенности такой лампы.
      А на счет 0 вольт - то вряд ли, где именно (между какими точками) и как измеряете (в каком состоянии)? вольтметр не забыли на переменку постаить?

      ОтветитьУдалить
    14. это все свободная энергия

      ОтветитьУдалить
    15. доброго времени! подскажите пожалуйста если воспользовать этой же схемой но мос3041 заменить на мос 3021 можноли будет плавно изменять скорость электро двигателя? с нетерпением жду ответа)спасибо)

      ОтветитьУдалить
      Ответы
      1. Вы не указали параметры электродвигателя. Синхронник/асинхронник/какая мощность. На вскидку в любом случае оптронник moc 3062 и симистор BTA141-600 вам в помощь

        Удалить
    16. Нет, ибо симисторьі с индуктивной нагрузкой плохо дружат.

      ОтветитьУдалить
    17. Переменный ток проходит через цепочку R3C8 и через емкость остальной схемы, достаточно чтобы светодиодная лампа слегка светилась. Используйте лампу накаливания.

      ОтветитьУдалить