Перейти к содержимому

Работа с портами ввода-вывода GPIO из ESP-IDF

Добрый день, уважаемый читатель! В этой статье обсудим методы работы со встроенными портами ввода-вывода 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-DevKitC V4

Порты ввода-вывода для ESP32-DevKitC V4

  • На ввод и вывод для ESP32-WROOM можно смело использовать следующие 18 выводов GPIO: 4, 14, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33. Все указанные выводы имеют встроенные программно-подключаемые резисторы подтяжки 45 кОм (в документации это называется “слабая подтяжка”). Я буду называть эти порты “универсальными”, для простоты понимания. Некоторые из них можно назначить для использования различных интерфейсов: I2C, SPI и т.д. Примечания: для ESP32-WROVER выводы 16 и 17 использовать нельзя.
  • Выводы GPIO 34, 35, 36, 39 можно использовать только на ввод, и у них отсутствуют встроенные резисторы слабой подтяжки. На эти же выводы выведен канал ADC1.
  • Можно ещё использовать GPIO 0, 2, 5, 12, 15, но с учетом того, что их нельзя подтягивать ни к питанию, ни к земле при старте микроконтроллера.

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

Хочу обратить ваше внимание только на то, что, согласно спецификации, выводы 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_directiongpio_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 – порт может работать одновременно и на вход и на выход

В режимах с открытым коллектором (OD) микроконтроллер управляет только низким логическим уровнем, при установке на GPIO логической единицы вывод отключается и остается “болтаться в воздухе”. Этот режим удобно использовать для датчиков типа DHT11-22 или для управления светодиодами “по низкому уровню” (катодом к выводу, анодом к +3,3В).

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

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 мы обсудим в следующей статье, так как это отдельная большая тема.

 
 

Полезные ссылки

  1. ESP-IDF — GPIO and RTC GPIO.
  2. Пример на GitHub
Метки:

4 комментария для “Работа с портами ввода-вывода GPIO из ESP-IDF”

    1. Хороший вариант, согласен. Не обращал внимания на эту функцию, спасибо. Надо будет дополнить статью.
      PS: В данном случае сработал обычный принцип: “Работает и ладно”. Как у кого-то когда-то прочитал, так до сих пор сам и делаю.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *