Gpio driver что это

Обновлено: 02.07.2024

Linux GPIO разработка драйвера клавиатуры запись_OMAPL138

Базовая настройка Linux завершена. Разработка драйверов Linux началась в последние несколько дней. Начиная с самого простого драйвера клавиатуры, мы постепенно понимаем, каков процесс разработки драйверов. Взглянув на каталог драйверов в файле ядра Linux 3.3 и открыв файл C внутри, я чувствую, что механизм механизма Linux по-прежнему очень сложен, и его изучение требует много времени. То, что мы сейчас разрабатываем, нельзя назвать драйвером, это можно сказать только как приложение драйвера. Давайте начнем с приложения и узнаем больше.

0. Разработка подготовка

  • Исходные файлы ядра (каталог, когда мы собираем ядро ​​в то время, очень важен, и мы должны полагаться на эти исходные файлы ядра при компиляции драйвера)
  • Makefile (управляемый компиляцией Makefile)
  • Источник драйвера
  • Приложение (с основной функцией)

1. Схема подключения клавиатуры


В основном мы используем USER0 и USER1 KEY, две клавиши для завершения разработки драйвера клавиатуры GPIO для Linux. Из рисунка видно, что GPIO0_6 и GPIO6_1 в основном собирают информацию о нажатии на клавиатуру.

2. файл драйвера key.c


Структуру драйвера можно увидеть на рисунке выше.В основном это регистрация устройства ядра Linux. Мы используем platform_device для регистрации ядра. Мы смотрим на это с ума на диаграмму, и определяем это один за другим сзади.

2.1 Структура gpio_key_buttons

Определение порта GPIO может быть другим на других платформах. Я использую OMAPL138, а используемый файл ядра предоставляется OMAPL138. Его определение da8xx.h определяет макроопределение связанного GPIO.Вам нужно найти определение собственной структуры GPIO платформы, измените это определение макроса, Эта структура очень удобочитаема и понятна с первого взгляда. .code То есть, когда мы пишем приложение, значение, считанное нажатием клавиши, может быть оценено.

2.2 структура gpio_keys_platform_data

Структура platform_data, как следует из ее названия, в основном связана со всеми нашими данными разработки драйверов.

Члены внутри, .buttons Просто мы определяем массив gpio_keys_button, .nbuttons Является ли длина, мы используем функцию макроса ARRAY_SIZE Заполните значение.

2.3 платформа_устройства устройства

Карты разума движутся вперед, нам нужно определить platform_device Структура, эта структура является структурой для работы с ядром Linux. Имя устройства внутри .name , .id , dev 。

2.4 Функция инициализации и выхода

Последнее - это функция инициализации и функция выхода, а последнее, что вам нужно, - это функция переигровки. Зарегистрируйте устройство в ядре.

Когда мы используем insmod name.ko Когда функция, написанная в module_init (), вызывается автоматически, когда мы rmmod name.ko Когда он вызывается, функция, написанная в module_exit (), вызывается автоматически.

3 скомпилируйте ядро ​​или загрузите его как модуль

Мы можем статически скомпилировать этот драйвер в дерево кода ядра или загрузить его в ядро ​​как модуль. Я рекомендую использовать этот модуль для добавления в ядро ​​в начале разработки. После завершения компиляции скомпилируйте ядро. внутри.

3.1 сборник

Укажите путь к исходному файлу ядра, напишите Makefile и загрузите его по произвольному пути на целевой плате после компиляции.

3.1.1 Makefile

Тем более важно здесь:

  • KDIR =? Это местоположение - путь к вашим исходным файлам Linux, (Ядро должно быть скомпилировано заранее)
  • CROSS_COMPILE = Этот кросс-компилятор указан. Моё имя кросс-компилятора странное.

3.1.2 Make для генерации key.ko


После завершения компиляции используйте команду ls, чтобы убедиться, что файл key.ko создан.

Передайте файл key.ko на целевую плату через scp или ftp.

3.2 Загрузка драйвера и просмотр узла монтирования

1) insmod key.ko


2) пройти dmesg Посмотрите, если вывод ядра успешен


3) пройти cat /proc/bus/input/devices Команда для просмотра деталей


Дело в том, что event1, и нам нужно через некоторое время открыть приложение /dev/input/event1 Этот узел устройства выполняет операции на клавиатуре GPIO.

4) Проверка ввода с клавиатуры

Когда мы не пишем приложение, мы также можем выполнить простой тест на клавиатуре GPIO простым методом. Узел монтирования GPIO - это Handlers = event1, затем введите:

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

5) Отцепить драйвер

Если нам не нужен драйвер, нам нужно отключить драйвер:

Вы также можете просмотреть журнал вывода ядра с помощью команды dmesg.

4 Разработка приложений

Создайте файл: key_app.c

Очень простая процедура - вывести регистр. Мы компилируем это:

arm-arago-linux-gnueabi-gcc key_app.c -o key_app.o

Сгенерированный файл key_app.o помещается в каталог Linux целевой платы и затем запускается.

5 Результаты отладки


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

Приложение: исходная программа

Заявление об авторском праве:

1. Эта статья является дополнительной статьей, разработанной командой MULTIBEANS, и не может быть воспроизведена без разрешения.

2. Если содержание этой статьи нарушает авторские права, свяжитесь со мной, и я своевременно удалю его.

3. Уважайте результаты Все ссылки, использованные в этой статье, дают дань уважения самоотверженным инженерам и энтузиастам.

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


Michael.W Поговорите о Hyperledger Fabric. Проблема 20 - Подробная индивидуальная сортировка узла с пятью порядками с исходным кодом для чтения.

Michael.W Поговорите о Hyperledger Fabric. Проблема 20 - Подробная индивидуальная сортировка узла с пятью порядками с исходным кодом чтения Fabric Файл исходного кода одиночного режима находится в ord.


Мяу Пасс Матрица SDUT

Мяу Пасс Матрица SDUT Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Problem Description Лянцзян получил матрицу, но эта матрица была особенно уродливой, и Лянцзян испытал отвращение. Чт.


Гессенская легкая двоичная структура удаленного вызова

Hessian - это легкая двоичная структура удаленного вызова, официальный адрес документа, в основном он включает протокол удаленного вызова Hessian, протокол сериализации Hessian, прокси-сервер клиента .


TCP Pasket и распаковка и Нетти Solutions

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

Данная статья рассчитана на пользователей, имеющих представления об основах 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) и выполняетсяодновременнос основной программой. Также она имеет полный доступ к глобальным переменным и т.д

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

Если вы приобрели 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 относительно простые и общие (например, светодиодные индикаторы), а драйвер 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, поэтому я не буду здесь об этом говорить, это будет очевидно с первого взгляда;

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