Работа с GPIO

Материал из Wiren Board
Это утверждённая версия страницы. Она же — наиболее свежая версия.

ВНИМАНИЕ: статья рассчитана на разработчиков или опытных пользователей и даёт общие рекомендации того, как использовать gpio в обход официального ПО WirenBoard.

Если вам нужно работать напрямую с gpio, то мы рекомендуем делать это через драйвер wb-mqtt-gpio.

Описание доступных ножек gpio для конкретной ревизии контроллера можете посмотреть в статье GPIO.

Меры предосторожности

  • Убедитесь, что вашу задачу нельзя решить стандартными средствами программного обеспечения Wiren Board.
  • Все порты Wiren Board, в том числе и GPIO, работают с напряжением 3.3V.
  • Подключение сигнала с напряжением большим 3.3V к ножке GPIO грозит выходом из строя процессорного модуля.

В случае необходимости подключения устройств, работающих с более высоким напряжением, необходимо использовать схемы согласования или подключать (для 5V) через резистор в 20 кОм и более.

Именование gpio

К сожалению, четкого стандарта по именованию gpio не существует. Номера, смещения и названия gpiochip стоит брать в таблицах: Таблицы GPIO разных версий контроллера.

Работа из userspace

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

cat /sys/kernel/debug/gpio

В выводе команды видим примерно следующее:

gpiochip0: GPIOs 0-31, parent: platform/209c000.gpio, 209c000.gpio:
 gpio-0   (                    |sysfs               ) in  hi IRQ
 gpio-10  (                    |?                   ) in  lo    
 gpio-11  (                    |w1                  ) in  hi    
 gpio-13  (                    |w1 strong pullup    ) out lo    
 gpio-26  (                    |sysfs               ) out lo    
 gpio-27  (                    |sysfs               ) out hi

Это значит, что gpio 0, 26 и 27 уже экспортированы в sysfs и доступны для управления. Gpio 11 и 13 заняты ядерным драйвером onewire и недоступны для использования. Остальные gpio банка 0 — свободны.

Если нужный gpio — занят, то можно остановить драйвер:

lsmod | grep w1 # узнаем название драйвера
rmmod w1_gpio # выгружаем драйвер, название которого узнали

ВНИМАНИЕ: остановка драйверов может привести к неожиданному поведению контроллера. Теперь нужный gpio свободен до следующей перезагрузки.

В контроллерах Wiren Board доступные к использованию gpio обычно заняты сервисом wb-mqtt-gpio. Остановить его можно так:

systemctl stop wb-mqtt-gpio

Bash

В настоящий момент, для работы с gpio в userspace доступны 2 интерфейса: sysfs и chardev (начиная с версии ядра 4.8).

Различия между chardev и sysfs хорошо описаны в этой статье. Sysfs имеет статус deprecated, поэтому, по возможности, стоит работать через chardev.

Работа через chardev

Представленный в ядре 4.8 интерфейс chardev имеет C/Python библиотеку libgpiod и userspace-утилиты для работы с gpio. Исходный код библиотеки и документация доступны в репозитории libgpiod.

Утилиты распространяются в составе debian-пакетов gpiod и libgpiod-dev. Установка:

~# apt install gpiod

Для работы с gpio в пакете gpiod поставляются следующие утилиты:

  • gpiodetect — информация обо всех банках gpio в системе
  • gpioinfo — подробная информация обо всех линиях gpio определённого банка
  • gpioget <чип> <линия> — возвращает значение определённого gpio
  • gpioset <чип> <линия1>=<значение1> <линия2>=<значение2> — устанавливает состояние на определенные линии gpio
  • gpiofind <название> — возвращает номер gpio
  • gpiomon — отслеживание событий gpio

Пример работы:

~# systemctl stop wb-mqtt-gpio # stop WB GPIO<==>MQTT bridge
~# gpiofind "A1 IN" # find gpiochip and offset for a given label
gpiochip0 231
~# gpioget `gpiofind "A1 IN"` # get the current GPIO value, using data returned by gpiofind
1
~# gpiofind "A1 OUT" # find chip and offset for another GPIO
gpiochip0 109
~# gpioset gpiochip0 109=1 # enable A1 OUT
~# gpioset gpiochip0 109=0 # disable A1 OUT
~# gpioset `gpiofind "A1 OUT"`=0 # now the same using gpiofind

Примеры использования gpiod можно посмотреть в [1] и [2].

Интерфейс sysfs

Для работы через sysfs c определённым GPIO его надо экспортировать:

Здесь и далее N — номер gpio

echo N > /sys/class/gpio/export

Экспортированные gpio появляются в каталоге /sys/class/gpio:

~# ls -1 /sys/class/gpio/
export
gpio32
gpiochip0
gpiochip120
gpiochip32
gpiochip64
unexport

В директории /sys/class/gpioN теперь находятся файлы для работы с GPIO (где N — номер GPIO, как и было сказано ранее):

~# ls -1 /sys/class/gpio/gpioN/
active_low
device
direction
edge
power
subsystem
uevent
value

Установка направления GPIO (ввод/вывод) производится с помощью записи в файл direction

echo in > /sys/class/gpio/gpioN/direction # установим GPIO номер N на ввод
echo out > /sys/class/gpio/gpioN/direction # установим GPIO номер N на вывод

Чтение и установка значения GPIO производится с помощью файла value.

Чтение и запись

Чтение:

echo in > /sys/class/gpio/gpioN/direction # установим GPIO номер N на ввод
cat /sys/class/gpio/gpioN/value # вернёт 1 или 0

Запись:

echo out > /sys/class/gpio/gpioN/direction # установим GPIO номер N на вывод
echo 0 > /sys/class/gpio/gpioN/value # установим логический 0  (низкое напряжение) на GPIO номер N
echo 1 > /sys/class/gpio/gpioN/value # установим логический 1  (высокое напряжение) на GPIO номер N

Пример:

  1. Находим номер GPIO, соответсвующий вашей версии контролера нужному клеммнику в таблице WB2.8. Для клеммника номер 2 в версии 2.8 это GPIO 32.
  2. Экспортируем GPIO в sysfs
    echo 32 > /sys/class/gpio/export
    
  3. Устанавливаем GPIO в режим вывода для управления транзистором. Это обязательно, т.к. GPIO может находится в режиме ввода и иметь высокий импенданс, оставляя транзистор в неопределённом состоянии.
    echo out > /sys/class/gpio/gpio32/direction
    
  4. Открываем транзистор, подавая логический высокий уровень на затвор:
    echo 1 > /sys/class/gpio/gpio32/value
    
  5. Закрываем транзистор, подавая логический ноль на затвор:
    echo 0 > /sys/class/gpio/gpio32/value
    

Номера GPIO

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

Пример:

~# gpiofind "A1 OUT"
gpiochip0 109
~# cat /sys/class/gpio/gpiochip0/base 
0

Т.е. для A1 OUT будет старый номер 0 (base) + 109 (offset) = 109.

Номера по метке можно получить также через отладочный интерфейс ядра. Это не требует использования утилит gpiod, но формат вывода может быть изменён в новых версиях ядра. "Старый" (глобальный) номер указан в первом столбце:


~# cat /sys/kernel/debug/gpio  | grep "A1 OUT"
 gpio-109 (A1 OUT              |wb-mqtt-gpio        ) out lo

GPIO для дискретных входов и выходов модулей расширения и модулей ввода-вывода доступны таким же образом. Не забудьте выбрать модуль в интерфейсе Hardware Modules Configuration.



Работа с прерываниями

Через интерфейс sysfs можно запросить прерывания по изменению состояния процессора.

Установка прерывания производится путём записи значения в файл "edge". Значения могут быть:

  • none — отключить прерывание
  • rising — включить прерывание по нисходящему фронту
  • falling — включить прерывание по восодящему фронту
  • both — включить прерывание по обеим фронтам.

Пример работы с прерываниями:

~# echo 3 >  /sys/class/gpio/export # экспортируем GPIO номер 3 (TB10 на WB3.3)
~# cat /sys/class/gpio/gpio3/edge   # проверяем состояние прерывания
none
~# echo falling > /sys/class/gpio/gpio3/edge # устанавливаем прерывание по нисходящему фронту
~# cat /proc/interrupts  | grep gpiolib # прерывание появилось в списке. 26 - внутренний номер прерывания, 0 - количество событий
 26:          0  gpio-mxs   3  gpiolib
~# cat /proc/interrupts  | grep gpiolib # после нескольких собтий, 76 - количество событий
 26:         76  gpio-mxs   3  gpiolib

Прерывания можно ловить из userspace с помощью системного вызова epoll() и select() на файл value. Пример работы см. [3]

См. также elinux.org


Python

Для управления gpio из python был написан модуль

wb_common.gpio

Модуль представляет собой обёртку вокруг sysfs. Исходный код доступен на нашем github.

Модуль позволяет работать с gpio в синхронном и асинхронном (с регистрацией коллбэков) режимах.