Gpio linux как работать

Обновлено: 04.07.2024

Что делать, когда нечего делать? Попробовать что-нибудь новое!

Если вы приобрели Raspberry Pi просто ради любопытства, не отдавая себе отчёта в том, для чего он конкретно вам нужен, то наверняка с каждым днём вам становится всё труднее найти для него применение. Вы уже с ним вдоволь наигрались. Попробовали установку разных операционных систем, послушали музыку, посмотрели видео, попробовали поиграть и порисовать… И наверняка с огорчением для себя сделали вывод - «Всё ж таки Raspberry Pi мало годится для использования в качестве настольного компьютера». Слишком он уж медленный и задумчивый, по сравнению с обычным компьютером. И вроде бы ничего серьезного с ним сделать нельзя. Остаётся лишь найти ему применение в качестве либо медиацентра, либо простенького интернет-сервера, который не страшно оставлять включённым круглые сутки…

Но всё ж таки Raspberry Pi может делать одну вещь гораздо более эффективнее, чем любой домашний компьютер- он может управлять внешними устройствами. Устройства могут быть абсолютно любыми, от обычной лампочки, до беспилотного летательного аппарата. В данном случае, область применения Raspberry ограничена лишь вашей фантазией и знаниями. И если вы никогда и ничего подобного не делали, но это вас заинтересовало, то эта статья для вас. И так, начнём.

Чтобы общаться с любыми внешними устройствами и управлять ими, Raspberry Pi имеет на борту интерфейс, называемый GPIO. Это аббревиатура от General Purpose Input Output. А по-русски, это низкоуровневый интерфейс ввода-вывода прямого управления. На плате Raspberry он находится в углу, в виде гребёнки из 26 штырьков, рядом с видеовыходом. Т.е. через этот интерфейс Raspberry может слушать и отдавать команды любому внешнему устройству, например беспилотнику. Но сегодня мы беспилотник строить не будем, начнём с обычной лампочки, а точнее светодиода, который и исполнит роль подопытной лампочки. Наша задача - заставить светодиод, подключённый к Raspberry включаться и выключаться по его команде. Кроме того, дабы убедиться, что эти включения происходят вполне осознано и так, как мы этого хотим, а не благодаря каким-то глюкам в недрах процессора, мы привнесём в нашу программу элемент общения с нами. Т.е. отстроим чёткую иерархию- Raspberry управляет светодиодом, а самим Raspberry управляем мы. Теперь надо подготовиться и раздобыть где-то несколько вещей.

Во-первых, нужно найти светодиод:


Его можно достать из старой сломанной игрушки, из зажигалки с фонариком, попросить у знакомого радиоэлектронщика, в конце концов, просто купить.

Во-вторых, понадобятся проводочки любые и парочка коннекторов BLS:


Такие коннекторы можно вытащить из старого системного блока вместе с проводами, или попросить у знакомого компьютерщика, или тоже купить. Они прекрасно подходят для подключения к разъёму на Raspberry.

Начнём с планирования используемых портов. Порт- это грубо говоря штырёк на разъёме. Так, как штырьков там много (26), то и портов тоже много. А чтобы в них не запутаться, то каждому порту присвоен свой номер и обозначение. Следует заметить, что не все штырьки в этом разъёме являются портами. Некоторые штырьки подключены к источникам напряжения, а некоторые вообще никуда не подключены (По секрету, на самом деле они всё-же подключены, но ими пользоваться нельзя, можно убить свою Малинку. Поэтому лучше вобще их не трогайте).

Вот собственно как эти порты расположены на плате:


Чтобы светодиод зажёгся, нам нужно его подключить к источнику питания. Выбираем для питания светодиода Р1-01, верхний по рисунку штырёк, на котором присутствуетнапряжение 3,3в. Для управления светодиодом нам понадобится один порт GPIO. Можно выбрать любой. Но если у вас есть разъём BLS, то удобнее в данном случае использовать порт, который выведен на штырёк P1-03 и называется GPIO 0. В таком случае мы, воспользовавшись одним разъёмом, сможем подключить наш светодиод. И так, мы будем подключать светодиод между ножками разъёма P1-01 и Р1-03. С вывода Р1-01 мы берём +3,3в для питания светодиода, а вывод Р1-03 будет тем самым управляющим выводом порта GPIO. Все эти порты физически находятся внутри центрального процессора Raspberry Pi, который называется BCM2835. Этот процессор может подключать любой порт к источнику напряжения 3,3в, а может подключить порт к 0 питания (а может вообще никуда не подключать, но об этом позже). Эти переключения он делает в соответствии с поданной командой. Значит, когда порт будет подключён к напряжению +3,3в, наш светодиод гореть не будет, т.к. току некуда идти. А когда процессор подключит порт к 0, то наш светодиод загорится, т.к. ток побежит от +3,3в к 0 через светодиод. Значит наша программа должна будет отдавать соответствующие команды процессору в соответствии с нашим желанием.

Маленькое, но важное. На самом деле, мы не должны подключать светодиод напрямую между источником питания +3,3в и выводом порта. Это нельзя делать по двум причинам. Причина первая: любой светодиод нормально работает при определённом токе. Если через светодиод потечёт большой ток (а выход +3,3в способен отдать до 50мА), то светодиод сгорит. Если маленький ток, то светодиод будет гореть слишком слабо, либо вообще не будет светиться. Для большинства обычных светодиодов рабочий ток находится в пределах 10-20мА. Отсюда вытекает и вторая причина (хотя в данном случае она несущественна). Если мы пропустим большой ток через порт GPIO, то этим самым мы уничтожим процессор и Raspberry- умрёт. Поэтому, мы должны следить, чтобы через порт не протекал ток больше допустимого. Примем для себя ограничение в 16мА, так мы точно не сожжем процессор. Как этого добиться? Очень просто! Нам нужно последовательно со светодиодомвключить токоограничивающий резистор. И сейчас мы его рассчитаем.

Примем для светодиода рабочий ток в 10мА. Убеждаемся в том, что выбранный нами ток не превышает предельно допустимый ток для порта в 16мА. Теперь зная напряжение питания 3,3в и рабочий ток 10мА, мы можем по закону Ома рассчитать необходимое нам сопротивление. R=U/I=3,3/0,01=330Ом. Значит нам нужно найти резистор с сопротивлением 330Ом. А точнее- сопротивлением не менее 330Ом. Больше- можно. Светодиод будет заметно светиться и при сопротивлении 1000 Ом, или 1кОм. В общем наша задача- найти резистор с сопротивлением от 330 Ом до 1кОм. Если вы его нашли, то можно собрать вот такую схему:


Схему лучше собрать на макетной плате. Лично мне, для экспериментов, мой сын дал на прокат свой конструктор «Знаток».

Так выглядит схема в сборе:


Так мы подключаемся к Raspberry:


А вот общий план всей конструкции:


В крайнем случае, можно просто скрутить выводы элементов. Но в этом случае нужно следить за тем, чтобы оголённые ножки элементов случайно не попали на контактные площадки Raspberry. Это может убить его. Так же стоит обратить внимание на то, что светодиод должен подключаться Анодом к + источника питания, т.е. в нашем случае это Р1-01. Как найти на светодиоде Анод? Очень просто! Достаньте из любого ДУ батарейку на 1,5В и подключите к ней ваш светодиод. Если он не зажёгся, поменяйте выводы местами. Если зажёгся- то на + батарейки и будет Анод светодиода.


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

Почему на именно на Си? Просто по тому, что я других языков не знаю, а раз вы читаете эту статью, то скорее всего вы тоже немного знаете о программировании и радиоэлектронике, а значит, вам всё равно с какого языка начинать.

Обычно изучение языков программирования начинают с написания программы «Hello World!», но мы же круче «тех» чайников, поэтому мы начнём сразу с низкоуровневой работы с периферией. Тем более, что это не намного сложнее ХеллоуВорлда. ;) Что для этого нужно? Нужен любой текстовый редактор, в котором мы будем набирать программу. В Raspbian есть отлично подходящий для этого редактор “nano”. Ещё нужен компилятор, это программа, которая осуществляет перевод написанной нами программы с человечески понятного языка на язык, понятный компьютеру. Т.е. делает из нашей программы исполняемый файл, который мы впоследствии и запустим на Raspberry. Эта штука тоже у нас есть, называется gcc. Этот компилятор поставляется в комплекте со всеми Линуксами и уже готов к работе.

Библиотека быстренько скачивается. Чтобы её установить, нужно сначала её разархивировать. Это делается следующей командой: tar zxvf bcm2835-1.17.tar.gz

Теперь перейдём в директорию, куда эта библиотека развернулась: cd bcm2835-1.17

Ну и инсталлируем её:

Всё, теперь эта библиотека у нас есть в наличии, она установлена, и мы, и компилятор можем ей пользоваться в своих интересах. Начинаем писать программу. Возвращаемся в домашнюю директорию: cd ..

Тут можно создать папочку для наших экспериментов с любым именем, например myprog: mkdir myprog

Перейдём в эту папку: cd myprog

И начинаем писать нашу программу: nanoGPIO-test.c

Эта команда запускает текстовый редактор nano, который создаёт текстовый файл GPIO-test.c.Теперь можете набрать в нём следующую программу (можно просто скопировать и вставить):

Теперь о том, что делает каждая строка в нашей программе.

Все надписи после двойного слеша // являются коментариями и никак не влияют на выполнение программы.

int main() это начало нашей программы, обозначение главной функции в Си.

if (!bcm2835_init()) - эта часть пытается инициализировать GPIO и если это не получилось,

return 1; то аварийно завершает программу и передаёт на выходе код 1.

bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP); - Эта функция устанавливает для нашего порта Р1_03 режим на вывод. Т.е. говорит процессору, что этот порт будет использован для управления внешним устройством.

bcm2835_gpio_write(PIN, LOW); - устанавливаем порт Р1_03 в низкое состояние, т.е. процессор его подключает к 0. После этого светодиод загорается.

bcm2835_delay(1000); - Эта функция просто ждёт 1000 милисекунд, или ровно 1 секунду. Всё это время у нас горит светодиод.

bcm2835_gpio_write(PIN, HIGH); - устанавливаем порт Р1_03 в высокое состояние, т.е. процессор его подключает к +3,3в. При этом светодиод гаснет.

b>return 0; - Выход из программы с кодом 0.

Т.е. алгоритм работы с портом GPIO в режиме записи, т.е. вывода, выглядит следующим образом:

1. Инициализируем GPIO;

2. Устанавливаем режим для выбранного порта на Вывод;

3. Теперь можем управлять этим портом, устанавливая его в высокое, или низкое состояние. Соответственно на этом порте будет пристутствовать либо +3,3В, либо 0В. Что соответствует логической 1 и логическому 0 соответственно.

На этом на сегодня закончим. В следующей части научим наш светодиод загораться более полезным образом, а так же научимся портами GPIO не только отдавать команды другим устройством, но и слушать их.А пока можете начинать изучать язык Си. А так же попробуйте изменить эту программу так, чтобы светдиод управлялся бы другим портом и испытайте её.

ВНИМАНИЕ: статья рассчитана на разработчиков или опытных пользователей и даёт общие рекомендации того, как использовать gpio в обход официального ПО WirenBoard.

Если вам нужно работать напрямую с gpio, то мы рекомендуем делать это через драйвер wb-mqtt-gpio.

Описание доступных ножек gpio для конкретной ревизии контроллера можете посмотреть в статье GPIO.

Содержание

  • Убедитесь, что вашу задачу нельзя решить стандартными средствами программного обеспечения Wiren Board.
  • Все порты Wiren Board, в том числе и GPIO, работают с напряжением 3.3V.
  • Подключение сигнала с напряжением большим 3.3V к ножке GPIO грозит выходом из строя процессорного модуля.

В случае необходимости подключения устройств, работающих с более высоким напряжением, необходимо использовать схемы согласования или подключать (для 5V) через резистор в 20 кОм и более.

К сожалению, четкого стандарта по именованию gpio не существует, но при работе с контроллерами WirenBoard стоит придерживаться следующих правил:

  • выводы gpio сгруппированы по банкам (banks; эквивалентно gpiochips)
  • каждый банк содержит 32 gpio. Нумерация банков начинается с 0.

Вычисление номера gpio

Для управления ножкой gpio нужно знать её номер. В рассматриваемых примерах будем работать с gpio A1_IN контроллера WB6.7 (номер: 109; gpiochip 3, offset 13): Вычислим банк gpio и offset, зная номер (109):

То же самое справедливо и наоборот. Зная банк и offset (3 и 13, соответственно), можно вычислить номер gpio:

Перед началом работы из userspace, необходимо убедиться, в том, что нужный gpio — свободен. Для этого можно посмотреть на вывод команды

В выводе команды видим примерно следующее:

Это значит, что gpio 0, 26 и 27 уже экспортированы в sysfs и доступны для управления. Gpio 11 и 13 заняты ядерным драйвером onewire и недоступны для использования. Остальные gpio банка 0 — свободны.

Если нужный gpio — занят, то можно остановить драйвер:

ВНИМАНИЕ: остановка драйверов может привести к неожиданному поведению контроллера. Теперь нужный gpio свободен до следующей перезагрузки.

В настоящий момент, для работы с gpio в userspace доступны 2 интерфейса: sysfs и chardev (начиная с версии ядра 4.8).

Различия между chardev и sysfs хорошо описаны в этой статье. Sysfs имеет статус deprecated, поэтому, по возможности, стоит работать через chardev.

Интерфейс sysfs

Для работы через sysfs c определённым GPIO его надо экспортировать:

Здесь и далее N — номер gpio

Экспортированные gpio появляются в каталоге /sys/class/gpio :

В директории /sys/class/gpioN теперь находятся файлы для работы с GPIO (где N — номер GPIO, как и было сказано ранее):

Установка направления GPIO (ввод/вывод) производится с помощью записи в файл direction

Чтение и установка значения GPIO производится с помощью файла value .

Чтение и запись

  1. Находим номер GPIO, соответсвующий вашей версии контролера нужному клеммнику в таблице WB2.8. Для клеммника номер 2 в версии 2.8 это GPIO 32.
  2. Экспортируем GPIO в sysfs

Работа с прерываниями

Через интерфейс sysfs можно запросить прерывания по изменению состояния процессора.

Установка прерывания производится путём записи значения в файл "edge". Значения могут быть:

  • none — отключить прерывание
  • rising — включить прерывание по нисходящему фронту
  • falling — включить прерывание по восодящему фронту
  • both — включить прерывание по обеим фронтам.

Пример работы с прерываниями:

Прерывания можно ловить из userspace с помощью системного вызова epoll() и select() на файл value. Пример работы см. [1]

Работа через chardev

Представленный в ядре 4.8 интерфейс chardev имеет C/Python библиотеку libgpiod и userspace-утилиты для работы с gpio. Исходный код библиотеки и документация доступны в репозитории libgpiod.

Утилиты распространяются в составе debian-пакетов gpiod и libgpiod-dev для debian buster и новее. К сожалению, для stretch пакетов в официальных репозиториях нет.

Если нужно установить libgpiod в debian stretch, можно воспользоваться сторонними репозиториями (например, этим). Используйте сторонние репозитории на свой страх и риск; компания WirenBoard не контролирует их содержимое.

Для работы с gpio из bash в пакете gpiod поставляются следующие утилиты:

  • gpiodetect — информация обо всех банках gpio в системе
  • gpioinfo — подробная информация обо всех линиях gpio определённого банка
  • gpioget <чип> <линия> — возвращает значение определённого gpio
  • gpioset <чип> <линия1>=<значение1> <линия2>=<значение2> — устанавиливает состояние на определенные линии gpio
  • gpiofind <название> — возвращает номер gpio
  • gpiomon — отслеживание событий gpio

Примеры использования gpiod можно посмотреть в [2] и [3]

Python

Для управления gpio из python был написан модуль

Модуль представляет собой обёртку вокруг sysfs. Исходный код доступен на нашем github.

Модуль позволяет работать с gpio в синхронном и асинхронном (с регистрацией коллбэков) режимах.

Прямое обращение через память процессора

Этот метод настоятельно НЕ РЕКОМЕНДУЕТСЯ для использования без достаточных оснований. Для работы из С/C++ стоит использовать работу через файлы в sysfs или chardev, как описано в предыдущих разделах.

Управлять GPIO можно с помощью прямого доступа к регистрам процессора, в обход Linux, через интерфейс /dev/mem. При этом, по сравнению с работой через sysfs минимизируются накладные расходы. Этот метод можно использовать, если вам необходим очень быстрый доступ к GPIO, например bitbang протоколов или ШИМ. Стоит иметь в виду, что планировщик процессов всё ещё может вносить в работу программы значительные задержки. Рекомендуется выносить критичные ко времени задачи в ядро.

Ознакомиться с ядром Linux, использующимся в контроллерах WirenBoard можно в нашем репозитории ядра.

Рекомендации по Device Tree

Device-tree, использующиеся на контроллерах WirenBoard, доступны в репозитории ядра. Разные аппаратные ревизии контроллера используют разные dts (например, dts для WB6.X можно найти здесь)

Указывать GPIO в Device Tree необходимо для настройки работы GPIO в режиме программного SPI, I2C, для использования GPIO в качестве источника прерываний и т.д. Так, например, на пин 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 *внутри* банка.

Пример device-tree node


Что такое GPIO

Электрические характеристики GPIO (Electrical Specification)

GPIO на Allwinner A64 имеет следующие характеристики:

Все электрические характеристики можно найти в Allwinner_A64_Datasheet_V1.1 разделы 5. Electrical Characteristics. Основные таблицы с 5.1. Absolute Maximum Ratings по 5.5. Electrical Characteristics for Power Supply.

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

Плата Banana Pi BPI-M64

Для дальнейшей работы с GPIO необходимо получить datasheet на SoC и плату. Рассмотрим datasheet Banana Pi BPI-M64 и SoC AllWinner A64, затем поработает с GPIO через sysfs.

Banana Pi BPI-M64

Banana Pi BPI-M64 — это 64-битный четырехъядерный мини-одноплатный компьютер, поставляемый как решение с открытым исходном кодом. Ядром системы является процессор Allwinner A64 с 4-мя ядрами Cortex-A53 с частотой 1.2 ГГц. На плате размещено 2 ГБ DDR3 SDRAM 733МГц оперативной памяти и 8 ГБ eMMC. Также есть встроенный WiFi (AP6212) и BT4.0 (AP6212), Ethernet 10/100/1000 Мбит (Realtek RTL8211E/D). Видеоускоритель GPU Mali 400 MP2 поддерживает видео 1080P 4K. Более подробно можно почитать по ссылке.

Из спецификации представляет интерес, то что на плате размещен 40-контактный совместимый с Raspberry Pi разъем который содержит: GPIO (x28), Power (+5V, +3.3V and GND), UART, I2C, SPI. И 40-контактный интерфейс MIPI DSI.

SoC AllWinner A64

Процессор содержит:

PWM (ШИМ)

  • Поддержка вывода двух видов сигнала: непрерывного сигнала и импульсного сигнала.
  • Регулируемый рабочий цикл от 0% до 100%
  • Выходная частота до 24 МГц

UART

SPI

  • Два контроллера SPI
  • Полнодуплексный синхронный последовательный интерфейс
  • Настраиваемый ведущий/ведомый
  • Mode0

Two Wire Interface(TWI)/I2C

  • Четыре контроллеров TWI/I2C
  • Поддержка стандартного режима (до 100 Кбит/с) и быстрого режима (до 400 Кбит/с)
  • Настраиваемый Master/Slave
  • Позволяет 10-битные транзакции адресации
  • Выполнять арбитраж и синхронизацию часов
  • Может работать в широком диапазоне входных тактовых частот

Распиновка контактов GPIO

Большой плюс плат Banana Pi является наличие совместимого 40-контактного разъема Raspberry Pi 3.

Raspberry Pi GPIO Header

Позиция [1] 3V3 power соответствует позиции на плате со стрелочкой.

Banana Pi BPI-M64 GPIO Header Position

Banana Pi BPI-M64 GPIO Header Position

На 40-контактом разъеме Raspberry Pi определим какие из перечисленных интерфейсов нам доступны. На странице wiki опубликована спецификация 40-контактного разъема совместимого с Raspberry Pi 3. Совместим с данными WiringPi [BPI-WiringPi2], и получим сводную диаграмму:

Banana Pi BPI-M64 pinout diagram

Banana Pi BPI-M64 GPIOXX

Программирование GPIO

Существует несколько способов обращения к GPIO:

  • Посредством файл-устройством GPIO;
  • Используя языки программирования:
    • Через прямое обращение к регистрам чипа;
    • Используя уже готовые библиотеки.

    Каждый из методов имеет свои преимущества и недостатки.

    Работа с GPIO как файл-устройство через интерфейс sysfs

    Существует три типа записей в /sys/class/gpio:

    • Интерфейсы управления пользовательскими GPIO;
    • Сами GPIO;
    • Порты ввода-вывода (gpio_chip).

    Для доступа к контакту GPIO необходимо его зарегистрировать в системе, для этого используются интерфейсы управления по пути /sys/class/gpio, доступны только на запись:

    Практика

    Схема подключения датчиков:

    Управление светодиодом LED

    Для получение ссылки управление необходимо экспортировать GPIO для создания файл-устройства т.к. для оперирования контактом через sysfs нужен не порядковый номер 32-pin на разъеме типа Raspberry Pi, а номер ножки на процессоре. Для этого воспользуемся THE UNOFFICIAL BANANA PI BPI-M64 (r.23.01.2021) PINOUT DIAGRAM. Исходя из данной схемы 32-контакт соответствует 39-ножке процессора:

    Banana Pi BPI-M64 GPIOXX

    Поэтому необходимо экспортировать 39-контакт на процессоре, выполним команду:

    В результате в каталоге /sys/class/gpio должно появиться файл-устройство gpio39:

    Banana Pi BPI-M64

    Это означает, что виртуальный GPIO39 теперь доступен для работы. Поскольку нужно управлять светодиодом, нужно порт настроить его на выход:

    Теперь виртуальный GPIO39 настроен на выход и можно включить светодиод, отправляя в порт единицу:

    Работает! Светодиод загорелся:

    Также можно погасить светодиод, просто отправив логический 0, командой:

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

    Теперь можно вернуть контроль GPIO39 операционной системе, и освободить все ресурсы связанные с GPIO39:

    К выводам GPIO можно подключить через транзисторные ключи обычные электромагнитные реле, и управлять нагрузкой через сеть Интернет. Работать напрямую с GPIO через терминал не всегда бывает удобно, поэтому для демонстрации автоматизации создадим простейший bash-скрипт который выполнит регистрацию контакта, помигает 5-раз с интервалом раз в 0.5 секунды, и обратно вернет управление используемым контактом ОС.

    Скрипт bash
    1) Создадим скрипт в домашней папке пользователя root, путь /root, для редактирования файлом используется редактор mcedit (apt-get install mc):

    2) Поместим в редактор код:

    3) Сохраним файл(F2) и выйдем из редактора(F10).

    4) Установим права выполнения на файл:

    5) Запустим скрипт:

    Светодиод 5 раз будет мигать.

    Реализуем тоже самое, только на языке С:

    1) Создадим файл:

    2) Поместим в редактор код:

    3) Сохраним файл(F2) и выйдем из редактора(F10).

    4) Теперь скомпилируем программу:

    5) И так же как и в предыдущем примере запустим скомпилированную программу:

    Получение значения температуры от датчика DS18B20

    И выбрать пункт System из списка:

    Banana Pi BPI-M64 ds18b20

    Banana Pi BPI-M64 ds18b20

    И отметить w1-gpio:

    Banana Pi BPI-M64 ds18b20

    Затем сохранить <Save> и < Back >. Будет предложено перезагрузить палату, соглашаемся и ждем перезагрузки:

    Banana Pi BPI-M64 ds18b20

    После перезагрузки выполняем команду:

    В результате должны получить примерно такой результат:

    Banana Pi BPI-M64 ds18b20

    Из которого следует что драйвер поддержки включен и был обнаружен один датчик 28.0000034aa0ba. Если установлена библиотека WiringPi [BPI-WiringPi2], то выполнив команду

    Banana Pi BPI-M64 ds18b20

    Banana Pi BPI-M64 ds18b20

    Banana Pi BPI-M64 ds18b20

    Текущее значение температуры составляет: 24.5 градусов Цельсия. Задача выполнена!

    Но если понаблюдать за изменением температуры, то можно заметить, что точность показаний составляет 0.5 градусов. Драйвер устройства позволяет получать температуру с разной точностью. Согласно таблицы из спецификации к датчику, следует:

    Banana Pi BPI-M64 ds18b20

    • 9 бит: точность 0.5 °C, время получения данных 93.75 ms
    • 10 бит: точность 0.25 °C, время получения данных 187,5 ms
    • 11 бит: точность 0.125 °C, время получения данных 375 ms
    • 12 бит: точность 0.0625 °C, время получения данных 750 ms

    Чем выше точность, тем больше требуется времени на получение значения температуры. Для установки точности необходимо выполнить команду:

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

    Banana Pi BPI-M64 ds18b20

    В результате, температура составила: 24.437 °C

    Вывод использование интерфейса sysfs

    Недостатки:

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

    Ресурсы GPIO абстрагируются в ядре Linux, и абстрагируется вещь, называемая Gpiolib. Эта вещь существует как ядро ​​управления ресурсами GPIO:


    Средний уровень - это Gpiolib, который используется для управления GPIO в системе. Gpiolib суммирует общие операции GPIO.В соответствии с характеристиками GPIO, Gpiolib предоставляет унифицированный и общий программный интерфейс для операций GPIO (другие драйверы), который защищает конкретную реализацию различных микросхем. Между прочим, Gpiolib предоставляет набор фреймворков для различных операций чипа.Для разных чипов вам нужно только реализовать определенный драйвер чипа, а затем использовать функцию регистрации, предоставляемую Gpiolib, чтобы подключить его к Gpiolib, чтобы завершить этот набор вещей.

    Для других драйверов, таких как драйвер светодиодной лампы, вам необходимо использовать общую функцию Gpiolib для выполнения операций порта ввода-вывода.

    Сначала проанализируйте структуру данных.Gpiolib на самом деле суетится вокруг нескольких структур данных.Структура данных и уровень абстракции понятны, а код, естественно, быстрый.

    Структура данных в основном определена в include/linux/gpio/driver.h с участием /drivers/gpio/gpiolib.h в

    Сначала посмотрите на структуру данных под названием struct gpio_chipinclude/linux/gpio/driver.h ):

    На первый взгляд структура данных gpio_chip показывает, что многие структуры указателей на функции просты для понимания. Эта структура предназначена для абстрагирования всех операций GPIO и адаптации к общей структуре различных микросхем. Следовательно, эту структуру необходимо открыть для других микросхемы для выполнения конкретных задач Например, если вы являетесь микросхемой Qcom, вам необходимо реализовать содержимое вашего gpio_chip.

    2.1 структура gpio_chip

    Как правило, в микросхеме есть конфигурация для всех портов ввода-вывода. По умолчанию все порты ввода-вывода имеют вход GPIO (стабильный). Как правило, чип обеспечивает функцию мультиплексирования контактов (в более поздней версии Linux для абстрагирования используйте управление контактами). Чтобы использовать GPIO, вам сначала необходимо настроить его как функцию GPIO, а не другие функции.

    Для GPIO есть некоторые общие функции, такие как установка направления GPIO, чтение уровня GPIO (при вводе), запись уровня GPIO (при выводе), GPIO как ввод внешнего прерывания и т. Д.

    Абстракция gpio_chip на самом деле является абстракцией группы банков GPIO.Обычно аппаратно чип разделен на множество банков для портов ввода-вывода, и каждый банк разделен на N групп GPIO.

    Например: 1 SoC делит ввод / вывод на 4 банка:

    Bank 2:GPIOC ~ GPIOD

    Bank 3:GPIOE ~ GPIOF

    Bank 4:GPIOG ~ GPIOH

    Однако каждый банк имеет N наборов регистров для представления операций GPIO, таких как:

    В банке 1 для GPIO A:

    GPIOA_CFG, чтобы указать конфигурацию GPIO A

    GPIOA_PULL, чтобы указать конфигурацию подтягивания и опускания GPIO A

    GPIOA_DIR означает настройку GPIO A в качестве входа или выхода

    GPIOA_DATA, чтобы указать, что GPIO A установлен на высокий или низкий уровень, когда установлен как выход, или читается как высокий и низкий, когда ввод

    Конечно, та же операция применяется к GPIO B в банке 1:

    GPIOB_CFG для указания конфигурации GPIO B

    GPIOB_PULL для указания конфигурации подтягивания и опускания GPIO B

    GPIOB_DIR означает настройку GPIO B в качестве входа или выхода

    GPIOB_DATA, чтобы указать, что GPIO B установлен на высокий или низкий уровень, когда он установлен как выход, или читается как высокий и низкий, когда он вводится

    Выше приведен случай с одним банком, тогда у чипа несколько банков, поэтому все они похожи, поэтому я не буду их здесь повторять.

    Итак, общая структура выглядит следующим образом (здесь просто аналогия, некоторые чипы имеют много банков и много регистров):


    Драйвер Linux Gpiolib абстрагирует их, используя gpio_chip Соответствует набору описаний банков, например Bank · 1, выделенных с помощью gpio_chip:


    Для нескольких банков используйте указатели или массивы для их представления. Конечно, здесь это может быть немного неточно, gpio_chip Он просто абстрагируется от единого интерфейса группы банков.

    Для нижнего слоя микросхемы необходимо реализовать интерфейсы этих структур в соответствии с таблицей данных руководства по микросхеме.

    2.2 структура gpio_desc

    Поскольку система разделена на несколько банков, и каждый банк состоит из нескольких групп, каждый объект GPIO описывается с помощью gpio_desc:

    Эта структура относительно проста, как видите, она содержит структуру и флаг gpio_device, а также метку и имя;

    Указатель gdev указывает на gpio_device, которому принадлежит этот gpio_desc (скоро будет описан), а флаг представляет статус атрибута этого GPIO;


    Кажется, что gpio_chip и gpio_desc должны находиться во взаимосвязи, но эти две структуры не связаны напрямую в ядре, а связаны через другую структуру, которой является gpio_device.

    2.3 структура gpio_device

    gpio_device Его следует рассматривать как общего менеджера (последнее ядро ​​имеет, версия ядра Linux 3 не имеет этого), если gpio_chip - это конкретная абстракция оборудования GPIO банка, то gpio_device - это уровень программного обеспечения для управления GPIO банка Блок, структура его данных:

    В этой структуре gpio_device он включает gpio_chip (рабочий набор стыковочных чипов), gpio_desc (описание некоторых GPIO); эта структура проходит через весь Gpiolib, поскольку gpio_device представляет собой банк, а общий GPIO имеет несколько банков, поэтому ядро ​​в организации этого gpio_device состоит из gpio_devices (Здесь несколько устройств, поэтому s добавляется позже) в gpiolib.c:


    Давайте поговорим о том, как Gpiolib связан с фактическим базовым драйвером. Как упоминалось в предыдущем разделе 2.1, нижний слой необходимо закрепить. Фактически, стыковочная часть - это только те общие операции, которые на самом деле gpio_chip Это, следовательно, нижняя часть стыковки, основная проблема - это структура и процесс присвоения значений этой структуре.

    При подключении к Гпиолибу внизу в основном правильно gpio_chip Внедрить, а затем позвонить gpiochip_add , Зарегистрируйте свой GPIO в Gpiolib.

    Процесс реализации в основном заключается в реализации соответствующей операции GPIO в соответствии с руководством по микросхеме, то есть для программирования операции регистрации в функции и подключения ее к структуре gpio_chip.

    Например, соответствующий Exynos 4:

    Определяются базовый адрес каждого банка и количество GPIO. И поддержка для прерывания.

    В его функции инициализации:

    Вызывается функция xxx_gpiolib_add_xxxx. В этой серии вызовов назначаются соответствующие элементы структуры gpio_chip и, наконец, вызываются gpiochip_add Функция, которая прописана в подсистеме Gpiolib ядра. Общий процесс такой. Некоторые детали могут немного отличаться из-за несовместимости версий.

    Конечно, в более новой версии ядра Kernel предоставляет интерфейс devm для управления используемой памятью, тогда этот gpiochip_add_data становится:

    Далее мы рассмотрим эту функцию для регистрации GPIO.

    3.1. Зарегистрируйте ресурсы GPIO (gpiochip_add)

    Как упоминалось ранее, зарегистрируйте интерфейс использования ресурсов GPIO:

    В старом ядре нижняя часть стыковки заключается в том, что каждый банк напрямую вызывает функцию gpiochip_add. Новое ядро ​​поддерживает входящие данные, то есть с личными данными, поэтому интерфейс регистрации, поддерживаемый новым ядром, следующий: gpiochip_add_data(chip, data)

    Значение параметров, чип также является структурой gpio_chip, data is void * data, private data.

    Для обеспечения совместимости с предыдущим драйвером определение gpiochip_add в новой структуре:

    То есть звонок без данных;

    Конечно, Gpiolib также поддерживает интерфейсы с devm для управления своей памятью:


    Далее давайте проанализируем этот интерфейс;

    3.2、 gpiochip_add_data_with_key

    Эта функция разделена на несколько раз, первая часть 1:

    В части 1, поскольку входящей структурой является gpio_chip, она представляет банк, но отсутствует структура gpio_device. Поэтому в этой функции сначала выделите структуру gpio_device и добавьте чип ее члена структуры и т. Д. Дождитесь присвоения, чтобы установить родственные структурные связи.

    Посмотрите часть 2:

    В части 2, поскольку банк имеет более одного GPIO, структура gpio_chip-> ngpio указывает общее количество GPIO в этом банке.Каждый GPIO представлен gpio_desc, поэтому здесь размещаются дескрипторы ngpio;

    Наконец, часть 3:

    В части 3 база представляет собой номер каждого банка, назначьте его; затем передайте gpiodev_add_to_list (gdev) Прикрепите этот gdev к глобальному gpio_devices:

    Затем нужно установить некоторые поля имени, настроить прерывания и т.п., инициализировать флаги каждой структуры desc [] и, наконец, вызвать:

    Затем, если ничего не происходит, верните 0;

    Вот вызов gpiochip_setup_dev, это вызов gpiochip_setup_devs во время инициализации Gpiolib:

    И этот gpiochip_setup_devs вызывает для каждого узла gpio_devicecs: gpiochip_setup_dev:

    Личное понимание, потому что я не знаю, кто будет выполнять этот init и init нашего нижнего драйвера, поэтому используется переменная gpiolib_initialized Чтобы указать, завершил ли текущий Gpiolib регистрацию связанных символьных устройств, если Gpiolib сначала перейдет к инициализации, затем gpiolib_initialized Правда, нижняя часть стыковки чипа упускает возможность настройки gpio_chip, поэтому нужно снова вызвать интерфейс gpiochip_setup_dev, иначе ОК;

    На данный момент подключение к базовому драйверу в основном в порядке.Друзьям необходимо создать свою собственную структуру gpio_chip в соответствии со спецификацией своего чипа и, наконец, добавить ее в подсистему Gpiolib через gpiochip_add_data;

    Следует также отметить, что друзьям необходимо самостоятельно определять некоторые структуры для получения и выражения виртуального адреса своего собственного банка и т. Д., Чтобы они могли управлять реальными аппаратными регистрами;

    После стыковки нижнего уровня Gpiolib предоставляет ряд интерфейсов вызова для других драйверов:

    1. gpio_request: подать заявку на gpio в ядро

    Чтобы использовать GPIO, вы должны сначала обратиться к ядру и вернуть 0, что означает, что приложение выполнено успешно, и вы можете продолжить

    2. gpio_free: соответствует gpio_request, он предназначен для выпуска gpio после использования gpio

    3. gpio_direction_input: установить GPIO как вход

    4. gpio_direction_output: установить GPIO как выход

    5. gpio_get_value: прочитать значение GPIO

    6. gpio_set_value: установить значение порта GPIO

    4.1、gpio_request

    Когда другие драйверы используют GPIO, вам нужно сначала вызвать этот интерфейс и подать заявку на GPIO из Gpiolib:

    Он передал следующие параметры: gpio и метку. Параметр gpio - это число, которое представляет номер GPIO на плате. Что это за номер? Обратите внимание, что число здесь отличается от числа в таблице данных. Чтобы узнать это, давайте познакомимся с членами gpio_chip-> base. Например, многие люди знали о микросхемах Samsung 2440 или 6410. Для них определение GPIO, давайте посмотрим, как определить член gpio_chip-> base:

    .Base, которую мы видим здесь, это gpio_chip-> base, не определяйте ее как S3C64XX_GPA (0), S3C64XX_GPB (0), S3C64XX_GPC (0) . Он определен в:

    Приведенное выше определение показывает:

    Bank B base -> S3C64XX_GPB(0) -> S3C64XX_GPIO_A_START(=0) + S3C64XX_GPIO_A_NR(=8)

    Bank C base -> S3C64XX_GPC(0) -> S3C64XX_GPIO_B_START(8) + S3C64XX_GPIO_B_NR(=7)

    Следовательно, все GPIO нумеруются в этом порядке, начиная с 0. В реализации стыковки Gpiolib на программном уровне этого чипа основание каждого банка спроектировано так, чтобы быть начальной позицией предыдущего GPIO. Номер последнего GPIO, наконец, все GPIO пронумерованы в едином порядке на программном уровне.Это значение gpio_chip-> base относится к начальному номеру каждого банка!

    Ближе к дому входной параметр gpio в функции gpio_request относится к количеству пронумерованных GPIO.Если вы хотите его, вы должны перевернуть его карту!

    В реализации gpio_request сначала вызывается gpio_to_desc для индексации структуры gpio_desc в соответствии с маркой переданного gpio (помните, gpio_desc представляет собой экземпляр GPIO)

    Как вы можете видеть, на самом деле он проходит через gpio_devices, и затем, если номер, который вы передаете, находится в этом банке, ОК и верните вам эту структуру desc;

    Затем вызовите gpiod_request Gpiolib:

    Перейдите в gpiod_request_commit:

    Для этого GPIO, если никто не переходит к FLAG_REQUESTED, установите его метку в соответствии с входящей меткой, получите для него некоторые направления и дождитесь статуса;

    Если он был FLAG_REQUESTED, возвращается -EBUSY, и запрос GPIO не выполняется.

    4.2、gpio_direction_input/gpio_direction_output

    Когда запрос будет успешным, вы можете использовать свой успешный gpio для выполнения действий.Если вы хотите прочитать статус GPIO, вам необходимо настроить его как вход.Если вы пишете, настройте вывод (загорится светодиодный индикатор);

    Эти две функции очень просты:

    Чтобы не анализировать слишком много, на самом деле, согласно количеству переданных gpio, после преобразования в desc получается структура gpio_chip банка, а затем вызывается подключенное direction_input / direction_output. Давайте посмотрим на фактическая базовая реализация:

    4.3、gpiod_set_value/gpiod_get_value

    Чтение GPIO похоже на написание GPIO, поэтому я не буду здесь об этом говорить, это будет очевидно с первого взгляда;

    Читайте также: