Работа с GPIO: различия между версиями
Vdromanov (обсуждение | вклад) |
Taraant (обсуждение | вклад) м (Актуализировал ссылку) |
||
(не показано 13 промежуточных версий 4 участников) | |||
Строка 1: | Строка 1: | ||
'''ВНИМАНИЕ:''' статья рассчитана на разработчиков или опытных пользователей и даёт общие рекомендации того, как использовать gpio в обход [[Wiren Board Software | официального ПО WirenBoard]]. | |||
Если вам нужно работать напрямую с gpio, то мы рекомендуем делать это через драйвер [https://github.com/wirenboard/wb-homa-gpio wb-mqtt-gpio]. | |||
Описание доступных ножек gpio для конкретной ревизии контроллера можете посмотреть в статье [[GPIO]]. | |||
Описание доступных ножек gpio для конкретной ревизии контроллера | |||
=Меры предосторожности= | =Меры предосторожности= | ||
* '''Убедитесь, что вашу задачу нельзя решить стандартными средствами программного обеспечения Wiren Board.''' | |||
* '''Все порты Wiren Board, в том числе и GPIO, работают с напряжением 3.3V.''' | |||
* '''Подключение сигнала с напряжением большим 3.3V к ножке GPIO грозит выходом из строя процессорного модуля.''' | |||
В случае необходимости подключения устройств, работающих с более высоким напряжением, необходимо использовать схемы согласования или подключать (для 5V) через резистор в 20 кОм и более. | |||
=Именование gpio= | |||
К сожалению, четкого стандарта по именованию gpio не существует. Номера, смещения и названия gpiochip стоит брать в таблицах: [[GPIO | Таблицы GPIO разных версий контроллера]]. | |||
=Работа из userspace= | |||
Перед началом работы из userspace, необходимо убедиться, в том, что нужный gpio — свободен. Для этого можно посмотреть на вывод команды | |||
<syntaxhighlight lang="bash"> | |||
=Работа из userspace= | |||
Перед началом работы из userspace, необходимо убедиться, в том, что нужный gpio | |||
< | |||
cat /sys/kernel/debug/gpio | cat /sys/kernel/debug/gpio | ||
</ | </syntaxhighlight> | ||
В выводе команды видим примерно следующее: | В выводе команды видим примерно следующее: | ||
< | <syntaxhighlight lang="bash"> | ||
gpiochip0: GPIOs 0-31, parent: platform/209c000.gpio, 209c000.gpio: | gpiochip0: GPIOs 0-31, parent: platform/209c000.gpio, 209c000.gpio: | ||
gpio-0 ( |sysfs ) in hi IRQ | gpio-0 ( |sysfs ) in hi IRQ | ||
Строка 63: | Строка 30: | ||
gpio-26 ( |sysfs ) out lo | gpio-26 ( |sysfs ) out lo | ||
gpio-27 ( |sysfs ) out hi | gpio-27 ( |sysfs ) out hi | ||
</ | </syntaxhighlight> | ||
Это значит, что gpio 0, 26 и 27 уже экспортированы в sysfs и доступны для управления. Gpio 11 и 13 заняты ядерным драйвером onewire и недоступны для использования. Остальные gpio банка 0 — свободны. | |||
Это значит, что gpio 0, 26 и 27 уже экспортированы в sysfs и доступны для управления. Gpio 11 и 13 заняты ядерным драйвером onewire и недоступны для использования. Остальные gpio банка 0 | |||
Если нужный gpio — занят, то можно остановить драйвер: | |||
Если нужный gpio | <syntaxhighlight lang="bash"> | ||
< | |||
lsmod | grep w1 # узнаем название драйвера | lsmod | grep w1 # узнаем название драйвера | ||
rmmod w1_gpio # выгружаем драйвер, название которого узнали | rmmod w1_gpio # выгружаем драйвер, название которого узнали | ||
</ | </syntaxhighlight> | ||
'''ВНИМАНИЕ:''' остановка драйверов может привести к неожиданному поведению контроллера. Теперь нужный gpio свободен до следующей перезагрузки. | |||
''' | |||
В контроллерах Wiren Board доступные к использованию gpio обычно заняты сервисом wb-mqtt-gpio. Остановить его можно так: | |||
<syntaxhighlight lang="bash"> | |||
systemctl stop wb-mqtt-gpio | |||
</syntaxhighlight> | |||
==Bash== | |||
В настоящий момент, для работы с gpio в userspace доступны 2 интерфейса: ''sysfs'' и ''chardev'' (начиная с версии ядра 4.8). | В настоящий момент, для работы с gpio в userspace доступны 2 интерфейса: ''sysfs'' и ''chardev'' (начиная с версии ядра 4.8). | ||
Различия между ''chardev'' и ''sysfs'' хорошо описаны в [https://sergioprado.blog/new-linux-kernel-gpio-user-space-interface/ этой статье]. Sysfs имеет статус deprecated, поэтому, по возможности, <u>стоит работать через chardev</u>. | |||
Различия между ''chardev'' и ''sysfs'' хорошо описаны в [https:// | |||
===Работа через chardev=== | |||
Представленный в ядре 4.8 интерфейс chardev имеет C/Python библиотеку ''libgpiod'' и userspace-утилиты для работы с gpio. Исходный код библиотеки и документация доступны в [https://github.com/brgl/libgpiod репозитории libgpiod]. | |||
Утилиты распространяются в составе debian-пакетов ''gpiod'' и ''libgpiod-dev''. Установка: | |||
<syntaxhighlight lang="bash"> | |||
~# apt install gpiod | |||
</syntaxhighlight> | |||
Для работы с gpio в пакете ''gpiod'' поставляются следующие утилиты: | |||
* <code>gpiodetect</code> — информация обо всех банках gpio в системе | |||
* <code>gpioinfo</code> — подробная информация обо всех линиях gpio определённого банка | |||
* <code>gpioget <чип> <линия></code> — возвращает значение определённого gpio | |||
* <code>gpioset <чип> <линия1>=<значение1> <линия2>=<значение2></code> — устанавливает состояние на определенные линии gpio | |||
* <code>gpiofind <название></code> — возвращает номер gpio | |||
* <code>gpiomon</code> — отслеживание событий gpio | |||
Пример работы: | |||
<syntaxhighlight lang="bash"> | |||
~# 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 | |||
</syntaxhighlight> | |||
Примеры использования gpiod можно посмотреть в [https://www.acmesystems.it/gpiod] и [https://github.com/brgl/libgpiod]. | |||
===Интерфейс sysfs=== | ===Интерфейс sysfs=== | ||
Для работы через sysfs c определённым GPIO его надо экспортировать: | Для работы через sysfs c определённым GPIO его надо экспортировать: | ||
Здесь и далее N — номер gpio | |||
Здесь и далее N | |||
< | <syntaxhighlight lang="bash"> | ||
echo N > /sys/class/gpio/export | echo N > /sys/class/gpio/export | ||
</ | </syntaxhighlight> | ||
Экспортированные gpio появляются в каталоге <code>/sys/class/gpio</code>: | |||
Экспортированные gpio появляются в каталоге /sys/class/gpio | <syntaxhighlight lang="bash"> | ||
~# ls -1 /sys/class/gpio/ | |||
< | |||
< | |||
export | export | ||
gpio32 | gpio32 | ||
Строка 112: | Строка 107: | ||
gpiochip64 | gpiochip64 | ||
unexport | unexport | ||
</ | </syntaxhighlight> | ||
В директории <code>/sys/class/gpioN</code> теперь находятся файлы для работы с GPIO (где N — номер GPIO, как и было сказано ранее): | |||
В директории < | |||
< | <syntaxhighlight lang="bash"> | ||
~# ls -1 /sys/class/gpio/gpioN/ | |||
active_low | active_low | ||
device | device | ||
Строка 128: | Строка 121: | ||
uevent | uevent | ||
value | value | ||
</ | </syntaxhighlight> | ||
Установка направления GPIO (ввод/вывод) производится с помощью записи в файл <code>direction</code> | |||
Установка направления GPIO (ввод/вывод) производится с помощью записи в файл < | |||
< | <syntaxhighlight lang="bash"> | ||
echo in > /sys/class/gpio/gpioN/direction # установим GPIO номер N на ввод | echo in > /sys/class/gpio/gpioN/direction # установим GPIO номер N на ввод | ||
echo out > /sys/class/gpio/gpioN/direction # установим GPIO номер N на вывод | echo out > /sys/class/gpio/gpioN/direction # установим GPIO номер N на вывод | ||
</ | </syntaxhighlight> | ||
Чтение и установка значения GPIO производится с помощью файла <code>value</code>. | |||
Чтение и установка значения GPIO производится с помощью файла < | |||
====Чтение и запись==== | |||
Чтение: | Чтение: | ||
<syntaxhighlight lang="bash"> | |||
< | |||
echo in > /sys/class/gpio/gpioN/direction # установим GPIO номер N на ввод | echo in > /sys/class/gpio/gpioN/direction # установим GPIO номер N на ввод | ||
cat /sys/class/gpio/gpioN/value # вернёт 1 или 0 | cat /sys/class/gpio/gpioN/value # вернёт 1 или 0 | ||
</ | </syntaxhighlight> | ||
Запись: | Запись: | ||
<syntaxhighlight lang="bash"> | |||
< | |||
echo out > /sys/class/gpio/gpioN/direction # установим GPIO номер N на вывод | echo out > /sys/class/gpio/gpioN/direction # установим GPIO номер N на вывод | ||
echo 0 > /sys/class/gpio/gpioN/value # установим логический 0 (низкое напряжение) на GPIO номер N | echo 0 > /sys/class/gpio/gpioN/value # установим логический 0 (низкое напряжение) на GPIO номер N | ||
echo 1 > /sys/class/gpio/gpioN/value # установим логический 1 (высокое напряжение) на GPIO номер N | echo 1 > /sys/class/gpio/gpioN/value # установим логический 1 (высокое напряжение) на GPIO номер N | ||
</ | </syntaxhighlight> | ||
Пример: | Пример: | ||
# Находим номер GPIO, соответсвующий вашей версии контролера нужному клеммнику в таблице [[GPIO|WB2.8]]. Для клеммника номер 2 в версии 2.8 это GPIO 32. | # Находим номер GPIO, соответсвующий вашей версии контролера нужному клеммнику в таблице [[GPIO|WB2.8]]. Для клеммника номер 2 в версии 2.8 это GPIO 32. | ||
# Экспортируем GPIO в sysfs | # Экспортируем GPIO в sysfs | ||
#:< | #:<syntaxhighlight lang="bash"> | ||
echo 32 > /sys/class/gpio/export | |||
</syntaxhighlight> | |||
# Устанавливаем GPIO в режим вывода для управления транзистором. Это обязательно, т.к. GPIO может находится в режиме ввода и иметь высокий импенданс, оставляя транзистор в неопределённом состоянии. | # Устанавливаем GPIO в режим вывода для управления транзистором. Это обязательно, т.к. GPIO может находится в режиме ввода и иметь высокий импенданс, оставляя транзистор в неопределённом состоянии. | ||
#:< | #:<syntaxhighlight lang="bash"> | ||
echo out > /sys/class/gpio/gpio32/direction | |||
</syntaxhighlight> | |||
# Открываем транзистор, подавая логический высокий уровень на затвор: | # Открываем транзистор, подавая логический высокий уровень на затвор: | ||
#:< | #:<syntaxhighlight lang="bash"> | ||
echo 1 > /sys/class/gpio/gpio32/value | |||
</syntaxhighlight> | |||
# Закрываем транзистор, подавая логический ноль на затвор: | # Закрываем транзистор, подавая логический ноль на затвор: | ||
#:< | #:<syntaxhighlight lang="bash"> | ||
echo 0 > /sys/class/gpio/gpio32/value | |||
</syntaxhighlight> | |||
==== | ====Номера GPIO==== | ||
Наиболее унивверсальный способ получить номер GPIO для устаревшего интерфейса sysfs - получить его по метке GPIO. | |||
Для этого нужно найти номер gpiochip, смещение внутри gpiochip и базовый адрес gpiochip. | |||
Пример: | |||
< | <syntaxhighlight lang="bash"> | ||
~# gpiofind "A1 OUT" | |||
gpiochip0 109 | |||
~# cat /sys/class/gpio/gpiochip0/base | |||
0 | |||
</syntaxhighlight> | |||
Т.е. для A1 OUT будет старый номер 0 (base) + 109 (offset) = 109. | |||
Номера по метке можно получить также через отладочный интерфейс ядра. Это не требует использования утилит gpiod, но формат вывода может быть изменён в новых версиях ядра. "Старый" (глобальный) номер указан в первом столбце: | |||
< | <syntaxhighlight lang="bash"> | ||
~# cat /sys/kernel/debug/gpio | grep "A1 OUT" | |||
gpio-109 (A1 OUT |wb-mqtt-gpio ) out lo | |||
</syntaxhighlight> | |||
GPIO для дискретных входов и выходов модулей расширения и модулей ввода-вывода доступны таким же образом. Не забудьте выбрать модуль в интерфейсе Hardware Modules Configuration. | |||
====Работа с прерываниями==== | |||
Через интерфейс sysfs можно запросить прерывания по изменению состояния процессора. | |||
< | Установка прерывания производится путём записи значения в файл "edge". Значения могут быть: | ||
* <code>none</code> — отключить прерывание | |||
* <code>rising</code> — включить прерывание по нисходящему фронту | |||
* <code>falling</code> — включить прерывание по восодящему фронту | |||
* <code>both</code> — включить прерывание по обеим фронтам. | |||
Пример работы с прерываниями: | |||
<syntaxhighlight lang="bash"> | |||
~# 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 | |||
</syntaxhighlight> | |||
Прерывания можно ловить из userspace с помощью системного вызова epoll() и select() на файл value. | |||
Пример работы см. [https://github.com/contactless/wiegand-linux-sysfs] | |||
См. также [http://elinux.org/GPIO elinux.org] | |||
==Python== | |||
Для управления gpio из python был написан модуль <pre>wb_common.gpio</pre> | |||
Модуль представляет собой обёртку вокруг sysfs. Исходный код доступен [https://github.com/wirenboard/wb-common/blob/master/wb_common/gpio.py на нашем github.] | |||
Модуль позволяет работать с gpio в синхронном и асинхронном (с регистрацией коллбэков) режимах. | |||
Текущая версия на 22:40, 30 августа 2024
ВНИМАНИЕ: статья рассчитана на разработчиков или опытных пользователей и даёт общие рекомендации того, как использовать 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 <чип> <линия>
— возвращает значение определённого gpiogpioset <чип> <линия1>=<значение1> <линия2>=<значение2>
— устанавливает состояние на определенные линии gpiogpiofind <название>
— возвращает номер gpiogpiomon
— отслеживание событий 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
Пример:
- Находим номер GPIO, соответсвующий вашей версии контролера нужному клеммнику в таблице WB2.8. Для клеммника номер 2 в версии 2.8 это GPIO 32.
- Экспортируем GPIO в sysfs
echo 32 > /sys/class/gpio/export
- Устанавливаем GPIO в режим вывода для управления транзистором. Это обязательно, т.к. GPIO может находится в режиме ввода и иметь высокий импенданс, оставляя транзистор в неопределённом состоянии.
echo out > /sys/class/gpio/gpio32/direction
- Открываем транзистор, подавая логический высокий уровень на затвор:
echo 1 > /sys/class/gpio/gpio32/value
- Закрываем транзистор, подавая логический ноль на затвор:
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 в синхронном и асинхронном (с регистрацией коллбэков) режимах.