Драйвер индикатора ноутбука или планшета gpio

Обновлено: 05.07.2024

ВНИМАНИЕ: статья рассчитана на разработчиков или опытных пользователей и даёт общие рекомендации того, как использовать 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

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

Если вы приобрели 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 не только отдавать команды другим устройством, но и слушать их.А пока можете начинать изучать язык Си. А так же попробуйте изменить эту программу так, чтобы светдиод управлялся бы другим портом и испытайте её.

Для начала, отмечу, завидное постоянство, которое стало традицией для разработчиков, это то, что каждая новая ОС стала запускаться быстрее своей предшественницы, что не может не радовать всех пользователей персональных компьютеров. В целом, эта разница во времени, сократилась в два раза, по сравнению с Windows 8.1.

Еще одним постоянством являются системные требования, которые в принципе, не меняются уже с Windows 7:

Процессор: Не менее 1 ГГц или SoC

ОЗУ: 1 ГБ (для 32-разрядных систем) или 2 ГБ (для 64-разрядных систем)

Место на жестком диске: 16 ГБ (для 32-разрядных систем) или 20 ГБ (для 64-разрядных систем)

Видеокарта: DirectX версии 9 или выше с драйвером WDDM 1.0

Дисплей: 800 x 600

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

Если вы используете свой компьютер на Windows 10, к примеру, еще и для разговоров по скайпу или же как-то еще, то вам понадобится список дополнительных требований, опубликованных компанией-разработчиком новой ОС:

Кортана в настоящее время доступна только в выпусках Windows 10 для Соединенных Штатов, Соединенного Королевства, Китая, Франции, Италии, Германии и Испании.

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

набор микрофонов высокого качества;

драйвер оборудования с указанными пространственными характеристиками микрофонов.

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

Функции Continuum доступны во всех выпусках Windows 10 для настольных компьютеров при включении и выключении вручную режима планшета в Центре поддержки. Планшеты и трансформеры с индикаторами GPIO или индикаторами ноутбука и планшета можно настроить на автоматический переход в режим планшета.

На момент выпуска функции Continuum для телефонов будут доступны только на выбранных моделях премиум-устройств. Внешний монитор должен иметь HDMI-вход. Совместимые с Continuum аксессуары продаются отдельно. Доступность и функции приложения отличаются в зависимости типа устройства и рынка. Для некоторых функций требуется наличие подписки на Office 365.

Потоковая передача музыки и видео с помощью приложений «Музыка» или «Фильмы и ТВ-передачи» доступна только в некоторых регионах. См. актуальный список регионов на странице об Xbox в Windows.

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

Двухфакторная проверка подлинности требует использования ПИН-кода, биометрической проверки (устройства считывания отпечатков пальцев или инфракрасной камеры с подсветкой) или телефона с возможностями Wi-Fi или Bluetooth.

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

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

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

Для защищенной загрузки необходимо встроенное ПО, которое поддерживает UEFI v2.3.1 Errata B и имеет в базе сигнатур UEFI сертификат центра сертификации Microsoft Windows.

Некоторые ИТ-администраторы могут включить защищенный вход (CONTROL+ALT+DELETE) перед отображением экрана входа в систему. На планшетах, не имеющих клавиатуры, должна присутствовать кнопка Windows, так как сочетание клавиш для планшета – кнопка Windows + кнопка питания.

Для обеспечения оптимальной производительности некоторых игр и программ может потребоваться видеоадаптер, совместимый с технологией DirectX 10 или более поздними ее версиями.

Для функции BitLocker To Go необходимо USB-устройство флэш-памяти (только в Windows 10 Pro).

Для функции BitLocker требуется доверенный платформенный модуль TPM 1.2, TPM 2.0 или USB-устройство флэш-памяти (только в Windows 10 Pro и Windows 10 Корпоративная).

Уроки ESP32

В уроке научимся управлять портами ввода-вывода с помощью функций API. Разработаем три учебных проектов.

В этом уроке рассмотрим самые востребованные операции с портами.

  • Установка режима вывода:
    • вход с различными вариантами подключения подтягивающего резистора;
    • выход – активный или с открытым стоком.
    • отключение вывода.

    Работа с прерываниями по изменению состояния выводов, использование их в энергосберегающих режимах и т.п. будут рассмотрены в последующих уроках.

    Функции API управления GPIO-портами.

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

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

    Производится это функцией:

    void gpio_pad_select_gpio(uint8_t gpio_num);

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

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

    esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode);

    • gpio_num – номер вывода;
    • mode – направление. Может принимать значения:
      • GPIO_MODE_INPUT – вход;
      • GPIO_MODE_OUTPUT – активный выход;
      • GPIO_MODE_OUTPUT_OD – выход в режиме открытого стока;
      • GPIO_MODE_INPUT_OUTPUT – вход и выход;
      • GPIO_MODE_INPUT_OUTPUT_OD – вход и выход с открытым стоком;
      • G PIO_MODE_DISABLE – запрещен и вход, и выход.

      Функция возвращает значения:

      • ESP_OK – в случае успешного выполнения;
      • ESP_ERR_INVALID_ARG – в случае неправильно заданных параметров.

      Управление подтягивающим резистором производится функцией

      esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull);

      • gpio_num – номер вывода;
      • pull – режим подтягивающего резистора. Может принимать значения:
        • GPIO_PULLUP_ONLY – подключен к питанию;
        • GPIO_PULLDOWN_ONLY – подключен к земле;
        • GPIO_PULLUP_PULLDOWN – подключен и к питанию, и к земле;
        • GPIO_FLOATING – отключен.

        Функция возвращает значения:

        • ESP_OK – в случае успешного выполнения;
        • ESP_ERR_INVALID_ARG – в случае неправильно заданных параметров.

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

        e sp_err_t gpio_pullup_en(gpio_num_t gpio_num); - резистор подключен к питанию;

        esp_err_t gpio_pullup_dis(gpio_num_t gpio_num); - резистор отключен от питания;

        esp_err_t gpio_pulldown_en(gpio_num_t gpio_num); - резистор подключен к земле;

        esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); - резистор отключен от земли.

        Все функции имеют только один аргумент:

        Еще существует функция, которая позволяет устанавливать режимы сразу для нескольких выводов. Одним ее вызовом можно конфигурировать, например 8ми разрядную шину данных.

        esp_err_t gpio_config(const gpio_config_t * pGPIOConfig)

        Номера выводов, их режимы заданы в элементах структуры pGPIOConfig .

        typedef struct
        uint64_t pin_bit_mask; /* битовая маска вывода */
        gpio_mode_t mode; /* направление вывода */
        gpio_pullup_t pull_up_en; /* разрешение подтягивающего резистора к питанию */
        gpio_pulldown_t pull_down_en; /* разрешение подтягивающего резистора к земле */
        gpio_int_type_t intr_type; /* управление прерыванием */
        > gpio_config_t;

        • pin_bit_mask – это не номер, а маска выводов. Что позволяет задавать режим сразу нескольких выводов. Конечно, если у них одинаковая конфигурация.

        Маска вывода задается, например 1<<2 или GPIO_SEL_2.

        Сразу несколько выводов задаются выражением с логическим ИЛИ масок отдельных выводов.

        pin_bit_mask = GPIO_SEL_2 | GPIO_SEL_5 | GPIO_SEL_10;

        • mode – определяет направление вывода:
          • GPIO_MODE_INPUT – вход;
          • GPIO_MODE_OUTPUT – активный выход;
          • GPIO_MODE_OUTPUT_OD – выход в режиме открытого стока;
          • GPIO_MODE_INPUT_OUTPUT – вход и выход;
          • GPIO_MODE_INPUT_OUTPUT_OD – вход и выход с открытым стоком;
          • G PIO_MODE_DISABLE – запрещен и вход, и выход.
          • GPIO_PULLUP_DISABLE – резистор отключен;
          • GPIO_PULLUP_ENABLE – резистор подключен.
          • GPIO_PULLDOWN_DISABLE – резистор отключен;
          • GPIO_PULLDOWN_ENABLE – резистор подключен.

          Чтобы конфигурировать нужные выводы необходимо задать элементы структуры типа gpio_config_t и вызвать функцию gpio_config с указателем на структуру в качестве аргумента.

          Например, следующие строки конфигурируют выводы 2, 5 и 10.

          gpio_config_t conf_gpio; // объявление структуры конфигурации
          conf_gpio .pin_bit_mask = GPIO_SEL_2 | GPIO_SEL_5 | GPIO_SEL_10; // выводы
          conf_gpio.mode = GPIO_MODE_INPUT; // входы
          conf_gpio.pull_up_en = GPIO_PULLUP_ENABLE; // резистор на питание
          conf_gpio.pull_down_en = GPIO_PULLDOWN_DISABLE; // без резистора на землю
          conf_gpio.intr_type = GPIO_PIN_INTR_DISABLE; // прерывание запрещено

          gpio_config(&conf_gpio); // установка конфигурации

          Осталось выяснить, как устанавливать состояние выходов и считывать состояние входов. Для этого есть две простые функции.

          esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level)

          Устанавливает выход номер gpio_num в состояние level .

          Аргумент level – может иметь значения:

          • 0 – низкий уровень;
          • 1 – высокий уровень.

          int gpio_get_level(gpio_num_t gpio_num)

          Возвращает состояние вывода с номером gpio_num .

          • 0 – низкий уровень;
          • 1 – высокий уровень.

          Пока все функции, которые нам необходимы. Даже без примеров все просто и понятно.

          Структура программы ESP-IDF.

          Структура программы в среде ESP-IDF не отличается от типичной структуры C-программ. Это последовательность директив препроцессора, описаний, определений, глобальных объектов и функций.

          Единственная особенность – главная функция должна иметь имя app_main() .

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

          Заставим светодиод, подключенный к выводу 2, мигать с частотой 2 раза в секунду.

          Создадим новый проект. Последовательность действий описана в конце урока 3.

          • В рабочем каталоге урока 5 я создал паку Lesson5_1.
          • Копировал в нее содержимое папки шаблона проекта template из урока 3.
          • Задал имя проекта Lesson5_1 в файлах CMakeLists.txt и Makefile.
          • Открыл проект в VS Code.
          • Удалил содержимое файла app_main.c. Оставил пустую страницу. Ее и будем заполнять исходным кодом.

          Напишем название программы

          // светодиод на плате мигает 2 раза в секунду

          Подключим необходимые файлы библиотек.

          Этими файлами подключим операционную систему реального времени FreeRTOS. О ней поговорим в последующих уроках. Это обширная тема. В данном приложении она нужна только для формирования временных задержек. Работать с таймерами мы еще не умеем, а FreeRTOS позволит нам использовать функцию vTaskDelay , которая останавливает выполнение задачи на заданное время.

          Эта директива необходима для подключения API драйвера управления GPIO. Без нее выше описанные функции управления портами работать не будут.

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

          Создаем главную функцию, внутри которой будет основной код.

          Конфигурируем вывод светодиода как активный выход.

          gpio_pad_select_gpio(LED_GPIO); // режим ввода-вывода
          gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT); / / активный выход

          Остается создать бесконечный цикл, в котором зажигать и гасить светодиод с задержками 0,25 секунд.

          while(1)
          gpio_set_level(LED_GPIO, 0); // светодиод погашен
          printf("Светодиод погас\n");
          vTaskDelay(250 / portTICK_PERIOD_MS); //задержка 0,25 сек
          gpio_set_level(LED_GPIO, 1); / / светодиод горит
          printf("Светодиод горит\n");
          vTaskDelay(250 / portTICK_PERIOD_MS); / /задержка 0,25 сек
          >

          Проект можно загрузить по ссылке.

          Зарегистрируйтесь и оплатите. Всего 60 руб. в месяц за доступ ко всем ресурсам сайта!

          Компилируем, загружаем в FLASH, проверяем.

          Модифицируем предыдущую программу. Добавим кнопку и реализуем алгоритм, при котором нажатие кнопки уменьшает частоту мигания светодиодов в 2 раза.

          Я подключил кнопку между выводом 13 и сигналом GND.

          Копировал предыдущий проект. Изменил имя на Lesson5_2.

          Добавил определение вывода кнопки.

          Конфигурировал вывод кнопки, как вход с подтягивающим резистором.

          gpio_pad_select_gpio(BUTTON_GPIO); // режим ввода-вывода
          gpio_set_direction(BUTTON_GPIO, GPIO_MODE_INPUT); // вход
          gpio_pullup_en(BUTTON_GPIO); // резистор подключен к питанию
          gpio_pulldown_dis(BUTTON_GPIO); // резистор отключен от земли

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

          if( gpio_get_level(BUTTON_GPIO) != 0 ) vTaskDelay(250 / portTICK_PERIOD_MS); //задержка 0,25 сек
          else vTaskDelay(500 / portTICK_PERIOD_MS); //задержка 0,5 сек

          Зарегистрируйтесь и оплатите. Всего 60 руб. в месяц за доступ ко всем ресурсам сайта!

          Согласитесь, работать с портами ESP32 очень просто.

          Функции работы с несколькими битами одновременно.

          Существуют еще 4 функции недокументированные в API, которые позволяют значительно ускорить работу с портами.

          Функции gpio_set_level и gpio_get_level позволяют устанавливать и считывать состояние только одного вывода. Но часто возникает необходимость работать с несколькими битами одновременно. Это не только ускоряет работу программы, но иногда бывает абсолютно необходимым. Например, считывать шину данных по стробу или устанавливать сигналы, временные параметры которых критичны к сдвигу начальной фазы.

          uint32_t gpio_input_get(void);

          Функция считывает и возвращает одним словом состояние выводов 0-31.

          uint32_t gpio_input_get_high(void);

          Функция считывает и возвращает состояние выводов 32-39.

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

          void gpio_output_set(uint32_t set_mask, uint32_t clear_mask, uint32_t enable_mask, uint32_t disable_mask);

          Функция устанавливает, сбрасывает состояние выводов 0-31, а также разрешает и запрещает их работу.

          • set_mask – маска выводов, которые необходимо установить;
          • clear_mask – маска выводов, которые необходимо сбросить;
          • enable_mask – маска выводов, работу которых необходимо разрешить;
          • disable_mask – маска выводов, работу которых необходимо запретить.

          void gpio_output_set_high(uint32_t set_mask, uint32_t clear_mask, uint32_t enable_mask, uint32_t disable_mask);

          Функция устанавливает, сбрасывает состояние выводов 32-39, а также разрешает и запрещает их работу. В остальном формат аналогичен предыдущей функции.

          Кроме управления состоянием сигналов на выходах функции еще разрешают и запрещают их работу, что удобно при организации двунаправленных сигналов и шин данных.

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

          Зарегистрируйтесь и оплатите. Всего 60 руб. в месяц за доступ ко всем ресурсам сайта!

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

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