Работа с GPIO

From Wiren Board
This is the approved revision of this page, as well as being the most recent.

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

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

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

Введение

GPIO (General-purpose input/output — ввод/вывод общего назначения) — контакт электрической схемы, который может принимать одно из двух логических состояний — единицу или ноль. Пользователь может устанавливать и считывать состояние GPIO.

В контроллерах WirenBoard сервис wb-mqtt-gpio является владельцем большинства GPIO и публикует их состояние в шину MQTT. Поэтому, для корректной работы сервисов WirenBoard, рекомендуется работать с GPIO через подсистему MQTT.

Прямая работа с GPIO рекомендуется только для отладки.


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

Character device ABI (современный, поддерживаемый) — работает через символьные устройства /dev/gpiochipN.

Sysfs ABI (устаревший, deprecated) — работает через /sys/class/gpio/. Использовался исторически, но с 2017 года помечен как устаревший и будет удалён в будущем.

Использовать его можно только для поддержки старых скриптов.


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

Убедитесь, что вашу задачу нельзя решить стандартными средствами программного обеспечения Wiren Board.

Все порты Wiren Board, в том числе и GPIO, работают с напряжением 3.3V.

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

Внимание: Порты GPIO находятся непосредственно на микросхеме SoC (System on Chip) Allwinner A40/T507.

Порты GPIO в основном предназначены для работы с сигналами в пределах одной платы/одного корпуса устройства. Уровень защиты от попадания посторонних напряжений ограничен, перегрузка приведет к отказу SoC и соответственно всего контроллера, такие случаи нашей гарантией не покрываются. Например, воздействие даже самых кратковременных импульсов тока более 200мА на порт микросхемы T507 может необратимо вывести SoC из строя.

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

На портах GPIO используется 3.3V логика. Допустимые напряжения на входе должны быть в диапазоне (-0.3...3.6V) Если требуется взаимодействие с 5V логикой ( CMOS, TTL) необходимо использовать преобразователи логических уровней.

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

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

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

Если у вас есть конкретные вопросы по согласованию нестандартных устройств с GPIO портами, инженеры Wirenboard готовы помочь (обратитесь в техподдержку).

Именование GPIO

К сожалению, чёткого стандарта по именованию GPIO не существует.

Номера, смещения и названия gpiochip стоит брать в таблицах: GPIO

Работа с GPIO

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

Демон wb-mqtt-gpio управляет GPIO и публикует их состояния в MQTT-брокер mosquitto, запущенный на самом контроллере.

Таким образом, для чтения и записи GPIO можно использовать стандартные утилиты mosquitto_sub и mosquitto_pub, не трогая напрямую подсистему GPIO.

Чтение значения

Каждый GPIO, сконфигурированный в wb-mqtt-gpio, отображается как MQTT-контрол.

Топик имеет вид: /devices/<device>/controls/<control>.

Значение публикуется как retained-сообщение (можно получить его немедленно при подписке).

Пример: разово прочитать состояние EXT1_IN13:

mosquitto_sub -v -C 1 -t '/devices/+/controls/EXT1_IN13'
/devices/wb-gpio/controls/EXT1_IN13 1

Прочитать несколько значений:

for n in 10 11 12 13; do
mosquitto_sub -v -C 1 -t "/devices/wb-gpio/controls/EXT1_IN$n" | awk '{print $2}'
done

 0
 0
 0
 1

Запись значения

Чтобы изменить состояние GPIO, нужно опубликовать сообщение в топик .../on.

Пример: установить EXT2_ON1 в «1»:

mosquitto_pub -t '/devices/wb-gpio/controls/EXT2_ON1/on' -m 1

Сбросить в «0»:

mosquitto_pub -t '/devices/wb-gpio/controls/EXT2_ON1/on' -m 0

Подписка на события

Пример подписки на изменения значений GPIO:

mosquitto_sub -v -t '/devices/+/controls/EXT1_IN13'
 /devices/wb-gpio/controls/EXT1_IN13 0
 /devices/wb-gpio/controls/EXT1_IN13 1
 /devices/wb-gpio/controls/EXT1_IN13 0
 /devices/wb-gpio/controls/EXT1_IN13 1
 /devices/wb-gpio/controls/EXT1_IN13 0
 /devices/wb-gpio/controls/EXT1_IN13 1
 /devices/wb-gpio/controls/EXT1_IN13 0
 /devices/wb-gpio/controls/EXT1_IN13 1

Прямая работа с GPIO

Прямая работа с GPIO доступна через 2 интерфейса:

  1. chardev — актуальный интерфейс (доступен начиная с ядра версии 4.8)
  2. sysfs — устаревший интерфейс, однажды будет удалён

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

Для прямой работы с GPIO необходимо остановить сервис wb-mqtt-gpio (что нарушит работу сервисов WirenBoard, завязанных на GPIO):

systemctl stop wb-mqtt-gpio

Важно: останавливать сервис нужно только если вы сознательно хотите полностью взять GPIO под свой контроль. Для диагностики и отладки. В продакшене так делать не рекомендуется!

Если не остановить сервис, то попытка работы с GPIO приведёт к ошибке:

gpioget $(gpiofind "EXT2_ON2")
 gpioget: error reading GPIO values: Device or resource busy

Работа через интерфейс chardev

Внимание: для повседневной работы используйте MQTT. Интерфейс 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

Информация обо всех чипах GPIO в системе:

gpiodetect
 gpiochip0 [300b000.pinctrl] (288 lines)
 gpiochip1 [7022000.pinctrl] (32 lines)
 gpiochip2 [5011000.spi:wbec@0:wbec-gpio@0] (11 lines)
 gpiochip3 [mcp23017] (16 lines)
 gpiochip4 [mcp23008] (8 lines)

Информация о линиях определённого чипа:

gpioinfo gpiochip4
 gpiochip4 - 8 lines:
 line 0: "EXT2_DIR1" unused output active-high
 line 1: "EXT2_ON1" unused output active-high
 line 2: "EXT2_DIR2" unused output active-high
 line 3: "EXT2_ON2" unused output active-high
 line 4: "EXT2_DIR3" unused output active-high
 line 5: "EXT2_ON3" unused output active-high
 line 6: "EXT2_DIR4" unused output active-high
 line 7: "EXT2_ON4" unused output active-high

Если вы наблюдаете [used] и wb-mqtt-gpio, то значит вы забыли остановить сервис wb-mqtt-gpio, как описано выше:

gpioinfo gpiochip4
 gpiochip4 - 8 lines:
 line 0: "EXT2_DIR1" "wb-mqtt-gpio" output active-high [used]
 line 1: "EXT2_ON1" "wb-mqtt-gpio" output active-high [used]
 line 2: "EXT2_DIR2" "wb-mqtt-gpio" output active-high [used]
 line 3: "EXT2_ON2" "wb-mqtt-gpio" output active-high [used]
 line 4: "EXT2_DIR3" "wb-mqtt-gpio" output active-high [used]
 line 5: "EXT2_ON3" "wb-mqtt-gpio" output active-high [used]
 line 6: "EXT2_DIR4" "wb-mqtt-gpio" output active-high [used]
 line 7: "EXT2_ON4" "wb-mqtt-gpio" output active-high [used]

Чтение значения

Пример получения значения GPIO с меткой EXT2_ON2:

gpioget $(gpiofind "EXT2_ON2")
 1

То же самое через получение смещения:

gpiofind "EXT2_ON2"
 gpiochip4 3

gpioget gpiochip4 3
 1

Запись значения

Пример:

gpioset $(gpiofind "EXT2_ON2")=1

Массовые операции чтения и записи:

gpioset gpiochip4 1=1 2=1 3=0 4=1

gpioget gpiochip4 1 2 3 4
 1 1 0 1

Подписка на события

На примере модуля WBIO:

gpiomon $(gpiofind "EXT1_IN13")
 event: FALLING EDGE offset: 12 timestamp: [ 20588.266856349]
 event: RISING EDGE offset: 12 timestamp: [ 20588.610597731]
 event: FALLING EDGE offset: 12 timestamp: [ 20588.678908500]
 event: RISING EDGE offset: 12 timestamp: [ 20588.742134097]
 event: FALLING EDGE offset: 12 timestamp: [ 20589.000302827]
 event: RISING EDGE offset: 12 timestamp: [ 20589.026287028]
 event: FALLING EDGE offset: 12 timestamp: [ 20589.066125678]
 event: RISING EDGE offset: 12 timestamp: [ 20589.321982160]

Другие примеры использования gpiod можно посмотреть в источнике 2.

Работа через интерфейс sysfs (устаревший)

Важно: данный интерфейс работы с GPIO является устаревшим с 2017 года и не рекомендуется к использованию в новых скриптах! Он оставлен для совместимости со старыми скриптами и будет удалён в будущем. Этот раздел приведён только для тех, кто поддерживает старые скрипты. Новый код должен использовать MQTT или chardev.

Перед началом необходимо убедиться, что нужный GPIO — свободен. Для этого можно посмотреть на вывод команды cat /sys/kernel/debug/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 # выгружаем драйвер

ВНИМАНИЕ: остановка драйверов может привести к неожиданному поведению контроллера!

Для работы через sysfs с определённым 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/gpio/gpioN находятся файлы для работы:

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

Установка направления GPIO:

echo in > /sys/class/gpio/gpioN/direction # ввод
echo out > /sys/class/gpio/gpioN/direction # вывод

Чтение и установка значения

Чтение:

echo in > /sys/class/gpio/gpioN/direction
cat /sys/class/gpio/gpioN/value # вернёт 1 или 0

Запись:

echo out > /sys/class/gpio/gpioN/direction
echo 0 > /sys/class/gpio/gpioN/value
echo 1 > /sys/class/gpio/gpioN/value

Пример:

Находим номер GPIO для нужного клеммника (см. таблицы для вашей версии контроллера, напр. WB2.8). Экспортируем:

 
echo 32 > /sys/class/gpio/export 
echo out > /sys/class/gpio/gpio32/direction 
echo 1 > /sys/class/gpio/gpio32/value # открыть транзистор 
echo 0 > /sys/class/gpio/gpio32/value # закрыть транзистор

Номера GPIO

Глобальный номер для интерфейса sysfs по метке можно получить только через отладочный интерфейс ядра. Формат вывода может быть изменён в новых версиях ядра. «Старый» (глобальный) номер указан в первом столбце:

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

cat /sys/kernel/debug/gpio | grep EXT2_ON2
  gpio-542 (EXT2_ON2 |wb-mqtt-gpio ) out lo

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

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

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

Установка прерывания производится путём записи значения в файл 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 gpio-mxs 3 gpiolib

после нескольких событий:

cat /proc/interrupts | grep gpiolib
 26: 76 gpio-mxs 3 gpiolib

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

См. также elinux.org/GPIO.

Python

Для работы с GPIO на Python рекомендуется использовать библиотеку paho-mqtt:

import paho.mqtt.client as mqtt

def on_message(client, userdata, msg):
    print(f"{msg.topic} {msg.payload.decode()}")

client = mqtt.Client()
client.on_message = on_message
client.connect("localhost")

# подписываемся на все EXT1_IN1..14

for i in range(1, 15):
    client.subscribe(f"/devices/wb-gpio/controls/EXT1_IN{i}")

client.loop_forever()


Для управления GPIO через устаревшую систему sysfs из Python был написан модуль wb_common.gpio.

Модуль представляет собой обёртку вокруг sysfs.

Исходный код доступен на нашем GitHub: wb_common/gpio.py.

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

Важно: новый код должен использовать MQTT (через paho-mqtt). Библиотека wb_common.gpio сохранена только для совместимости со старыми скриптами и основана на sysfs-интерфейсе, который будет удалён.