Использование интерфейса I2C в ESP32 с помощью ESP-IDF


В этом уроке мы изучим интерфейс связи I2C в ESP32 и рассмотрим осуществление связи типа «ведущий-ведомый» (master - slave) между двумя платами разработки ESP32 с использованием ESP-IDF. Мы будем использовать драйвер I2C для демонстрации связи между ведущим (хостом) и ведомым (устройством) устройствами по протоколу I2C, при этом ведущее устройство будет отправлять сообщение ведомому устройству для переключения встроенного светодиода.

Также про использование интерфейса I2C в ESP32 на нашем сайте вы можете прочитать в статье протоколы связи в ESP32-S3 с помощью ESP-IDF - возможно некоторые моменты в ней объяснены более понятным языком чем в данной статье.

Принципы работы протокола I2C 

Протокол связи I2C — это двухпроводной многомастерный последовательный протокол связи, используемый для передачи данных на короткие расстояния. Он представляет собой асинхронный полудуплексный протокол последовательной связи. Более того, для последовательной передачи данных требуется всего два провода: линии SCL и SDA. SCL — это двунаправленная линия тактового сигнала, а SDA — двунаправленная линия передачи данных.

Шина I2C состоит из множества ведомых и множества ведущих устройств. Устройство, подключенное к шине I2C, может выступать как в роли ведущего, так и в роли ведомого. Ведущее устройство запускает процесс передачи данных. Обычно имеется одно ведущее устройство, которое управляет одним или несколькими ведомыми устройствами, подключенными к той же шине I2C через подтягивающие резисторы. Ведомые устройства идентифицируются по своему уникальному 7-битному адресу.

Соединение I2C между устройствами

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

На приведенной ниже схеме показан один ведущий узел и несколько ведомых узлов, соединенных одной шиной.

Пример соединения I2C между одним ведущим и несколькими ведомыми узлами

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

Контакты I2C в ESP32

Плата разработки ESP32 поставляется с двумя контроллерами I2C, также известными как порты. Эти контроллеры управляют связью по шине I2C. Они могут работать как в режиме ведущего, так и ведомого устройства.

На приведенной ниже схеме показаны стандартные контакты I2C, присутствующие на плате ESP32 DevKit V1. Как вы можете заметить, здесь стандартным контактом I2C для SDA является GPIO21, а для SCL — GPIO22. Вы также можете изменить их в коде.

Контакты I2C в ESP32

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

Связь ведущий-ведомый по интерфейсу I2C в ESP32 с помощью ESP-IDF

В этом разделе мы создадим и протестируем проект, использующий библиотеку драйверов I2C для ESP-IDF. Проект состоит из двух примеров: ведущего и ведомого устройств, которые будут установлены на отдельных платах ESP32. Платы будут соединены друг с другом через контакты связи I2C, которые будут заданы в коде. API драйвера I2C будут использоваться для программирования как ведущего, так и ведомого устройств. Оба примера должны быть загружены на соответствующие платы ESP32, соединенные друг с другом, для обеспечения передачи данных по протоколу I2C.

Создание примера проекта

Откройте VS Code и перейдите в меню View > Command Palette («Вид» > «Палитра команд»). Введите в строке поиска ESP-IDF: New Project и нажмите Enter.

Укажите название проекта и каталог. Мы назвали наш проект «ESP32_I2C_MASTER». Для платы ESP-IDF мы выбрали опцию custom board («Пользовательская плата»). В качестве целевого модуля ESP-IDF мы выбрали модуль ESP32. Нажмите кнопку ‘Choose Template’ («Выбрать шаблон»), чтобы продолжить.

Создание примера проекта для обеспечения передачи данных по протоколу I2C

В разделе Extension («Расширение») выберите опцию ESP-IDF:

Выбор опции ESP-IDF в разделе Extension («Расширение»)

На вкладке getting-started («Начало работы») мы щелкнем по пункту «sample_project». Затем нажмем  ‘Create project using sample_project’ («Создать проект, используя sample_project»).

Создание проекта с использованием шаблона sample_project

Вы получите уведомление о создании проекта. Чтобы открыть проект в новом окне, нажмите «Да».

Это откроет созданный нами проект ESP32_I2C_MASTER во вкладке EXPLORER. Внутри папки проекта находится несколько папок. Это относится ко всем проектам, которые вы будете создавать с помощью ESP-IDF Explorer. Перейдем к файлу main.c. Перейдите в  main > main.c  и откройте его. Скопируйте приведенный ниже код из этого файла и сохраните его.

Код ESP-IDF для ведущего I2C

Как работает код?

Для начала мы подключим необходимые библиотеки, включая driver/i2c.h для связи по протоколу I2C и esp_log.h в качестве библиотеки для вывода информационных сообщений и т.д.

Этот код использует информационное логирование. Функция логирования принимает два аргумента. Первый аргумент — это тег, а второй — отформатированная строка. Поэтому эта глобальная переменная будет полезна при вызове функций ESP_LOGI(). Здесь «i2c-master» — это тег, который будет использоваться при логировании.

Далее мы определили различные параметры для ведущего устройства I2C. Это включает в себя контакты SCL и SDA, которые будут использоваться для связи по протоколу I2C. Используйте эти контакты GPIO для соединения двух плат ESP32. Вы также можете изменить эти контакты.

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

Номер I2C-порта для ведущего устройства установлен равным '0'.

Инициализация ведущего устройства I2C

Следующая функция отвечает за инициализацию ведущего устройства I2C.

Сначала определяются параметры структуры i2c_config_t. Это включает в себя режим работы (в данном случае — ведущий), контакты связи I2C, настройку внутренних подтягивающих резисторов ESP32 и установку тактовой частоты I2C.

После установки параметров выполняется инициализация конфигурации I2C для конкретного порта. Это достигается путем вызова функции i2c_param_config(), которая принимает два параметра. Первый параметр — это порт I2C, а второй — указатель на константную структуру конфигурации типа i2c_config_t. На этом этапе выполняется настройка порта I2C.

Следующий шаг — установка драйвера I2C. Он принимает несколько параметров, перечисленных ниже:

  • Номер порта I2C.
  • Режим конфигурации, который мы устанавливаем (ведущий/ведомый).
  • Отключение буфера приема. Это размер буфера приема, применяемый к ведомому устройству.
  • Отключение буфера TX. Это размер буфера отправки, который также применяется к ведомому устройству.
  • Последний параметр — 'intr_alloc_flags', который задает флаги для выделенного прерывания. В нашем случае он равен '0', поскольку в этом примере мы не используем прерывания.

Передача

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

Для начала мы создадим и инициализируем канал связи I2C с помощью функции i2c_cmd_link_create(). Эта функция вернет обработчик канала связи I2C.

После создания командной связи мы вызовем функцию i2c_master_start() и передадим ей командную связь в качестве параметра. Это позволит ведущему устройству I2C сгенерировать стартовый сигнал. Оно будет передавать все команды из очереди.

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

Затем мы вызываем функцию i2c_master_write_byte() для ведущего устройства I2C, чтобы записать один байт в шину I2C. Эта функция принимает три параметра: команду I2C link, команду I2C на запись одного байта в шину и проверку подтверждения включения для ведущего устройства.

Далее мы вызываем функцию i2c_master_write() для ведущего устройства I2C, чтобы записать буфер в шину I2C. Эта функция принимает четыре параметра: команду I2C, передаваемые данные, длину данных и подтверждение включения для ведущего устройства.

Затем мы вызываем функцию i2c_master_cmd_begin(), которая позволит ведущему устройству I2C начать отправку команд из очереди. Эта функция принимает три параметра. Первый параметр — номер порта I2C, второй — обработчик команд I2C, а третий — максимальное количество тактов ожидания, которое в данном случае установлено на 1 секунду.

После этого мы освободим канал связи, когда закончим отправку команд. Это достигается путем вызова функции i2c_cmd_link() и передачи в нее обработчика команд I2C в качестве параметра.

app_main()

Внутри функции app_main() мы сначала создадим два массива, которые будут содержать команды on_command и off_command. Массив on_command[] будет содержать строку “LED_ON”, а массив off_command[] — строку “LED_OFF”. Затем мы вызовем функцию i2c_master_init() для инициализации I2C-мастера. Внутри бесконечного цикла мастер будет непрерывно отправлять команды on_command и off_command с задержкой в ​​1 секунду между ними, используя функцию i2c_master_send(), которую мы определили ранее.

Компиляция скетча

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

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

Тестирование работы в терминале I2C ведущего

Теперь давайте настроим I2C-приемник, который будет принимать эти команды и в результате переключать свой встроенный светодиод.

Использование ESP32 в качестве ведомого устройства I2C с помощью ESP-IDF

Откройте VS Code и перейдите в меню View > Command Palette («Вид» > «Палитра команд»). Введите в строке поиска ESP-IDF: New Project и нажмите Enter.

Укажите название проекта и каталог. Мы назвали наш проект «ESP32_SPI_SLAVE». Для платы ESP-IDF мы выбрали опцию custom board («Пользовательская плата»). В качестве целевой платформы ESP-IDF мы выбрали модуль ESP32. Нажмите кнопку ‘Choose Template’ («Выбрать шаблон»), чтобы продолжить.

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

В разделе Extension («Расширение») выберите опцию ESP-IDF:

Выбор опции ESP-IDF в разделе Extension («Расширение»)

На вкладке getting-started («Начало работы») мы щелкнем по пункту «sample_project». Затем нажмем  ‘Create project using sample_project’ («Создать проект, используя sample_project»).

Создание проекта с использованием шаблона sample_project

Вы получите уведомление о создании проекта. Чтобы открыть проект в новом окне, нажмите «Да».

Это откроет созданный нами проект ESP32_I2C_SLAVE во вкладке EXPLORER. Внутри папки проекта находится несколько папок. Это относится ко всем проектам, которые вы будете создавать с помощью ESP-IDF Explorer. Перейдем к файлу main.c. Перейдите в main > main.c и откройте его. Скопируйте приведенный ниже код из этого файла и сохраните его.

Код ESP-IDF для ведомого I2C

Как работает код?

Для начала мы подключим необходимые библиотеки, включая driver/i2c.h для связи по протоколу I2C, esp_log.h в качестве библиотеки для вывода информационных сообщений и driver/gpio.h для управления цифровыми выходными контактами ESP32 и т.д.

Здесь «i2c-slave» — это тег, который будет использоваться при регистрации данных.

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

Далее мы определили различные параметры для ведомого устройства I2C. Это включает в себя контакты SCL и SDA, которые будут использоваться для связи по протоколу I2C. Используйте эти контакты GPIO для соединения двух плат ESP32. Это те же самые контакты, которые мы ранее настроили как контакты I2C для ведущего устройства ESP32.

Кроме того, к другим параметрам относятся частота тактового сигнала ведомого устройства I2C, адрес ведомого устройства, длина буфера передачи и приема, бит записи, бит чтения и т. д.

Номер I2C-порта для ведомого устройства установлен равным '0'.

Инициализация ведомого устройства I2C

Следующая функция отвечает за инициализацию ведомого устройства I2C.

Сначала определяются параметры структуры i2c_config_t. Это включает в себя режим работы (в данном случае — ведомое устройство), контакты связи I2C, настройку внутренних подтягивающих резисторов ESP32, адрес ведомого устройства и т. д.

После установки параметров выполняется инициализация конфигурации I2C для конкретного порта. Это достигается путем вызова функции i2c_param_config(), которая принимает два параметра. Первый параметр — это порт I2C, а второй — указатель на константную структуру конфигурации типа i2c_config_t. На этом этапе выполняется настройка порта I2C для ведомого устройства.

Следующий шаг — установка драйвера I2C. Он принимает несколько параметров, перечисленных ниже:

  • Номер порта I2C.
  • В данном случае мы установили режим конфигурации, который является подчиненным.
  • Длина буфера приема. Это размер буфера приема, применимый только для ведомого устройства.
  • Длина буфера TX. Это размер буфера отправки, который применяется только к ведомому устройству.
  • Последний параметр — 'intr_alloc_flags', который задает флаги для выделенного прерывания. В нашем случае он равен '0', поскольку в этом примере мы не используем прерывания.

app_main()

Внутри функции app_main() мы сначала создадим массив размером «I2C_SLAVE_RX_BUF_LEN», который будет хранить полученные данные.

Затем мы настроим вывод LED_PIN как вывод GPIO. Для этого мы воспользуемся функцией gpio_pad_select_gpio(). Укажите вывод LED_PIN в качестве параметра внутри этой функции. Затем мы установим направление вывода как выходное, используя функцию gpio_set_direction(). Эта функция принимает два аргумента. Первый аргумент — это вывод GPIO, а второй — режим (вход или выход), в котором мы хотим установить вывод. В нашем случае мы хотим установить вывод LED_PIN как выходной вывод.

Затем мы вызываем функцию i2c_slave_init() для инициализации ведомого устройства I2C.

Внутри бесконечного цикла сначала ведомое устройство I2C считывает данные из внутреннего буфера с помощью функции i2c_slave_read_buffer(). Внутренний буфер сохраняет полученные данные, которые затем можно прочитать. Эта функция принимает четыре параметра. Первый параметр — номер порта. Второй параметр — массив, в котором хранятся полученные данные из внутреннего буфера. Третий параметр — размер данных, которые будут считаны. В данном случае это длина буфера приема. Четвертый параметр — максимальное количество тактов ожидания, которое в данном случае установлено равным 1 секунде.

Затем мы сбросим I2C RX FIFO, вызвав функцию i2c_reset_rx_fifo(). Эта функция принимает один параметр — номер I2C-порта.

Для проверки полученной строки мы будем использовать условие if-else-if. Если полученная строка соответствует "LED_ON", то встроенный светодиод включится, установив уровень контакта LED_PIN в 1. Аналогично, если полученная строка соответствует "LED_OFF", то встроенный светодиод выключится, установив уровень контакта LED_PIN в 0. Последовательный терминал также будет непрерывно отображать полученное сообщение.

Компиляция скетча

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

В нашем случае ведущая плата ESP32 подключена к COM5, а ведомая плата ESP32 — к COM14.

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

ESP32 Master ESP32 Slave
SDA GPIO21 GPIO21
SCL GPIO22 GPIO22
GND GND

Схема конфигурации ESP32 I2C с одним ведущим и одним ведомым устройством

Вот что мы получаем на терминале ведомого ESP32:

Тестирование работы ведомого ESP32 в терминале последовательной связи

Ведомый модуль ESP32 непрерывно получает команды LED_ON и LED_OFF с задержкой в ​​1 секунду между ними. Это приводит к тому, что его встроенный светодиод, подключенный к GPIO2, переключается каждую секунду.

(Проголосуй первым!)
Загрузка...
1 просмотров

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

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