16 879
правок
Vdromanov (обсуждение | вклад) |
м (Поправил форматирование) |
||
Строка 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= | |||
К сожалению, четкого стандарта по именованию gpio не существует, но при работе с контроллерами WirenBoard стоит придерживаться следующих правил: | К сожалению, четкого стандарта по именованию gpio не существует, но при работе с контроллерами WirenBoard стоит придерживаться следующих правил: | ||
* выводы gpio сгруппированы по банкам (''banks''; эквивалентно ''gpiochips'') | * выводы gpio сгруппированы по банкам (''banks''; эквивалентно ''gpiochips'') | ||
* каждый банк содержит 32 gpio. Нумерация банков начинается с 0. | * каждый банк содержит 32 gpio. Нумерация банков начинается с 0. | ||
==Вычисление номера gpio== | ==Вычисление номера gpio== | ||
Для управления ножкой gpio нужно знать её номер. В рассматриваемых примерах будем работать с gpio ''A1_IN'' контроллера WB6.7 (''номер: 109; gpiochip 3, offset 13''): | Для управления ножкой gpio нужно знать её номер. В рассматриваемых примерах будем работать с gpio ''A1_IN'' контроллера WB6.7 (''номер: 109; gpiochip 3, offset 13''): | ||
Вычислим банк gpio и offset, зная номер (109): | Вычислим банк gpio и offset, зная номер (109): | ||
< | <syntaxhighlight lang="bash"> | ||
# Поделим 109 на 32. Целая часть | # Поделим 109 на 32. Целая часть — номер банка, остаток - offset: | ||
109.0 / 32.0 = 3, остаток 13 | 109.0 / 32.0 = 3, остаток 13 | ||
</ | </syntaxhighlight> | ||
То же самое справедливо и наоборот. Зная банк и offset (3 и 13, соответственно), можно вычислить номер gpio: | То же самое справедливо и наоборот. Зная банк и offset (3 и 13, соответственно), можно вычислить номер gpio: | ||
< | <syntaxhighlight lang="bash"> | ||
# Умножим номер банка на 32 и прибавим offset: | # Умножим номер банка на 32 и прибавим offset: | ||
3 * 32 + 13 = 109 | 3 * 32 + 13 = 109 | ||
</ | </syntaxhighlight> | ||
=Работа из userspace= | =Работа из userspace= | ||
Перед началом работы из userspace, необходимо убедиться, в том, что нужный gpio — свободен. Для этого можно посмотреть на вывод команды | |||
<syntaxhighlight lang="bash"> | |||
Перед началом работы из 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: | Строка 46: | ||
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 свободен до следующей перезагрузки. | |||
''' | |||
==Bash== | ==Bash== | ||
В настоящий момент, для работы с gpio в userspace доступны 2 интерфейса: ''sysfs'' и ''chardev'' (начиная с версии ядра 4.8). | В настоящий момент, для работы с gpio в userspace доступны 2 интерфейса: ''sysfs'' и ''chardev'' (начиная с версии ядра 4.8). | ||
Различия между ''chardev'' и ''sysfs'' хорошо описаны в [https://embeddedbits.org/new-linux-kernel-gpio-user-space-interface/ этой статье]. Sysfs имеет статус deprecated, поэтому, по возможности, <u>стоит работать через chardev</u>. | Различия между ''chardev'' и ''sysfs'' хорошо описаны в [https://embeddedbits.org/new-linux-kernel-gpio-user-space-interface/ этой статье]. Sysfs имеет статус deprecated, поэтому, по возможности, <u>стоит работать через chardev</u>. | ||
===Интерфейс 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: | Строка 84: | ||
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: | Строка 98: | ||
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> | |||
====Работа с прерываниями==== | ====Работа с прерываниями==== | ||
Через интерфейс sysfs можно запросить прерывания по изменению состояния процессора. | Через интерфейс sysfs можно запросить прерывания по изменению состояния процессора. | ||
Установка прерывания производится путём записи значения в файл "edge". Значения могут быть: | Установка прерывания производится путём записи значения в файл "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 | none | ||
~# echo falling > /sys/class/gpio/gpio3/edge # устанавливаем прерывание по нисходящему фронту | |||
~# cat /proc/interrupts | grep gpiolib # прерывание появилось в списке. 26 - внутренний номер прерывания, 0 - количество событий | |||
26: 0 gpio-mxs 3 gpiolib | 26: 0 gpio-mxs 3 gpiolib | ||
~# cat /proc/interrupts | grep gpiolib # после нескольких собтий, 76 - количество событий | |||
26: 76 gpio-mxs 3 gpiolib | 26: 76 gpio-mxs 3 gpiolib | ||
</syntaxhighlight> | |||
Прерывания можно ловить из userspace с помощью системного вызова epoll() и select() на файл value. | Прерывания можно ловить из userspace с помощью системного вызова epoll() и select() на файл value. | ||
Пример работы см. [https://github.com/contactless/wiegand-linux-sysfs] | Пример работы см. [https://github.com/contactless/wiegand-linux-sysfs] | ||
См. также [http://elinux.org/GPIO elinux.org] | См. также [http://elinux.org/GPIO elinux.org] | ||
===Работа через chardev=== | ===Работа через chardev=== | ||
Представленный в ядре 4.8 интерфейс chardev имеет C/Python библиотеку ''libgpiod'' и userspace-утилиты для работы с gpio. Исходный код библиотеки и документация доступны в [https://github.com/brgl/libgpiod репозитории libgpiod]. | Представленный в ядре 4.8 интерфейс chardev имеет C/Python библиотеку ''libgpiod'' и userspace-утилиты для работы с gpio. Исходный код библиотеки и документация доступны в [https://github.com/brgl/libgpiod репозитории libgpiod]. | ||
Утилиты распространяются в составе debian-пакетов ''gpiod'' и ''libgpiod-dev'' для debian buster и новее. К сожалению, '''для stretch пакетов в официальных репозиториях нет'''. | Утилиты распространяются в составе debian-пакетов ''gpiod'' и ''libgpiod-dev'' для debian buster и новее. К сожалению, '''для stretch пакетов в официальных репозиториях нет'''. | ||
Если нужно установить libgpiod в debian stretch, можно воспользоваться сторонними репозиториями (например, [https://github.com/rcn-ee/repos этим]). '''Используйте сторонние репозитории на свой страх и риск; компания WirenBoard не контролирует их содержимое'''. | Если нужно установить libgpiod в debian stretch, можно воспользоваться сторонними репозиториями (например, [https://github.com/rcn-ee/repos этим]). '''Используйте сторонние репозитории на свой страх и риск; компания WirenBoard не контролирует их содержимое'''. | ||
Для работы с gpio из bash в пакете ''gpiod'' поставляются следующие утилиты: | Для работы с gpio из bash в пакете ''gpiod'' поставляются следующие утилиты: | ||
* gpiodetect | * <code>gpiodetect</code> — информация обо всех банках gpio в системе | ||
* gpioinfo | * <code>gpioinfo</code> — подробная информация обо всех линиях gpio определённого банка | ||
* gpioget <чип> <линия> | * <code>gpioget <чип> <линия></code> — возвращает значение определённого gpio | ||
* gpioset <чип> <линия1>=<значение1> <линия2>=<значение2> | * <code>gpioset <чип> <линия1>=<значение1> <линия2>=<значение2></code> — устанавиливает состояние на определенные линии gpio | ||
* gpiofind <название> | * <code>gpiofind <название></code> — возвращает номер gpio | ||
* gpiomon | * <code>gpiomon</code> — отслеживание событий gpio | ||
Примеры использования gpiod можно посмотреть в [https://www.acmesystems.it/gpiod] и [https://github.com/brgl/libgpiod] | Примеры использования gpiod можно посмотреть в [https://www.acmesystems.it/gpiod] и [https://github.com/brgl/libgpiod] | ||
==Python== | ==Python== | ||
Для управления gpio из python был написан модуль <pre>wb_common.gpio</pre> | Для управления gpio из python был написан модуль <pre>wb_common.gpio</pre> | ||
Модуль представляет собой обёртку вокруг sysfs. Исходный код доступен [https://github.com/wirenboard/wb-common/blob/master/wb_common/gpio.py на нашем github.] | Модуль представляет собой обёртку вокруг sysfs. Исходный код доступен [https://github.com/wirenboard/wb-common/blob/master/wb_common/gpio.py на нашем github.] | ||
Строка 238: | Строка 194: | ||
Модуль позволяет работать с gpio в синхронном и асинхронном (с регистрацией коллбэков) режимах. | Модуль позволяет работать с gpio в синхронном и асинхронном (с регистрацией коллбэков) режимах. | ||
==Прямое обращение через память процессора== | ==Прямое обращение через память процессора== | ||
<b>Этот метод настоятельно НЕ РЕКОМЕНДУЕТСЯ для использования без достаточных оснований. Для работы из С/C++ стоит использовать работу через файлы в sysfs или chardev, как описано в предыдущих разделах</b>. | <b>Этот метод настоятельно НЕ РЕКОМЕНДУЕТСЯ для использования без достаточных оснований. Для работы из С/C++ стоит использовать работу через файлы в sysfs или chardev, как описано в предыдущих разделах</b>. | ||
Управлять GPIO можно с помощью прямого доступа к регистрам процессора, в обход Linux, через интерфейс /dev/mem. При этом, по сравнению с работой через sysfs минимизируются накладные расходы. Этот метод можно использовать, | Управлять GPIO можно с помощью прямого доступа к регистрам процессора, в обход Linux, через интерфейс /dev/mem. При этом, по сравнению с работой через sysfs минимизируются накладные расходы. Этот метод можно использовать, | ||
если вам необходим очень быстрый доступ к GPIO, например bitbang протоколов или ШИМ. Стоит иметь в виду, что планировщик процессов всё ещё может вносить в работу программы значительные задержки. | если вам необходим очень быстрый доступ к GPIO, например bitbang протоколов или ШИМ. Стоит иметь в виду, что планировщик процессов всё ещё может вносить в работу программы значительные задержки. | ||
Рекомендуется выносить критичные ко времени задачи в ядро. | Рекомендуется выносить критичные ко времени задачи в ядро. | ||
См. [http://olimex.wordpress.com/2012/09/11/imx233-olinuxino-gpios-faster-and-faster/] , [https://github.com/OLIMEX/OLINUXINO/blob/master/SOFTWARE/iMX233/gpio-mmap.h] | См. [http://olimex.wordpress.com/2012/09/11/imx233-olinuxino-gpios-faster-and-faster/] , [https://github.com/OLIMEX/OLINUXINO/blob/master/SOFTWARE/iMX233/gpio-mmap.h] | ||
=Работа в ядре Linux= | =Работа в ядре Linux= | ||
Ознакомиться с ядром Linux, использующимся в контроллерах WirenBoard можно в нашем [https://github.com/wirenboard/linux репозитории ядра]. | Ознакомиться с ядром Linux, использующимся в контроллерах WirenBoard можно в нашем [https://github.com/wirenboard/linux репозитории ядра]. | ||
===Рекомендации по Device Tree=== | ===Рекомендации по Device Tree=== | ||
Device-tree, использующиеся на контроллерах WirenBoard, доступны в репозитории ядра. | Device-tree, использующиеся на контроллерах WirenBoard, доступны в репозитории ядра. | ||
Разные аппаратные ревизии контроллера используют разные dts (например, dts для WB6.X можно найти [https://github.com/wirenboard/linux/blob/dev/v4.9.x/arch/arm/boot/dts/imx6ul-wirenboard61.dts здесь]) | Разные аппаратные ревизии контроллера используют разные dts (например, dts для WB6.X можно найти [https://github.com/wirenboard/linux/blob/dev/v4.9.x/arch/arm/boot/dts/imx6ul-wirenboard61.dts здесь]) | ||
Указывать GPIO в Device Tree необходимо для настройки работы GPIO в режиме программного SPI, I2C, для использования GPIO в качестве источника прерываний и т.д. | Указывать GPIO в Device Tree необходимо для настройки работы GPIO в режиме программного SPI, I2C, для использования GPIO в качестве источника прерываний и т.д. | ||
Так, например, на пин 10@UEXT1 (CS) и пины 5@UEXT2 (SCL), 6@UEXT2 (SDA), 10@UEXT2 (CS) выведены линии GPIO процессора. Их можно сконфигурировать для использования, например, в качестве chip-select для SPI или в качестве I2C. | Так, например, на пин 10@UEXT1 (CS) и пины 5@UEXT2 (SCL), 6@UEXT2 (SDA), 10@UEXT2 (CS) выведены линии GPIO процессора. Их можно сконфигурировать для использования, например, в качестве chip-select для SPI или в качестве I2C. | ||
GPIO процессора и периферийных устройств разбиты на банки (gpiochip). GPIO процессора разбиты на 3 банка по 32 GPIO: gpio0, gpio1, gpio2. Адресация GPIO в Device Tree происходит по номеру банка и номеру GPIO *внутри* банка. | GPIO процессора и периферийных устройств разбиты на банки (gpiochip). GPIO процессора разбиты на 3 банка по 32 GPIO: gpio0, gpio1, gpio2. Адресация GPIO в Device Tree происходит по номеру банка и номеру GPIO *внутри* банка. | ||
====Пример device-tree node==== | ====Пример device-tree node==== | ||
Определим сигнал 6@UEXT2 (SDA) в качестве источника прерываний для драйвера mrf24j40. Согласно таблице [[Список GPIO]], сигнал соответствует GPIO 53 процессора. 53 принадлежит второму банку gpio (от 32 до 63). Номер GPIO внутри банка 53-32=21 : | |||
Определим сигнал 6@UEXT2 (SDA) в качестве источника прерываний для драйвера mrf24j40. Согласно таблице [[ | |||
< | <syntaxhighlight lang="json"> | ||
6lowpan@0 { | |||
compatible = "microchip,mrf24j40"; | |||
spi-max-frequency = <100000>; | |||
reg = <6>; | |||
interrupt-parent = <&gpio1>; | |||
interrupts = <21 0>; | |||
}; | |||
</ | </syntaxhighlight> | ||