Работа с GPIO в Linux. Часть 6. Библиотека Libgpiod

В 2017 году Bartosz Golaszewski начал разработку библиотеки Libgpiod и утилит для работы с GPIO. Библиотека libgpiod инкапсулирует вызовы ioctl и структуры данных за простым API в Linux, и предназначена для замены доступа к GPIO через виртуальную файловую систему sysfs. Используемый в библиотеке новый интерфейс chardev гарантирует, что все выделенные ресурсы будут освобождены после закрытия файлового дескриптора устройства, и добавляет несколько новых функций, которых нет в устаревшем интерфейсе sysfs (например, опрос событий, установка/чтение нескольких значений одновременно). В публикации будет установка библиотеки Libgpiod из исходного текста и работа с GPIO через утилиты на примере светодиода и кнопки. Практическая часть будет выполнятся на одноплатном компьютере Banana Pi BPI-M64, ОС Armbain.

Оглавление

  1. Постановка задачи
  2. Схема подключения светодиода (LED) и кнопки
  3. Интерфейс GPIO ядра Linux
  4. Старый путь: использование виртуальной файловой системы sysfs для доступа к GPIO
  5. Новый путь: интерфейс chardev
  6. Библиотека libgpiod и инструменты управления GPIO
  7. Литература

Постановка задачи

Установить библиотеку libgpiod и утилиты для работы с GPIO из исходных текстов. Выполнить демонстрацию работы утилит на примере включения/включения светодиода и получение событий нажатия на кнопку.

Схема подключения светодиода (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
libgpiod Armbian

Схема назначения контактов к которым подключается светодиод (LED) и кнопка
libgpiod Armbian

Интерфейс GPIO ядра Linux

GPIO (General-Purpose Input/Output) является одним из наиболее часто используемых периферийных устройств во встраиваемых системах (embedded system) Linux.

Во внутренней архитектуре ядро Linux реализует доступ к GPIO через модель производитель/потребитель. Существуют драйверы, которые предоставляют доступ к линиям GPIO (драйверы контроллеров GPIO) и драйверы, которые используют линии GPIO (клавиатура, сенсорный экран, датчики и т. д.).

В ядре Linux система gpiolib занимается регистрацией и распределением GPIO. Эта структура доступна через API как для драйверов устройств, работающих в пространстве ядра (kernel space), так и для приложений пользовательского пространства (user space).

Схема работы gpiolib
libgpiod Armbian

Старый путь: использование виртуальной файловой системы sysfs для доступа к GPIO

До версии ядра Linux 4.7 для управления GPIO в пользовательском пространстве использовался интерфейс sysfs. Линии GPIO были доступны при экспорте по пути  /sys/class/gpio . Более подробно почитать про интерфейс sysfs в публикации Работа с GPIO на примере Banana Pi BPI-M64. Часть 1. Интерфейс sysfs LED и DS18B20. Так, например, для подачи сигнала «0» или «1» на линию GPIO, необходимо:

  1. Определить номер линии (или номер ножки процессора) GPIO;
  2. Экспортировать номер GPIO, записав его номер в  /sys/class/gpio/export ;
  3. Конфигурировать линию GPIO как вывод, указав это в  /sys/class/gpio/gpioX/direction ;
  4. Установить значение «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, он неплохо работает, но имеет некоторые недостатки:

  1. Экспорт линии GPIO не связан с процессом, поэтому если процесс использующий линию GPIO аварийно завершит свою работу, то эта линия GPIO так и останется  экспортированной;
  2. Учитываю первый пункт возможен совместный доступ к одной и той же линии GPIO, что приведет к проблеме совместного доступа. Процесс не может «узнать» у ОС используется ли та или иная линия GPIO в настоящий момент;
  3. Для каждой линии GPIO приходится выполнять множество операций open()/read()/write()/close(), а так же указывать параметры (export, direction, value, и т.д.) используя методы работы с файлами. Это усложняет программный код, и замедляет скорость работы программы;
  4. Невозможно включить/выключить сразу несколько линий GPIO одним вызовом;
  5. Процесс опроса для перехвата событий (прерываний от линий GPIO) ненадежен;
  6. Нет единого интерфейса (API) для конфигурирования линий GPIO;
  7. Номера, присвоенные линиям GPIO непостоянны, их приходится каждый раз экспортировать;
  8. Низкая скорость работы с линиями 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
libgpiod Armbian

Несмотря на то, что новый 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;
  • gpiofind — выполняет поиск контроллера GPIO и линии по имени;
  • gpiomon — осуществляет мониторинг состояния линии GPIO и выводит значение при изменение состояния.

Например, следующая программа написанная на C использует libgpiod для чтения строки 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#, и т.д.

Для управления линиями GPIO из терминала необходимо использовать инструменты командной строки, предоставляемые libgpiod. Библиотеку libgpiod и инструменты управления GPIO можно установить скомпилировать из исходного текста и установить.

Установка библиотеки libgpiod и инструментов управления GPIO

Репозитарий библиотеки libgpiod доступ по адресу libgpiod/libgpiod.git. В разделе Download опубликованы релизы библиотеки. На 28.04.2021 последний релиз: v1.6.3.

Библиотеку libgpiod можно установить из репозитария дистрибутива, но скорее всего будет доступна старая версия:

$ sudo apt-get update
$ sudo apt-get install -y libgpiod-dev gpiod

Для установки последней актуальной версии необходимо выполнить скрипт установки. В строке вызова скрипта установки  setup-libgpiod-arm64.sh , в качестве первого параметра указать номер версии библиотеки (например: 1.6.3), второй параметр (необязательный) — папка установки скрипта. По умолчанию библиотека установится по пути:  /usr/share/libgpiod .

Скрипт установки из исходного текста библиотеки libgpiod и утилит для ARM32/ARM64:

$ cd ~/
$ sudo apt-get update
$ sudo apt-get install -y curl 
$ curl -SL --output setup-libgpiod-armv7-and-arm64.sh https://raw.githubusercontent.com/devdotnetorg/dotnet-libgpiod-linux/master/setup-libgpiod-armv7-and-arm64.sh
$ chmod +x setup-libgpiod-armv7-and-arm64.sh
$ sudo ./setup-libgpiod-armv7-and-arm64.sh 1.6.3

Для удаления библиотеки выполнить скрипт: remove-libgpiod-armv7-and-arm64.sh

Если по итогу выполнения скрипта появится надпись «Successfully», то значит библиотека и утилиты успешно установлены. Дополнительно для проверки, можно вызвать команду с выводом номера версии библиотеки:

root@bananapim64:~# gpiodetect -v
gpiodetect (libgpiod) v1.6.3
Copyright (C) 2017-2018 Bartosz Golaszewski
License: LGPLv2.1
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Инструменты библиотеки 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]

List all GPIO chips, print their labels and number of GPIO lines.

Options:
  -h, --help:           display this message and exit
  -v, --version:        display the version and exit

Команда gpioinfo выведет информацию о линиях GPIO конкретного контроллера GPIO (или всех контроллеров GPIO, если они не указаны). Результат выполнения команды:

root@bananapim64:~# gpioinfo 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 1 68=0 , где 1 — gpiochip1, 68 — номер линии(контакта), 0 — логическое значение, может быть «0» или «1». Результат выполнения команды:

root@bananapim64:~# gpioset 1 68=0
gpioset: error setting the GPIO line values: Device or resource busy
root@bananapim64:~#

В результате мы получим ошибку — линия занята, т.к. данная линия занята драйвером «gpio-backlight».

Попробуем включить подключенный светодиод на линии 36, название «PB4», номер контакта на 40-контактный разъем совместимый с Raspberry Pi №33. Результат выполнения команды:

root@bananapim64:~# gpioset 1 36=1

В результате выполнения команды, светодиод включится.

Команда gpioget считывает текущее состояние линии GPIO. Результат выполнения команды:

root@bananapim64:~# gpioget 1 36
1

Получили значение «1», т.к. до этого включили светодиод командой gpioset.

Команда gpiomon будет осуществлять мониторинг состояния линии GPIO и выводить значение при изменение состояния. Будем мониторить состояние кнопки, которая подключена на линию 38, название «PB4», номер контакта на 40-контактный разъем совместимый с Raspberry Pi №35. Команда:  gpiomon 1 38 , где 1 — gpiochip1, 38 — номер линии (контакта). Результат выполнения команды:

root@bananapim64:~# gpiomon 1 38
event:  RISING EDGE offset: 38 timestamp: [     122.943878429]
event: FALLING EDGE offset: 38 timestamp: [     132.286218099]
event:  RISING EDGE offset: 38 timestamp: [     137.639045559]
event: FALLING EDGE offset: 38 timestamp: [     138.917400584]

Кнопка несколько раз нажималась. RISING — повышение, изменение напряжения с 0V до 3.3V, кнопка нажата и удерживается состояние. FALLING — понижение, изменение напряжения с 3.3V до 0V, происходит отпускание кнопки, и кнопка переходит в состояние «не нажата».

С механической кнопкой возникла проблема из-за дребезга контакта, регистрировались множественные нажатия вместо одного. Поэтому механическая кнопка была заменена на емкостную (touch) кнопку.

Литература

  1. Libgpiod/libgpiod.git — Bartosz Golaszewski git.kernel.org
  2. About libgpiod — Bartosz Golaszewski git.kernel.org
  3. Linux kernel GPIO user space interface — Sergio Prado embeddedbits.org
  4. An Introduction to chardev GPIO and Libgpiod on the Raspberry PI — Craig Peacock BeyondLogic
  5. Manage GPIO lines with gpiod — Sergio Tanzilli acmesystems.it
  6. Gpiod library API — pub.dev
  7. New Linux Kernel 5.5 – New Interfaces in gpiolib — Matheus Castello microhobby.com.br

Вам также может понравиться

About the Author: Anton

Programistik