В 2017 году Bartosz Golaszewski начал разработку библиотеки Libgpiod и утилит для работы с GPIO. Библиотека libgpiod инкапсулирует вызовы ioctl и структуры данных за простым API в Linux, предназначена для замены доступа к GPIO через виртуальную файловую систему sysfs. Используемый в библиотеке новый интерфейс chardev гарантирует, что все выделенные ресурсы будут освобождены после закрытия файлового дескриптора устройства, и добавляет несколько новых функций, которых нет в устаревшем интерфейсе sysfs (например, опрос событий, установка/чтение нескольких значений одновременно). В публикации будет установка библиотеки Libgpiod и работа с GPIO используя утилиты, на примере светодиода и кнопки. Практическая часть выполняется на одноплатном компьютере Banana Pi BPI-M64, ОС Armbain.
Оглавление
- Постановка задачи
- Схема подключения светодиода (LED) и кнопки
- Интерфейс GPIO ядра Linux
- Старый путь: использование виртуальной файловой системы sysfs для доступа к GPIO
- Новый путь: интерфейс chardev
- Библиотека libgpiod и инструменты управления GPIO
- Литература
Постановка задачи
Установить библиотеку libgpiod и утилиты для работы с GPIO. Выполнить демонстрацию работы утилит на примере включения/включения светодиода и получение событий нажатия на кнопку.
- Плата Banana Pi BPI-M64 установлена версия Armbian_21.02.1_Bananapim64_bionic_current_5.10.12_minimal.img.xz, основанная на Ubuntu 18.04.5 LTS (Bionic Beaver), ядро Linux 5.10.12. uname: Linux bananapim64 5.10.12-sunxi64 #21.02.1 SMP Wed Feb 3 20:42:58 CET 2021 aarch64 aarch64 aarch64 GNU/Linux.
Схема подключения светодиода (LED) и кнопки
Подключим светодиод и кнопку на 40-контактный разъем совместимый с Raspberry Pi 3. Светодиод подключаем к №33 контакту разъема, название контакта «PB4», номер линии — 36. Кнопка подключим к №35 контакту разъема, название контакта «PB6», номер линии — 38. Необходимо обратить внимание на поддержку прерывания на контакте «PB6» для кнопки. Поддержка прерывания необходима для исключения постоянного опроса линии с помощью CPU. На контакте «PB6» доступно прерывание «PB_EINT6», поэтому кнопку к этому контакту и подключим. Например, соседний контакт «PL12» не имеет прерывание, поэтому подключать кнопку к нему кнопку не будем. К разъему совместимого с Raspberry Pi 3, подключается светодиод и кнопка от DFROBOT. Если вы подключаете кнопку и светодиод напрямую, то не забывайте в цепь добавлять резистор для сопротивления во избежания выгорания порта! Схема распиновки контактов доступна в публикации Распиновка GPIO для Banana Pi BPI-M64.
Схема подключения светодиода (LED) и кнопки к 40-контактному разъему совместимый с Raspberry Pi 3.
Схема назначения контактов к которым подключается светодиод (LED) и кнопка.
Интерфейс GPIO ядра Linux
GPIO (General-Purpose Input/Output) является одним из наиболее часто используемых периферийных устройств во встраиваемых системах (embedded system) Linux.
Во внутренней архитектуре ядро Linux реализует доступ к GPIO через модель производитель/потребитель. Существуют драйверы, которые предоставляют доступ к линиям GPIO (драйверы контроллеров GPIO) и драйверы, которые используют линии GPIO (клавиатура, сенсорный экран, датчики и т. д.).
В ядре Linux система gpiolib занимается регистрацией и распределением GPIO. Эта структура доступна через API как для драйверов устройств, работающих в пространстве ядра (kernel space), так и для приложений пользовательского пространства (user space).
Старый путь: использование виртуальной файловой системы sysfs для доступа к GPIO
До версии ядра Linux 4.7 для управления GPIO в пользовательском пространстве использовался интерфейс sysfs. Линии GPIO были доступны при экспорте по пути /sys/class/gpio . Более подробно почитать про интерфейс sysfs в публикации Работа с GPIO на примере Banana Pi BPI-M64. Часть 1. Интерфейс sysfs LED и DS18B20. Так, например, для подачи сигнала «0» или «1» на линию GPIO, необходимо:
- Определить номер линии (или номер ножки процессора) GPIO;
- Экспортировать номер GPIO, записав его номер в /sys/class/gpio/export ;
- Конфигурировать линию GPIO как вывод, указав это в /sys/class/gpio/gpioX/direction ;
- Установить значение «1» или «0» для линии GPIO /sys/class/gpio/gpioX/value ;
Для наглядности установим для линии GPIO 36 (подключен светодиод) из пользовательского пространства, значение «1». Для этого необходимо выполнить команды:
$ echo 36 > /sys/class/gpio/export $ echo out > /sys/class/gpio/gpio36/direction $ echo 1 > /sys/class/gpio/gpio36/value
Этот подход очень простой как и интерфейс sysfs, он неплохо работает, но имеет некоторые недостатки:
- Экспорт линии GPIO не связан с процессом, поэтому если процесс использующий линию GPIO аварийно завершит свою работу, то эта линия GPIO так и останется экспортированной;
- Учитывая первый пункт, возможен совместный доступ к одной и той же линии GPIO, что приведет к проблеме совместного доступа. Процесс не может «узнать» у ОС используется ли та или иная линия GPIO в настоящий момент;
- Для каждой линии GPIO приходится выполнять множество операций open()/read()/write()/close(), а так же указывать параметры (export, direction, value, и т.д.) используя методы работы с файлами. Это усложняет программный код, и замедляет скорость работы программы;
- Невозможно включить/выключить сразу несколько линий GPIO одним вызовом;
- Процесс опроса для перехвата событий (прерываний от линий GPIO) ненадежен;
- Нет единого интерфейса (API) для конфигурирования линий GPIO;
- Номера, присвоенные линиям GPIO непостоянны, их приходится каждый раз экспортировать;
- Низкая скорость работы с линиями GPIO;
Новый путь: интерфейс chardev
Начиная с ядра Linux версии 4.8 интерфейс GPIO sysfs объявлен как «deprecated» и не рекомендуется к использованию. На замену sysfs появился новый интерфейс API, основанный на символьных устройствах для доступа к линиям GPIO из пользовательского пространства.
Каждый контроллер GPIO (gpiochip) будет иметь символьное устройство в разделе /dev , и мы можем использовать файловые операции (open(), read(), write(), ioctl(), poll(), close()) для управления и взаимодействия с линиями GPIO. контроллеры GPIO доступны по путям /dev/gpiochipN или /sys/bus/gpiochipN , где N — порядковый номер чипа. Просмотр доступных контроллеров GPIO (gpiochip) на Banana Pi BPI-M64:
root@bananapim64:~# ls /dev/gpiochip* /dev/gpiochip0 /dev/gpiochip1 /dev/gpiochip2
Стек работы библиотеки libgpiod.
Несмотря на то, что новый API предотвращает управление линиями GPIO с помощью стандартных инструментов командной строки, таких как echo и cat, он обладает весомыми преимуществами по сравнению с интерфейсом sysfs, а именно:
- Выделение линий GPIO связано с процессом, который он его использует. При завершении процесса, так же в случае аварийного завершения, линии GPIO используемые процессом автоматически освобождаются;
- Дополнительно, можно всегда определить какой процесс в данное время использует определенную линию GPIO;
- Можно одновременно читать и писать в несколько линий GPIO одновременно;
- Контроллеры GPIO и линии GPIO можно найти по названию;
- Можно настроить состояние вывода контакта (open-source, open-drain и т. д.);
- Процесс опроса для перехвата событий (прерывания от линий GPIO) надежен.
Библиотека libgpiod и инструменты управления GPIO
Для использования нового интерфейса символьного устройства есть библиотека и набор инструментов, предоставляемых проектом libgpiod.
Libgpiod (Library General Purpose Input/Output device) предоставляет набор API для вызова из своих программ и несколько утилит для управления линиями GPIO из пользовательского режима.
В состав libgpiod входят следующие утилиты:
- gpiodetect — выведет список всех чипов GPIO, их метки и количество линий;
- gpioinfo — выведет информацию о линиях GPIO конкретного контроллера GPIO. В таблице вывода по колонкам будет указано: номер линии, название контакта, направление ввода/вывода, текущее состояние;
- gpioget — считает текущее состояние линии GPIO;
- gpioset — установит значение для линии GPIO;
- gpiomon — осуществляет мониторинг состояния линии GPIO и выводит значение при изменение состояния;
- gpionotify — ожидает определенное состояние линии GPIO и выводит статус изменение в консоль.
void main() { struct gpiod_chip *chip; struct gpiod_line *line; int req, value; chip = gpiod_chip_open("/dev/gpiochip0"); if (!chip) return -1; line = gpiod_chip_get_line(chip, 3); if (!line) { gpiod_chip_close(chip); return -1; } req = gpiod_line_request_input(line, "gpio_state"); if (req) { gpiod_chip_close(chip); return -1; } value = gpiod_line_get_value(line); printf("GPIO value is: %d\n", value); gpiod_chip_close(chip); }
Библиотеку можно вызывать так же и из кода на C++, Python, C#, Rust и т.д.
Для управления линиями GPIO из терминала необходимо использовать инструменты командной строки, предоставляемые libgpiod. Библиотеку libgpiod и инструменты управления GPIO можно установить из пакетов, бинарников, или скомпилировать из исходного текста.
Установка библиотеки libgpiod и инструментов управления GPIO
Репозитарий библиотеки libgpiod доступен по адресу libgpiod/libgpiod.git. В разделе Download опубликованы релизы библиотеки. На 25.07.2024 последний релиз: v2.1.2
Начиная с версии 2.0 библиотеки libgpiod требуется интерфейс uAPI v2 GPIO символьных устройств, который впервые был добавлен в ядро Linux 5.10.
Библиотека libgpiod установливается из репозитария дистрибутива, но скорее всего, будет доступна старая версия (в большинстве случаев это будет версия v1.6.3):
$ sudo apt-get update $ sudo apt-get install -y libgpiod-dev gpiod
Для установки последней актуальной версии необходимо выполнить скрипт установки. Доступна установки из скомпилированных бинарников для вашей ОС и установка из исходного текста.
В репозитарии devdotnetorg/docker-libgpiod присутствуют два скрипта:
- setup-libgpiod.sh — установка библиотеки libgpiod из репозитория, бинарников, исходного текста;
- remove-libgpiod.sh — удаление библиотеки libgpiod в не зависимости от варианта установки.
Вызов скрипта setup-libgpiod.sh без указания аргументов позволит установить библиотеку libgpiod в интерактивном режиме.
Скрипт установки библиотеки libgpiod и утилит для x86/ARM32/ARM64/RISC-V:
$ sudo apt-get update $ sudo apt-get install -y curl $ curl -SL --output setup-libgpiod.sh https://raw.githubusercontent.com/devdotnetorg/docker-libgpiod/HEAD/setup-libgpiod.sh $ chmod +x setup-libgpiod.sh $ sudo ./setup-libgpiod.sh
Рекомендуется вариан установки из бинарников (4) Installation from binaries . Для вашей ОС загружается архив *.zip, затем содержимое архива распаковывается в системные директории. Все архивы располагаются в папке /out.
Бинарники скомпилированы для следующих ОС:
- Alpine 3.15, 3.16, 3.17, 3.18, 3.19, 3.20;
- Debian 11, 12;
- Ubuntu 18.04, 20.04, 22.04, 23.10, 24.04.
Библиотека libgpiod компилируется со следущими аргументами:
--enable-tools=yes \ --enable-bindings-cxx \ --enable-bindings-python \ ac_cv_func_malloc_0_nonnull=yes
Что включается в себя поддержку вызова функций из C++ и Python. Поддержка Rust не включена в компиляцию, будет добавлена позже.
Как самостоятельно собрать библиотеку для разных ОС рассказано в публикации Кросс-компиляция проекта в Docker используя Buildx на примере сборки shadowsocks-rust и библиотеки Libgpiod.
Если архив для вашей системы имеет название типа «libgpiod-bin-2.0-ubuntu-18.04-aarch64-without_support_python_and_cxx», то это говорит о том, что в данной версии отсутствует поддержка Python и C++, в основном это старые версии Ubuntu 18.04 и 20.04. Если название архива заканчивается на «-without_support_python.zip», то поддержка C++ присутствует без поддержки Python.
По итогу выполнения скрипта должна появиться надпись «Successfully», это означает что библиотека и утилиты успешно установлены. Дополнительно для проверки, можно вызвать команду с выводом номера версии библиотеки:
root@bananapim64:~# gpiodetect -v gpiodetect (libgpiod) v2.1.2 Copyright (C) 2017-2023 Bartosz Golaszewski License: GPL-2.0-or-later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
Для удаления библиотеки предназначен скрипт: remove-libgpiod.sh.
Дополнительные аргументы скрипта setup-libgpiod.sh
Если необходимо установить конкретную версию библиотеки libgpiod из бинарников без каких либо вопросов, например подойдет для создания контейнера, то необходимо выполнить команду:
$ sudo ./setup-libgpiod.sh --type binary --version 2.1 --canselect no
Остальные аргументы описаны в заголовке скрипта установки.
Инструменты библиотеки libgpiod
Команда gpiodetect выведет список всех чипов GPIO, их метки и количество линий. Результат выполнения команды:
root@bananapim64:~# gpiodetect gpiochip0 [1f02c00.pinctrl] (32 lines) gpiochip1 [1c20800.pinctrl] (256 lines) gpiochip2 [axp20x-gpio] (2 lines)
gpiochip0 и gpiochip1, это чипы входящие в состав SoC Allwinner A64. gpiochip1 имеет выход на 40-контактный разъем совместимый с Raspberry Pi. Чип gpiochip2 — отдельная микросхема управления электропитанием axp209 подключенная по интерфейсу I2C.
Для вывода справки к вызываемой команде необходимо добавлять параметр «—help». Вызов справки для команды gpiodetect. Результат выполнения команды:
root@bananapim64:~# gpiodetect --help Usage: gpiodetect [OPTIONS] [chip]... List GPIO chips, print their labels and number of GPIO lines. Chips may be identified by number, name, or path. e.g. '0', 'gpiochip0', and '/dev/gpiochip0' all refer to the same chip. If no chips are specified then all chips are listed. Options: -h, --help display this help and exit -v, --version output version information and exit
Команда gpioinfo выведет информацию о линиях GPIO конкретного контроллера GPIO (или всех контроллеров GPIO, если они не указаны). Результат выполнения команды:
root@bananapim64:~# gpioinfo --chip 1 gpiochip1 - 256 lines: line 0: unnamed unused input active-high ... line 64: unnamed "dc" output active-high [used] ... line 68: unnamed "backlightlcdtft" output active-high [used] ... line 96: unnamed "spi0 CS0" output active-low [used] line 97: unnamed unused input active-high line 98: unnamed unused input active-high line 99: unnamed unused input active-high line 100: unnamed "reset" output active-low [used] ... line 120: unnamed "bananapi-m64:red:pwr" output active-high [used] ... line 254: unnamed unused input active-high line 255: unnamed unused input active-high
В таблице по колонкам указано: номер линии, название контакта, направление ввода/вывода, текущее состояние. Сейчас к Banana Pi BPI-M64 подключен LCD экран ILI9341 на SPI интерфейсе, для подключения используется вариант с управляемой подсветкой, файл DTS sun50i-a64-spi-ili9341-backlight-on-off.dts. В DTS файле контакт «PC4» GPIO68 обозначен для управления подсветкой, название «backlightlcdtft». Соответственно в выводе команды, указан номер линии 68, название «backlightlcdtft», направление — вывод, текущее состояние — active-high (включено).
Команда gpioset установит значение для линии GPIO. Например, следующая команда попытается выключит подсветку на LCD ILI9341. Команда: gpioset —hold-period 200ms -t0 —chip 1 68=0 , где 1 — gpiochip1, 68 — номер линии(контакта), 0 — логическое значение, может быть «0» или «1», 200ms — время удержания линии. Результат выполнения команды:
root@bananapim64:~# gpioset --banner --hold-period 200ms -t0 --chip 1 68=0 gpioset: unable to request lines on chip '/dev/gpiochip1': Device or resource busy
В результате мы получим ошибку — линия занята, т.к. данная линия занята драйвером «gpio-backlight».
Попробуем включить подключенный светодиод на линии 36, название «PB4», номер контакта на 40-контактный разъем совместимый с Raspberry Pi №33. Результат выполнения команды:
root@bananapim64:~# gpioset --chip 1 36=1 gpioset --banner --hold-period 200ms -t0 --chip 1 36=1
В результате выполнения команды, светодиод включится.
Команда gpioget считывает текущее состояние линии GPIO. Результат выполнения команды:
root@bananapim64:~# gpioget --chip 1 36 1
Получили значение «1», т.к. до этого включили светодиод командой gpioset.
Команда gpiomon осуществляет мониторинг состояния линии GPIO и выводит значение при изменение состояния. Будем мониторить состояние кнопки, которая подключена на линию 38, название «PB4», номер контакта на 40-контактный разъем совместимый с Raspberry Pi №35. Команда: gpiomon —chip 1 38 , где 1 — gpiochip1, 38 — номер линии (контакта). Результат выполнения команды:
root@bananapim64:~# gpiomon --chip 1 38 631.682345039 rising gpiochip1 38 632.836326153 falling gpiochip1 38 634.400444509 rising gpiochip1 38 635.585806741 falling gpiochip1 38
Кнопка несколько раз нажималась. RISING — повышение, изменение напряжения с 0V до 3.3V, кнопка нажата и удерживается состояние. FALLING — понижение, изменение напряжения с 3.3V до 0V, происходит отпускание кнопки, и кнопка переходит в состояние «не нажата».
С механической кнопкой возникла проблема из-за дребезга контакта, регистрировались множественные нажатия вместо одного. Поэтому механическая кнопка была заменена на емкостную (touch) кнопку.
GitHub — devdotnetorg/docker-libgpiod.
Читайте также:
- Управляем контактами GPIO в Linux из Docker-контейнера библиотекой Libgpiod
- .NET IoT. Часть 2. Мигаем светодиодом (LED) используя библиотеку Libgpiod
- Программирование на Python и установка Docker для Sipeed Lichee RV RISC-V
Литература
- Libgpiod/libgpiod.git — Bartosz Golaszewski git.kernel.org
- About libgpiod — Bartosz Golaszewski git.kernel.org
- GitHub devdotnetorg/docker-libgpiod
- Linux kernel GPIO user space interface — Sergio Prado embeddedbits.org
- An Introduction to chardev GPIO and Libgpiod on the Raspberry PI — Craig Peacock BeyondLogic
- Manage GPIO lines with gpiod — Sergio Tanzilli acmesystems.it
- Gpiod library API — pub.dev
- New Linux Kernel 5.5 – New Interfaces in gpiolib — Matheus Castello microhobby.com.br