Gpio driver что это

Обновлено: 02.07.2024

Данная статья рассчитана на пользователей, имеющих представления об основах GPIO Raspberry Pi.

Если эта тема для вас новая, то перед прочтением статьи рекомендую ознакомится с другими статьями для начинающих на тему GPIO - их можно найти в разделе"Документация"

В настоящее время самой популярной библиотекой для работы с GPIO на Raspberry Pi сталаwiringPi

Установка wiringPi

1. Если у вас нет утилиты git для работы с github-репозиториями, то устанавливаем:

2. Скачиваем исходники библиотеки из репозитория -

. UPD: автор библиотеки прекратил её поддержку и разработку. Крайняя версия WiringPi от Гордона Хендерсона не работает нормально на Raspberry Pi 4. Поэтому следует использовать версию библиотеки, которая разрабатывается сообществом:

Использование

После того, как библиотека wiring Pi установлена, вы можете использовать её в своих проектах. Пример:

Библиотека должна быть обязательно инициализирована вызовом функции wiringPiSetup() После этого можно использовать библиотечные функции

Основные библиотечные функции

Если библиотека была инициализирована функцией wiringPiSetup() , то в функции следует передавать "виртуальный" номер пина. Таблицу соответствия "виртуальных" пинов (колонка wPi) реальным можно получить при помощи команды gpio readall (утилита gpio устанавливается автоматически вместе с библиотекой WiringPi)


void pinMode (int pin, int mode) Устанавливает режим работы пина. Доступные значения параметра mode: INPUT, OUTPUT, PWM_OUTPUT, GPIO_CLOCK. Примечание: режим работы PWM_OUTPUT поддерживается только пином BCM_GPIO 18, режим GPIO_CLOCK поддерживается только пином BCM_GPIO 4

void pullUpDnControl (int pin, int pud) Включает внутренние подтягивающие резисторы для пина, работающего в режиме INPUT. Возможные значения PUD_OFF (подтягивающие резисторы отключены), PUD_DOWN (подтяжка к земле), PUD_UP (подтяжка к 3.3v)/Сопротивление подтягивающих резисторов на Raspberry Pi составляет около 50KОм

void digitalWrite (int pin, int value) Устанавливает высокий (value=1)/низкий уровень(value=0) на пине, работающем в режиме OUTPUT

int digitalRead (int pin) Считывание состояния пина. В зависимости от логического уровня функция возвращает 0 или 1

analogWrite (int pin, int value) Функция записывает значение в АЦП.

Временные функции

unsigned int millis (void) Возвращает время (в миллисекундах) прошедшее с момента вызова функции инициализации (wiringPiSetup) библиотеки WiringPi. Значение сбрасывается через 49 дней

unsigned int micros (void) Возвращает время (в микросекундах) прошедшее с момента вызова функции инициализации (wiringPiSetup) библиотеки WiringPi. Значение сбрасывается приблизительно через 71 минуту

void delay (unsigned int howLong) Приостанавливает выполнение программы на период времени, заданный в параметре howLong (задаётся в миллисекундах)

void delayMicroseconds (unsigned int howLong) Приостанавливает выполнение программы на период времени, заданный в параметре howLong (задаётся в микросекундах)

Прерывания

int wiringPiISR (int pin, int edgeType, void (*function)(void)) Регистрирует функцию, которая будет выполнена при наступлении условия прерывания. Условие прерывания (параметр edgeType) может принимать следующие значения:

  • INT_EDGE_FALLING (прерывание при смене уровня на пине с высокого на низкий)
  • INT_EDGE_RISING (прерывание при смене уровня на пине с низкого на высокий)
  • INT_EDGE_BOTH (прерывание при любой смене уровня на пине)
  • INT_EDGE_SETUP

При условии INT_EDGE_SETUP не будет происходить инициализации пина - подразумевается, что он уже был настроен в другом приложении.

В случае если следующее прерывание наступает до окончания обработки предыдущего, то оно также будет обработано. Но если 2 прерывания будут находится в обработке одновременно, то последующие прерывания будут игнорироваться.

Функция-обработчик прерывания выполняется с высоким приоритетом (если программа запущена от пользователя root) и выполняетсяодновременнос основной программой. Также она имеет полный доступ к глобальным переменным и т.д

4.2、gpio_direction_input/gpio_direction_output

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

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

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

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 Он просто абстрагируется от единого интерфейса группы банков.

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

GPIO для чайников (часть 1)

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

Если вы приобрели 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 для чайников (Часть 2).

В первой части мы научились включать и выключать наш светодиод. Это самое элементарное и простое, на что способен порт GPIO. Но даже это самое простое уже можно использовать в своих целях. За примером далеко ходить ненужно. Например, на плате Raspberry светодиоды управляются точно так же, через подключение к GPIO. Просто на разъём GPIO выведены не все доступные порты. Часть этих портов используется самим Raspbery для своих нужд. Моргает светодиодами, общается с SD-картой, управляет адаптером Ethernet и т.д.

Пойдём дальше. Что получится, если мы будем повторять последовательность включенxя и выключения? Давайте попробуем! Слегка подправим нашу написанную программу. Откроем, написанную нами ранее программу:

nano GPIO-test.c

Сохраним её под новым именем:<>ctrl+o

и введём blink.c

Теперь отредактируем её до следующего вида (или просто удалим всё написанное ранее и вставим текст отсюда):

Проверяем на наличие ошибок и сохраняемся:

gcc -o blink blink.c -lrt -lbcm2835

Ну вот, теперь наш светодиод мигает. В эту программу мы внесли следующие изменения:

Во-первых, изменили временные промежутки включённого и выключенного состояния. Теперь наш светодиод 500 миллисекунд горит и 500 миллисекунд не горит. Т.е. на всё уходит ровно 1 секунда.

Во-вторых, мы заключили функции включения и выключения светодиода в бесконечный цикл. Оператор while(1) будет выполнять всё, что заключено в следующую за while пару скобок <> до тех пор, пока параметром while будет 1. А раз мы эту единичку туда прописали, то она там будет всегда, а следовательно и команды включения-выключения будут тоже выполняться бесконечно.

Посмотрели, порадовались своим успехам… А светодиод всё ещё мигает. И будет мигать до тех пор, пока не выдернете источник питания из розетки. А ещё можно прекратить выполнение программы «варварским» способом- нажать Ctrl+c. Только если нажать неудачно, то программа завершится, а светодиод продолжит гореть. Непорядок. Нужно это дело исправлять.

Ставим перед собой задачу:

1. нам нужно предусмотреть возможность прерывать выполнение программы в любой нужный нам момент;

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

Т.е. после выхода из программы светодиод однозначно бы гас.

Приступим. Для того, чтобы завершить выполнение программы в любое время, мы подключим к нашему Raspberry кнопочку. Пока она не нажата, светодиод будет мигать. Когда нам надоест смотреть на мигающий светодиод, мы нажимаем на кнопочку и наша программа завершается с выключенным светодиодом. Чтобы это реализовать, нам нужно выделить для кнопочки ещё один порт GPIO. Причём этот порт нужно настроить не на Вывод, а на Ввод (или другими словами не на запись, а на чтение). Этот порт будет следить за состоянием подключённой к нему кнопки, и как только на нём появится напряжение (уровень HIGH), наша программа должна завершиться. Выберем порт для этой цели. Пусть будет Р1_07.

Так же нам нужен источник сигнала для кнопки, за состояние которой мы будем следить. На этом источнике должна постоянно находиться логическая единица. Можно в качестве такого источника использовать присутствующие на Р1_01 +3,3в. Но к нему неудобно подключаться, он уже использован для питания светодиода. Мы поступим проще. Помните, я в первой части говорил, что процессор bcm2835 умеет подключать свои порты к 0, либо к +3,3в? Так вот, если мы подключим порт процессора к +3,3В посредством установки этого порта в высокое состояние HIGH, то этот порт и можно будет использовать в качестве источника сигнала "1" для кнопки. Выделим под это дело порт P1_05, который находится по соседству с Р1_07. В результате у нас должна получиться вот такая схема:


Самые внимательные могут вспомнить, что в предыдущей первой части я говорил, что мы должны всегда следить за тем, чтобы не перегрузить порт по току, и должны устанавливать токоограничивающие резисторы. А в данном случае кнопка подключена безо всего. А так, как кнопка практически не имеет никакого сопротивления, то замкнув кнопкой +3,3В с 0 мы получим короткое замыкание и сожжём нашу Малинку… Но этого не произойдёт. И вот почему. Во-первых, разработчики процессора решили снять часть ответственности с нас и сделали так, что процессор подключает свои порты к +3,3в и 0 через микроскопические резисторы, установленные в самом процессоре. Поэтому короткого замыкания не произойдёт. Ток потечёт через внутренние резисторы. Во-вторых, так же в первой части я говорил, что процессор может вообще никуда не подключать порт. Так вот. Если мы настраиваем порт на Ввод (чтение), то это как раз тот самый случай. Процессор будет читать данные с этого порта, поэтому ему нет необходимости подключать его к источнику питания, или 0. В этом режиме порт имеет достаточно высокое сопротивление. Настолько высокое, что мы в принципе можем безбоязненно подать на него прямиком 3,3 вольта от источника питания, и с ним ничего не случится. Но всё равно, если вы подключаете кнопку напрямую от источника питания, то лучше подстраховаться от всяческих бросков напряжения и поставить небольшой токоограничивающий резистор. Например тот же в 330Ом (не менее 100Ом). На будущее запомним следующие правила:

1. Мы можем спокойно подключать напрямую (без резисторов) к портам GPIO любые входы и выходы микросхем, работающих от того же источника питания, что и наш Raspberry, и имеющих 3-вольтовую логику. Под это же правило попадает и наше подключение кнопки между 2 портами процессора;

2. Если подключаемое устройство имеет 3-вольтовую логику, но работает от другого источника питания, то нужно подстраховаться и подключать это устройство через ограничительные резисторы сопротивлением 100-330 Ом.

3. Если подключаемое устройство работает на 5-вольтовой логике, то мы обязательно должны делать согласование уровней. На GPIO Raspberry нельзя подключать напряжение выше 3,3В! По этому о непосредственном подключении не может быть и речи!

Ну и информация для общего развития. Даже в режиме чтения порт может быть подключён к +3,3В, или 0 через внутренний резистор. Это называется подтяжкой.

Теперь давайте соберём схему. В принципе у нас она собрана, осталось только подключить к портам Р1_05 и Р1_07 кнопку. Кнопку можно взять абсолютно любую, хоть выключатель от электролампочки. Я взял кнопку Reset из старого системного блока:


В крайнем случае, можно просто использовать 2 проводочка из разъёма и просто замыкать их между собой.Подключаем всё к RPi таким образом:


Откроем нашу программу blink.c и добавим в неё несколько строк. В результате она должна будет выглядеть вот так:

Проверяем программу на ошибки и сохраняем под новым именем:

Компилируем и запускаем:

gcc -o button button.c -lrt -lbcm283

sudo ./button

Светодиод начинает мигать, как и раньше. Теперь нажмём нашу кнопку (нажатие должно быть продолжительным).

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

Первое, что мы сделали, это закрепили свои названия за выбранными портами.

Порт Р1_05, который служит источником питания для нашей кнопки, мы обозвали PIN_OUT.\nПорт Р1_07, который следит за состоянием нашей кнопки, мы обозвали PIN_IN.

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

Следующее изменение касается установки новых портов в нужные нам режимы.

bcm2835_gpio_fsel(PIN_OUT, BCM2835_GPIO_FSEL_OUTP);

bcm2835_gpio_fsel(PIN_IN, BCM2835_GPIO_FSEL_INPT);

Т.е. установили PIN_OUT в режим Вывода (он нам даёт +3,3В для кнопки), а PIN_IN установили в режим Ввода (он будет следить за состоянием кнопки).

bcm2835_gpio_pud(PIN_IN, 1);

Этой строкой мы подключили порт PIN_IN через внутренний резистор процессора к "0" (или GND), применили подтяжку порта к 0. Это позволит повысить помехоустойчивость опроса состояния кнопки. В более ранней версии этой статьи этого сделано небыло. И хотя всё работало, но при подключении RPi к сети по Wi-Fi возникали ошибки чтения из-за того, что Wi-Fi донгл наводил в проводниках кнопки потенциал, достаточный для распознавания на порте PIN_IN логической единицы, хотя кнопка была ещё не нажата. В 5-й части цикла статей я ещё коснусь этого вопроса.

Далее, мы установили порт PIN_OUT в высокое состояние:

bcm2835_gpio_write(PIN_OUT, HIGH);

Теперь на выходе порта Р1_05 у нас присутствует логическая единица в виде напряжения в +3,3В.

И ещё мы изменили содержимое функции while()

while(!bcm2835_gpio_lev(PIN_IN)) ;

Мы поместили в условие выполнения цикла функцию чтения состояния порта PIN_IN.

Функция bcm2835_gpio_lev() возвращает значение состояния выбранного порта, т.е. читает его состояние. В нашем случае она читает состояние порта PIN_IN, который в соответствии с нашим определением является портом Р1_07. Если на ножке этого порта присутствует логическая единица, или по другому +3,3В, то эта функция возвращает 1 (помните, в нашей предыдущей программе какраз на этом месте и стояла 1?). А если на ножке порта логический ноль (или напряжение 0В), то эта функция и возвращает 0. Восклицательный знак перед этой функцией обозначает инверсию результата. В булевой математике он соответствует функции «НЕ». Т.е. !1=0, а !0=1. Другими словами изменяет результат на противоположный. Вот и получается, что наш светодиод будет мигать до тех пор, пока результатом чтения состояния порта PIN_IN будет НЕ единица, а собственно Ноль. А НЕ единица будет до тех пор, пока мы не нажмём кнопочку. Как мы только нажмём на кнопку, то логическая единичка с порта PIN_OUT придёт на порт PIN_IN и функция чтения состояния этого порт возвратит единичку. Цикл прервётся и программа завершится. А так, как последним состоянием светодиода в этом цикле было «выключено», то программа завершится именно с выключенным светодиодом. Так же стоит отметить, что чтение порта PIN_IN в нашей программе выполняется 1 раз в секунду. Именно поэтому нажатие кнопки должно быть продолжительным, чтобы гарантировано уложиться в эту 1 секунду и попасть на чтение порта. Разумеется, есть масса способов обойти это неудобство, но пока мы не будем заострять на этом внимание, чтобы не усложнять программу и разобраться в сути чтения информации с порта.

Ну и появилась ещё одна новая функция, которой мы ранее не пользовались.

bcm2835_close ()

Эта функция закрывает библиотеку, освобождает выделенную под использование GPIO память и приводит состояние портов GPIO к исходному.

Другими словами корректно завершает работу с портами.

Мы её поместили в return потому, что эта функция возвращает значение 1, если всё прошло успешно, и 0, если возникли какие-то проблемы.

Этой функцией нужно пользоваться всегда при завершении работы с портами. Это, как правило хорошего тона - уходя говорить: «до свидания». Так что эту функцию можно смело ставить и во все ранее написанные нами программы.

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

Но на этом мы пока не будем разбирать нашу схему со светодиодом. Он нам поможет разобраться ещё в нескольких интересных и нужных вещах. И об этом речь пойдёт в следующих частях серии «GPIO для чайников».

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

Автор:
Комментариев: 13


zhevak7

Hi, мое имя Александр Антонович. Я отношу себя к профи. Поэтому, позвольте я дам несколько советов и чуть-чуть покритикую. 1. Пожалуйста, замените варажения типа "питание для кнопки". Во первых, это режет слух, во вторых, это в корне не правильно, а в третьих Вы дуроно воспитаете своих пионеров. 2. К вопросу -- почему разработчики рекомендуют делать так: bcm2835_gpio_fsel(PIN_IN, BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_fsel(PIN_IN, BCM2835_GPIO_FSEL_INPT); Зайду из далека. Несложно ответить на вопрос -- какое напряжение будет на GPIO4, когда кнопка нажата? Ответ -- такое напряжение, какое в этот момент будет присутствовать на GPIO1. На GPIO1 присутствует (всегда) логическая единица (далее -- "1"), значит, на прои нажатии на кнопку на выводе порта GPIO4 будет присутствовать "1". Ответь на вопрос -- "какое напряжение будет присутствовать на GPIO4, когда кнопка не нажата?" -- очень сложно. Дело в том, что высокоомный вход микросхемы практически остается брошенным на произвол судьбы и помехам. В граммотно спректированных схемах вывод ВСЕГДА "поддтягивают" к земле или к питанию с помощью высокоомных резисторов. В нашем случае, мы должны "подтягивать" вывод к земле. Сопротивление резисторов обычно берут в пределах от 10 до 100 кОм. Но в редких случаях, можно увидеть и 1 кОм и 1 МОм. Подтягивающие резисторы могут внутренними (если таковые имеются в микроконтроллере и удовлетворяют требованиям подтяжки), либо внешние. Когда кнопка не нажата, высокоомный резистор подтягивает вход. В нашем случае -- тянет его к земле. Иначе говоря, на выводе формируется лог."0". Причем следует заметить, формируется весьма слабыми токами -- ведь резистор-то -- относительно высокоомный. Поэтому, как только произойдет нажатие на кнопк, на выводе тут же сформируется лог."1", так как выход порта микросхемы -- это более сильный источник сигнала, чем подтягивающий резистор. Когда кнопку отпустят, на входе снова сформируется лог."0". Поддягивающий резистор делает свое дело. Наша задача, выбрать сопротивление подтягивающего резистора такое, чтобы он, с одной стороны, не сильно нагружал выход порта, когда нажимается кнопка, а с другой -- не слишком большим, чтобы он мого шунтировать наведенные на провода помехи. Далее. Что происходит в нашей схем, ведь в ней нет подтягивающих резисторов? Для ответа на этот вопрос нужно вспмнить, что на входе порта стоят полевые транзисторы. Полевые транзисторы характеризуются тем, что они практически не имеют входных токов, но зато имеют ощутимую входную емкость. Таким образом, выполняя последовательность команд: bcm2835_gpio_fsel(PIN_IN, BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_fsel(PIN_IN, BCM2835_GPIO_FSEL_INPT); мы сначала переводим вывод порта в режим выхода, тем самым формируем на выводе микросхемы напряжение, соответствующее состоянию порта, а это значит -- ЗАРЯЖАЕМ или РАЗРЯЖАЕМ входные емкости (емкости переходов у входных полевых транзисторов), а потом переводим вывод порта в режим ввода. После этих опрераций на входной емкости порта КАКОЕ-ТО ВРЕМЯ будет сохраняться полученный заряд. Потом, через несколько секунд, а может быть десятков минут, токи утечек разрядят входные емкости и схема (а, следовательно, и программа) начнет вести себя непредсказуемо. В нашем случае всё это вроде бы работает. Во первых, при переводе вывода порта на выхд, мы предполагаем, что на выходе порта у нас лог."0". На самом деле это так: после сброса микроконтроллера состояние портов обычно равно нулю. Но если перед запуском нашей проги, кто-то выведет в порт GPIO1 лог."1", то мы вместо того чтобы сообщить входным емкостям напряжение лог."0", зарядим их. Как поведет себя в этом случае прога, думаю, вы легко сообразите самостоятельно. К стати, проведите эксперимент -- предварительно выведите в порт единичку, а потом выполните эти две команды, и посмотрите, сработает-ли "рекомендации специалистов". 3. Я с большим удовольствием хочу отметить, что не смотря на незначительные огрехи (а укого их нет!) автор цикла статей старается сделать мир лучше. Если мы не будем делиться своими знаниями и опытом, а будем делать на этом деле деньги, мы не станем богаче. Возможно, у нас лично будет больше денег, чем у других окружающих нас людей, но как нация в целом, мы будем беднее и духовно и материально. Автор статей, видимо, познал эту истину, за что ему огромной респект!


zhevak7

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 не выполняется.

1. Краткое описание

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

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


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

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

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 для управления своей памятью:


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

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:


2. Анализ структуры данных, связанных с 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.2 структура gpio_desc

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

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

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


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

3. Нижний слой стыковочного чипа Gpiolib

Давайте поговорим о том, как 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.

4. API, предоставляемые Gpiolib для других драйверов.

После стыковки нижнего уровня 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

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;

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

4.3、gpiod_set_value/gpiod_get_value

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

Интеллектуальная рекомендация

[AtCoder-4242] в бесконечность (мышление)

Стебель вопроса: Problem Statement Mr. Infinity has a string S consisting of digits from 1 to 9. Each time the date changes, this string changes as follows: Each occurrence of.


перейти на веб-сайт третьего дня примечания к изучению - mysql


Отчет о тенденциях цифрового маркетинга в Китае за 2015 год

Стандарты набора персонала Unicorn Enterprise Heavy для Python-инженеров 2019 >>> [Отчет о тенденциях цифрового маркетинга в Китае, 2015 г.] Отчет о тенденциях цифрового маркетинга в Китае за.

7-3 три цифры в обратном порядке (язык C)

Это тот, который я начал практиковать в кратчайшие сроки и сдал тест? Продолжай ↖ (^ ω ^) ↗ Прикрепите код: .

Игра головоломки (передний конец)

Обмен каждым элементом приведет к этому изменению массивов, в соответствии с этим, для завершения рендеринга страницы «Exchange» эффект, MOE анимации HTML раздел CSS Style Part. ЧАСТЬ ЧАСТ.

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

В виртуальной машине-клоне сеть не может нормально загрузиться после загрузки

Описание проблемы: При клонировании виртуальной машины сетевая карта запускается ненормально. Содержание ошибки и явление Решение первое: Измените mac-адрес eth0 на mac-адрес eth1, измените имя устрой.

Linux BlaceSing команда

Основа командной строки Некоторое существительное "Графический интерфейс" "Командная строка" "Терминал" 「shell」 「bash」 Монтаж Windws: Установите Git, Открыть Gitbash Linu.

Шаблон Angular ссылается на переменные для привязки данных. Значение данных страницы не изменяется и не обновляется.

описание проблемы Эта статья посвящена привязке данных ссылочных переменных шаблона angular При использовании шаблона angular для ссылки на переменные значение привязки страницы не обновляется после в.


Об использовании SimpleAdapter

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


org.apache.jasper.JasperException: Unable to compile class for JSP: An error occurred at line: 1 i

Причина в том, что Maven имеет встроенную версию 1.6 Tomcat Не могу скомпилировать jsp Поэтому измените версию Tomcat на более позднюю версию 1.7 или 1.8. Просто измените номер после tomcat в теге art.

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