Добрый день, уважаемый читатель! В этой статье обсудим методы работы со встроенными портами ввода-вывода GPIO в цифровом режиме.
Если вы создавали скетчи для Arduino IDE, то наверное, знаете, как осуществляется работа с GPIO для Arduino:
pinMode(10, OUTPUT); // Настраиваем PIN10 на выход
digitalWrite(10, HIGH); // Записываем в PIN10 высокий уровень
В данном случае pinMode настраивает порт ввода-вывода на режим "выход", а digitalWrite служит для записи в ранее настроенный порт логической единицы (или нуля). Это унифицированные функции Arduino, которые "внешне" не зависят от аппаратной платформы, а вот их внутренняя реализация будет зависеть от того, какой микроконтроллер вы используете. Это позволяет сравнительно легко переносить код с одного микроконтроллера на другой без адаптации (на самом деле это не всегда прокатывает, но тем не менее, разработчики платформы Arduino к этому стремятся).
На ESP-IDF необходимости в такой унификации нет, поэтому используются более специфичные функции. Их мы сегодня и обсудим.
Какие выводы GPIO можно использовать для ввод и вывод
Давайте вспомним, какие выводы можно использовать на ESP32 (я буду рассматривать линейки ESP32-WROOM или ESP32-WROVER).
Чип ESP32 имеет 34 физических контакта GPIO. Каждая контактная площадка может использоваться как вход/выход общего назначения (GPIO) или может быть подключена к внутреннему периферийному сигналу. Мультиплексоры IO_MUX, RTC IO_MUX и матрица GPIO отвечают за маршрутизацию сигналов от периферийных устройств к контактам GPIO. Вместе эти системы обеспечивают гибко настраиваемый ввод-вывод.
- На ввод и вывод для ESP32-WROOM можно смело использовать следующие 18 выводов GPIO: 2, 4, 5, 12, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33. Все указанные выводы имеют встроенные программно-подключаемые резисторы подтяжки 45 кОм (в документации это называется "слабая подтяжка"). Я буду называть эти порты "универсальными", для простоты понимания. Некоторые из них можно назначить для использования различных интерфейсов: I2C, SPI и т.д. Примечания: выводы 5, 12, 14, 15 использовать следует с осторожностью; для ESP32-WROVER выводы 16 и 17 использовать нельзя.
- Выводы GPIO 34, 35, 36, 39 можно использовать только на ввод, и у них отсутствуют встроенные резисторы слабой подтяжки. На эти же выводы выведен канал ADC1.
- Можно ещё использовать GPIO 0, но так как он используется в схеме сброса контроллера после прошивки, его нельзя подтягивать его к питанию через резистор.
Более подробно об выводах GPIO можно почитать здесь:
На все эти выводы можно подключить обработчики прерываний для отслеживания изменения уровня извне микроконтроллера, но прерывания обсудим в следующей статье.
Хочу обратить ваше внимание только на то, что, согласно спецификации, выводы GPIO допускают ток аж до 40 mA "высокого" уровня и до 28 mA "низкого" уровня. Это позволяет управлять различными слаботочными устройствами типа светодиодов напрямую, без применения коммутирующих транзисторов, нужно только учитывать напряжение на выводе не более 3.3В.
Настройка порта GPIO
Для работы с портами GPIO необходимо подключить модуль "driver/gpio.h":
# include "driver/gpio.h"
Как и в Arduino IDE, прежде чем начинать работу с GPIO, его нужно настроить (сконфигурировать). Каждый "универсальный" порт можно настроить:
- направление: на вход или выход
- слабая подтяжка (45 КОм) к +3,3В или к "земле"
- прерывание (если необходимо)
ESP-IDF предлагает два метода конфигурации:
- Пакетная конфигурация сразу нескольких портов: с помощью функции gpio_config (const gpio_config_t* pGPIOConfig). Эта функция позволяет за один вызов настроить все параметры для выбранных портов: направление, подтяжки, прерывания. Соответственно это чуть более быстрый способ.
- С помощью набора функций gpio_set_direction, gpio_set_pull_mode, и т.д. Это более "детализированные" функции и работают они только для одного выбранного порта. Я чаще использую именно этот способ, скорее всего "по привычке" (так как они более похожи на способ из Arduino IDE).
Выбор вывода для работы в режиме GPIO
Прежде всего необходимо настроить GPIO для использования в режиме ввода-вывода (так как GPIO на ESP32 могут быть использованы для разных целей с помощью мультиплексора выводов IO_MUX). Разработчики ESP32 не гарантируют, что после аппаратного сброса микроконтроллера все его выводы установятся в режим ввода-вывода. Поэтому операцию перевода нужных выводов в режим GPIO желательно делать всегда.
Сделать это можно с помощью функции:
esp_err_t gpio_reset_pin(gpio_num_t gpio_num)
где:
- gpio_num - номер вывода GPIO
Эта функция настраивает IOMUX для этого вывода на работу с GPIO, включает встроенную слабую подтяжку и отключает режимы работы на вход и на выход (то есть устанавливается режим GPIO_MODE_DISABLE – см. следующий раздел).
Для этой же цели можно воспользоваться другой функцией, которая просто перенастраивает IOMUX для этого вывода на работу с GPIO:
void gpio_pad_select_gpio(uint8_t gpio_num)
где:
- gpio_num – номер вывода GPIO
Режимы работы портов ввода-вывода
ESP32 поддерживает несколько режимов GPIO:
- GPIO_MODE_DISABLE - порт отключён
- GPIO_MODE_INPUT - порт работает только на вход
- GPIO_MODE_OUTPUT - порт работает только на выход
- GPIO_MODE_OUTPUT_OD - порт работает только на выход в режиме "открытый коллектор" (open-drain)
- GPIO_MODE_INPUT_OUTPUT_OD - порт может работать одновременно и на вход и на выход с открытым коллектором
- GPIO_MODE_INPUT_OUTPUT - порт может работать одновременно и на вход и на выход
Для выбора режима порта используйте функцию
esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
где:
- gpio_num - идентификатор GPIO
- mode - режим работы
Переключить режим работы порта можно в любой момент, не обязательно делать это только при старте прошивки.
Встроенная подтяжка
Большинство портов имеют встроенные резисторы слабой подтяжки. Чтобы их задействовать, существует несколько функций.
Универсальная функция:
esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
где:
- gpio_num - идентификатор GPIO
- pull - режим подтяжки
Режим gpio_pull_mode_t может принимать одно из нескольких значений:
- GPIO_PULLUP_ONLY - подтяжка к питанию +3,3В
- GPIO_PULLDOWN_ONLY - подтяжка к "земле"
- GPIO_PULLUP_PULLDOWN - подтяжка одновременно к питанию +3,3В и "земле"
- GPIO_FLOATING - подтяжка отключена
Однако можно использовать и несколько более простых функций:
Какой метод использовать - выбирайте на вкус.
Настройка допустимого выходного тока
Для GPIO, работающих на выход, ESP32 позволяет задать максимальный ток через выход. Это может быть полезно в некоторых случаях, например можно ограничить ток через светодиод без использования резистора. Для этого воспользуйтесь функцией:
esp_err_t gpio_set_drive_capability (gpio_num_t gpio_num , gpio_drive_cap_t strength)
где:
- gpio_num - идентификатор GPIO
- strength - максимально допустимый ток
Режим gpio_drive_cap_t может принимать одно из нескольких значений:
- GPIO_DRIVE_CAP_0 - слабый, до ~5мА
- GPIO_DRIVE_CAP_1 - сильнее, до ~10мА
- GPIO_DRIVE_CAP_2 - средний (по умолчанию), до ~20мА
- GPIO_DRIVE_CAP_3 - максимальный, до ~40мА
Как видите, по умолчанию ток высокого уровня ограничен на уровне 20мА. В большинстве случаев вызывать данную функцию при настройке порта не требуется. Но если ваше устройство на ESP32 требует большего тока (например при управлении мощным биполярным транзистором), то вы можете столкнуться с "неправильным" поведением.
Запись логического уровня в GPIO
Для записи данных в выходной порт необходимо воспользоваться функцией:
esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level)
где:
- gpio_num - идентификатор GPIO
- level - логический уровень, 0 (низкий) или 1 (высокий)
Ничего сложного, всё предельно просто.
Чтение логического уровня из GPIO
Для чтения данных из порта необходимо воспользоваться другой функцией:
uint32_t gpio_get_level(gpio_num_t gpio_num)
где:
- gpio_num - идентификатор GPIO
Если GPIO не настроен для ввода (или ввода и вывода), возвращаемое значение всегда равно 0.
Практическое использование GPIO в режиме вывода - мигаем светодиодом
Допустим, мы хотим использовать светодиод, подключенный к одному из выводов через токоограничительный резистор, например это GPIO12. Классика жанра.
Светодиод можно подключить двумя способами:
- С управлением по высокому уровню. Анод через резистор к GPIO, катод - к общему проводу. В этом случае следует использовать режим GPIO_MODE_OUTPUT.
- С управлением по низкому уровню. Анод к +3,3В, катод через резистор к GPIO. В этом случае лучше использовать режим GPIO_MODE_OUTPUT_OD.
Для ESP32 наверное оптимальнее использовать первый способ, так как ток GPIO для высокого уровня может достигать до 40мА, а для низкого - только 28мА. Хотя для светодиода вполне достаточно 10мА, в крайнем случае - 20мА (для старых советских светодиодов зеленого цвета).
Настройка вывода будет выглядеть так:
Для мигания светодиодов создадим задачу (пример создания задач мы рассматривали в одной из предыдущих статей).
Функция задачи для мигания светодиодом будет выглядеть примерно так:
Использование GPIO для пробуждения микроконтроллера
Если вы используете в своих разработках режим глубокого сна (например при питании от батарей), то вы можете использовать вывод GPIO для пробуждения микроконтроллера по внешнему сигналу. Задействовать данную функцию можно с помощью функции:
esp_err_t gpio_wakeup_enable (gpio_num_t gpio_num, gpio_int_type_t intr_type)
где:
- gpio_num - идентификатор GPIO
- intr_type - логический уровень на GPIO для пробуждения. Можно использовать только GPIO_INTR_LOW_LEVEL или GPIO_INTR_HIGH_LEVEL.
Отключить пробуждение можно с помощью функции gpio_wakeup_disable (gpio_num_t gpio_num).
Ну вот и всё, о чем я хотел рассказать в данной статье. Пример вы можете посмотреть на GitHub.
Прерывания по изменению уровня на GPIO мы обсудим в следующей статье, так как это отдельная большая тема.
Полезные ссылки
_______________
На этом пока всё, до встречи на сайте и на dzen-канале!
👍 Понравилась статья? Поддержите канал лайком или комментарием! Каналы на Дзене "живут" только за счет ваших лайков.
📌Подпишитесь на канал и вы всегда будете в курсе новых статей.
🔶 Полный архив статей вы найдете здесь
Благодарю за вашу поддержку! 🙏