Настоящая статья посвящена вопросу программного управлению GPIO через системные вызовы к драйверу символьных устройств и познакомит вас с возможностью использования библиотеки libgpiod2 на Repka Pi 3. В статье приведены рекомендации по настройке ОС для использования этой библиотеки. Результаты тестовой эксплуатации libgpiod2 на Repka Pi 3 обобщены в виде справочной таблицы. В заключении статьи приводятся примеры использования инструментов управления GPIO из пользовательского интерфейса.
Общие сведения #
GPIO (General-Purpose Input/Output) интерфейс ввода/вывода общего назначения чаще всего используемый на платах микроконтроллеров и одноплатных компьютеров для программного управления цифровыми сигналами.
Во внутренней архитектуре ядро Linux реализует доступ к GPIO через модель производитель/потребитель. Существуют драйверы, которые предоставляют доступ к линиям GPIO (драйверы контроллеров GPIO) и драйверы, которые используют линии GPIO (клавиатура, сенсорный экран, датчики и т. д.).
Основные особенности GPIO:
- Контакты GPIO могут быть сконфигурированы для ввода или вывода;
- Контакты GPIO могут быть включены / выключены;
- Контакты для ввода доступны для чтения значения;
- Контакты для вывода доступны для записи / чтения значения;
- Входные значения часто могут использоваться в качестве IRQs (обычно для событий пробуждения).
В ядре Linux система gpiolib занимается регистрацией и распределением GPIO. Эта структура доступна через API как для драйверов устройств, работающих в пространстве ядра (kernel space), так и для приложений пользовательского пространства (user space).
В Linux (в том числе и в RepkaOS) доступ к аппаратным устройствам осуществляется пользователем через специальные файлы устройств. Эти файлы сгруппированы в каталоге /dev, а системные вызовы перенаправляются операционной системой на драйвер устройства, связанного с физическим устройством. Драйвер устройства - это компонент ядра ОС Linux (обычно модуль), который взаимодействует с аппаратным устройством.
В Linux существуют две категории файлов устройств, и следовательно, драйверов устройств: символьные и блочные. К первой категории относятся медленные устройства, которые управляют небольшим объемом данных, а доступ к данным не требует частых запросов seek. Примерами могут служить такие устройства, как клавиатура, мышь, последовательные порты, звуковая карта, джойстик и т.д. В общем, операции с этими устройствами (чтение, запись) выполняются последовательно, байт за байтом. Ко второй категории относятся устройства, где объем данных велик, данные организованы по блокам, а поиск является обычным делом. Примерами таких устройств являются: жесткие диски, CD-ROM, диски в оперативной памяти и т.д. Для этих устройств чтение и запись выполняются на уровне блоков данных.
Для двух типов драйверов устройств ядро Linux предлагает разные API. Для символьных устройств используются системные вызовы (ioctl) непосредственно к драйверам устройств. Как правило, системные вызовы к драйверам устройств чаще всего используются в системном программировании, а для разработки приложений пользовательского режима используются библиотеки-обвертки. Одной из таких библиотек-обверток вокруг системных вызовов драйвера символьного устройства GPIO является библиотека libgpiod2.
Примечание: сообществом разработчиков ядра Linux, см. официальную документацию, рекомендуется использовать библиотеку libgpiod2 для разработки нативных и управляемых виртуальной машиной приложений.
Библиотека libgpiod2 #
Libgpiod (Library General Purpose Input/Output device) предоставляет набор API для вызова из своих программ и несколько утилит для управления линиями GPIO из пользовательского режима.
Контроллер GPIO (gpiochip) представляет собой символьное устройство, отображается в разделе /dev. Предназначен для управления и взаимодействия с линиями GPIO. Контроллеры GPIO отображаются в каталог /dev/gpiochipN или /sys/bus/gpiochipN , где N — порядковый номер чипа.
Линия общего назначения контроллера GPIO (line) управляется контролером GPIO, сопоставлена с физическим контактом GPIO-разъема. Управление сигналами на контактах осуществляется через запись в линию или чтение из нее.
Библиотека libgpiod2 инкапсулирует вызовы ioctl и структуры данных за простым API. Библиотека разработана для замены доступа к GPIO через виртуальную файловую систему sysfs, так как начиная версии 4.8 ядра Linux интерфейс GPIO sysfs объявлен как устаревший (deprecated). Используемый в библиотеке новый интерфейс chardev гарантирует, что все выделенные ресурсы будут освобождены после закрытия файлового дескриптора устройства, и добавляет несколько новых функций, которых нет в устаревшем интерфейсе sysfs (например, опрос событий, установка/чтение нескольких значений одновременно).
Библиотека libgpiod2 обладает весомыми преимуществами по сравнению с интерфейсом sysfs, а именно:
- При завершении процесса (в том числе аварийного) используемые процессом линии GPIO автоматически освобождаются;
- Предоставляет возможность определить какой процесс в данное время использует определенную линию GPIO;
- Доступно одновременное чтение и запись в несколько линий GPIO;
- Контроллеры и линии GPIO можно найти по названию;
- Позволяет настроить состояние вывода контакта;
- Предоставляет надежные функции опроса прерываний от линий для перехвата событий изменения состояния сигнала.
В RepkaOS 1.0.14 (от 06.10.2023) библиотека libgpiod2 версии 1.3 установлена “из коробки”, но для начала работы с GPIO в пользовательском режиме необходимо дополнительно установить пакет gpiod. А использование библиотеки для разработки программ станет возможно только после установки пакета libgpiod-dev. Библиотека libgpiod2 может быть использована при написании программ на ЯП: C, C++, C#, Java (Kotlin), Python. В настоящее время разработчики библиотеки работают над реализацией ее поддержки в ЯП Rust.
Для полной установки libgpiod2 из общего репозитория в RepkaOS введите команду:
sudo apt install libgpiod2 gpiod libgpiod-dev
Для удаления:
sudo apt remove libgpiod2 gpiod libgpiod-dev
Для установки более новых бинарных версий версий библиотеки (актуальная версия 2.1) можно воспользоваться скриптом setup-libgpiod.sh, а для ее удаления remove-libgpiod.sh.
Для прямого доступа к линиям GPIO в RepkaOS пользователь должен обладать правами администратора (root) или получать привилегии администратора через команду sudo при работе с линиями GPIO.
Примечание: если вы не создавали нового пользователя и пользуетесь предустановленной учетной записью (root), то можете пропустить эту настройку.
-
Создадим группу gpiod и добавим в нее обычного пользователя user (не обладающего административными правами, пользователь должен быть зарегистрирован в ОС заранее):
sudo groupadd gpiod sudo usermod -G gpiod user
-
Разрешим пользователям группы gpiod управлять контроллерами GPIO № 0 и 1, создадим файл 60-gpiod.rules в каталоге /etc/udev/rules.d следующего содержания:
#udev rules for gpio port access SUBSYSTEM=="gpio", KERNEL=="gpiochip[0-1]", GROUP="gpiod", MODE="0660"
-
Перезагрузите устройство:
sudo reboot
Особенности управления GPIO из пользовательского режима #
Прежде чем начать работу с инструментами рассмотрим справочную таблицу (рис. 1), которая пригодится нам для работы с линиями GPIO.
Таблица составлена на основе опытного тестирования программного управления GPIO из приложения на ЯП Python с использованием библиотеки libgpiod2 версии 1.3. В процессе исследования проверялось:
- переключение режимов работы линий ввод/вывод;
- программное включение резистора для подтяжки линии вверх и вниз;
- доступность обработчиков событий изменения состояния сигнала (высокое / низкое) в линии с программным включением резистора подтяжки вверх и вниз.
Примечание:
- использовался первый вариант распиновки 40 pin разъема;
- тестирования линий 4 и 5 контроллера gpiochip1 осуществлялось при выключенном выводе сообщений на UART0 (по умолчанию включена);
- подтяжка вниз на линиях, сопоставленных с пинами 3, 5, 10, 27 и 28 GPIO работает как подтяжка вверх, соответственно обработка событий с подтяжкой вниз не доступна;
- на линиях, сопоставленных с пинами 19, 21, 23 и 24, обработка событий не доступна;
- пороговое напряжения вызова прерывания на изменение состояния сигнала высокое / низкое не измерялось, в виду отсутствия технической возможности;
- состав используемого оборудования при тестировании: Repka Pi 3 1.4 GHz ОЗУ 2 Gb (версия платы 1.4), монитор, клавиатура, мышь, макетная плата, резистор 220 Ом, светодиод, кнопка, комплект проводов, мультиметр.
Рис. 1. Справочная таблица для работы с линиями контроллера GPIO с использованием библиотеки libgpiod2 на Repka Pi 3 (версия прошивки 1.0.8 и выше).
Для управления линиями GPIO через libgpiod2 из пользовательского режима можно использовать следующие утилиты (входят в пакет gpiod):
- gpiodetect — выведет список всех чипов GPIO, их метки и количество линий;
- gpioinfo — выводит информацию о линиях GPIO конкретного контроллера GPIO. Информация выводится в виде таблицы со следующими столбцами: номер линии, название контакта (для Repka Pi 3 к сожалению не заданы), направление ввода/вывода, текущее состояние;
- gpioget — считывает текущее состояние линии GPIO;
- gpioset — устанавливает значение для линии GPIO;
- gpiomon — осуществляет мониторинг состояния линии GPIO и выводит значение при изменение состояния;
- gpionotify — ожидает определенное состояние линии GPIO и выводит статус изменения в консоль.
Примечание: все перечисленные утилиты имеют параметр -h (—help) для вывода списка допустимых параметров, а также man-страницы с подробным описанием.
Тест работоспособности библиотеки libgpiod2 #
Для проверки работоспособности библиотеки libgpiod2 соберем макет схемы с двумя светодиодами (рис. 2).
Рис. 2. Макет схемы для теста (номера контактов: черный - 40, красный - 32, зеленый - 23).
Запросим информацию о доступных в Repka Pi 3 контролерах:
~/gpiodetect
и их линиях:
~/gpioinfo gpiochip0
~/gpioinfo 1
Примечание: в Repka Pi 3 контакты PL 2, 3, 10 и 11 управляются контроллером gpiochip0, все остальные контроллером gpiochip1.
Подадим питание на красный светодиод, подключенный к линии 11 gpiochip0 (контакт № 32)
~/gpioset gpiochip0 11=1
или
~/gpioset 0 11=1
Отключим питание от красного светодиода:
~/gpioset gpiochip0 11=0
или
~/gpioset 0 11=0
Задание для самостоятельного выполнения:
- Повторите операции включения и выключения питания для зеленого светодиода. Для определения номера контроллера и линии, к которой он подключен, используйте справочную таблицу (рис. 1).
- Посмотрите с помощью утилиты
gpioinfo
как изменяется информация о состоянии линии при включении и выключении светодиодов. - Получите значения используемых вами линий с помощью утилиты
gpioget
, до и после включения светодиодов.
Примеры использования библиотеки в различных ЯП #
Ниже приведены примеры для мигания красным светодиодом (см. рис. 2), подключенным к 11 линии gpiochip0.
Python
import gpiod
import sys
import time
LED_CHIP = 0
LED_LINE_OFFSET = 11
chip = gpiod.chip(LED_CHIP)
led = chip.get_line(LED_LINE_OFFSET)
config = gpiod.line_request()
config.consumer = "Blink"
config.request_type = gpiod.line_request.DIRECTION_OUTPUT
led.request(config)
while True:
led.set_value(0)
time.sleep(0.1)
led.set_value(1)
time.sleep(0.1)
Для установки модуля gpiod для Python выполните следующую команду:
pip3 gpiod
С++
// в разделе #include <> заменены на "" из-за некорректного вывода кода
// (пропадают названия заголовочных файлов)
#include "chrono"
#include "cstdlib"
#include "gpiod.hpp"
#include "iostream"
#include "string"
#include "thread"
int main(int argc, char **argv) {
std::string LED_CHIP = "gpiochip0";
int LED_LINE_OFFSET = 11;
gpiod::chip chip(LED_CHIP);
gpiod::line led = chip.get_line(LED_LINE_OFFSET);
gpiod::line_request config;
config.consumer = "Blink";
config.request_type = gpiod::line_request::DIRECTION_OUTPUT;
led.request(config);
while(1) {
led.set_value(0);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
led.set_value(1);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
gcc -o blink blink.o -lgpiod
./blink
C# & .NET 6
dotnet new console dotnet_iot_blink
vim ./dotnet_iot_blink/Program.cs
using System;
using System.Device.Gpio;
using System.Device.Gpio.Drivers;
using System.Threading;
namespace dotnet_iot_blink
{
class Program
{
static void Main(string[] args)
{
const int GPIOCHIP = 0;
const int LED_PIN = 11;
GpioController controller;
var drvGpio = new LibGpiodDriver(GPIOCHIP);
controller = new GpioController(PinNumberingScheme.Logical, drvGpio);
controller.OpenPin(LED_PIN, PinMode.Output);
bool ledOn = true;
while (true)
{
controller.Write(LED_PIN, ((ledOn) ? PinValue.High : PinValue.Low));
Thread.Sleep(100);
ledOn = !ledOn;
}
}
}
}
cd ./dotnet_iot_blink
dotnet run
Для работы на Repka Pi 3 с GPIO в .NET 6 и выше рекомендуется обновить библиотеку libgpiod-dev до версии 1.6.3.
Примечание: из личного опыта, .NET 6 отлично работает с libgpiod-dev версии 1.3, установленной в RepkaOS 1.4 по умолчанию.
Заключение #
В следующих статьях будут опубликованы учебные материалы по управлению GPIO на Repka Pi 3 с примерами на ЯП Python в связке с библиотекой libgpiod2. В частности, рассмотрим вопросы настройки линий для вывода сигнала, помигаем светодиодами и решим несколько интересных задач с ними.
Это мега крутая статья!
Лучший пятничный подарок проекту Репка! Спасибо Вам большое!!!!
Грех не помочь такому смелому проекту, тем более отечественному. Главное чтобы впрок пошло.
Добавлен раздел "Примеры использования библиотеки в различных ЯП"
Скажите, а в чём код писать то? Не vscode же на репку ставить! И... я ведь могу прям на репке писать? Вопросы наверно глупые, я второй день с ней вожусь.
ЗЫ. накатил на репку alt linux, т.к. на ноуте он же
Я пишу в VS Code (непосредственно на Репке, 1,4 гГц, 2 Гб ОЗУ). Можно использовать предустановленный Geany. Если планируете писать на Python, то можно установить Thonny из репозитория, он адаптирован для одноплатников. Во второй части статьи (п. 4.1, https://repka-pi.ru/blog/post/59), описан порядок настройки VS Code (в примечаниях). Если в двух словах, после установки нужно отключить аппаратное ускорение графики, так как из-за него некорректно работает отображение текста в терминале.
Спасибо! Меня как раз больше плюсы и шарп интересует. Пойду пытаться! :)
Дополнительно по VS Code. Расширения для Python и C/C++ (в т.ч. CMake) работают без нареканий. Для работы с C# нужно ставить расширения C# (base language support), который за собой тянет .NET Install Tool тянет за собой .NET 7 Runtime. Настоятельно (пока) не рекомендую ставить C# Dev Kit и IntelliCode for C# Dev Kit, так как они очень ресурсоемкие, даже не все настольники с ними справляются. P.s. последние 2 расширения используются для интеллектуального анализа кода и умных подсказок (не путать с обычным IntelliSense, который включен в первое расширение).
Для того, чтобы gpiod заработал "без рута" сделал:
sudo chmod a+rw /dev/gpiochip0
так как ругался что нет прав...
Как один из вариантов подойдет, но у него есть минус - после перезагрузки придется это делать снова... лучше воспользоваться способом описанным в подразделе "Линия общего назначения контроллера GPIO (line) " настоящей статьи
Для С++ правильно (у меня только так заработало)
g++ -o name name.cpp -lgpiodcxx (name - имя файла)