ESP32 Урок 6. I2C. Подключаем внешний EEPROM

 

 

 

 На данном уроке мы попробуем поработать с шиной I2c.

С данной шиной мы раньше также работали с применением других микроконтроллеров. Настало время поработать с ней, подключив по ней что-нибудь к контроллеру ESP32.

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

Мы не будем уходить от сложившихся традиций и подключим, как обычно, к данной шине микросхему памяти EEPROM — AT24C32, которая установлена в модуле с часовой микросхемой DS3231 и также в часовом модуле с микросхемой DS1307.

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

Выглядит модуль DS1307, с которым мы будем работать на данном уроке вот так

 

 

Подключим модуль к нашей отладочной плате, только провода для SCL и SDA возьмём с ещё одним отводом для подключения логического анализатора, чтобы потом наглядно увидеть, как будут передаваться данные по шине, а заодно и скорость их передачи. Подключим сразу и логический анализатор

 

 

 

Посмотрим распиновку нашей платы

 

 

Здесь мы видим, что ножки, предназначенные для I2C, то бишь SDA и SCL у нас соответствуют ножкам портов GPIO21 и GPIO22. Так мы их и подключили.

Теперь сразу к проекту. Он был сделан из проекта прошлого урока  с именем EXTI01 и назван был I2C_EEPROM.

Как всегда, сначала конфигурирование, поэтому после добавления проекта в дерево проектов среды Espressif IDE мы первым делом откроем файл Kconfig.projbuild и внесём там следующие изменения. Мы уберём пункт для ножки светодиода, а пункты для входов исправим под требования нашего нового проекта. Я приведу весь текст данного файла

 

 

Соберём проект и посмотрим, что у нас в конфигураторе

 

 

В конфигураторе всё нормально. Значит будем в проекте будем пользоваться назначенными здесь макросами.

В main.c удалим все макросы, глобальные переменные и  функции с телами, за исключением app_main, в которой оставим только бесконечный цикл

 

 

Объявим макрос, в котором будет храниться адрес микросхемы

 

 

Подключим библиотеку для работы с I2C, а также библиотеку для отслеживания ошибок

 

 

Объявим глобальный указатель на строку для отображения логов

 

 

В функции app_main объявим целочисленную переменную, переменную для номера модуля и переменную для кода ошибки

 

 

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

 

 

 

Подключим наш новый модуль в файле main.c

 

 

Перейдём в файл at24c.c и добавим там функцию инициализации периферии

 

 

На входе мы здесь получаем номер модуля, адрес микросхемы и номера портов для ножек SDA и SCL.

Создадим в заголовочном файле прототип на нашу функцию и вызовем её в app_main файла main.c

 

 

Если мы сейчас соберём код, то мы получим ошибку. Дело в том, что мы видим только прототип функции, а реализацию не видим. Если мы добавляем файлы реализации в дерево проекта, это не значит, что они будут видны из кода и будут выполняться. Чтобы полностью подключить такой файл в проект, откроем файл CMakeLists.txt в каталоге main и добавим в перечень подключаемых файлов наш новый файл

 

set(COMPONENT_SRCS "main.c at24c.c")

 

При сборке теперь будет только одна ошибка — отсутствие возвращаемого аргумента в нашей новой функции i2c_master_driver_initialize.

 

 

Для начала идём в файл at24c.h и объявим там макрос для частоты шины и заодно макрос для бита  проверки отклика

 

 

Перейдём в файл at24c.c в тело функции i2c_master_driver_initialize , объявим переменную типа структуры для конфигурации модуля I2C и проинициализируем её поля

 

 

Вызовем функцию конфигурации шины, результат вернётся в переменную, и вернём результат

 

 

Вот теперь всё соберётся без ошибок.

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

 

 

Объявим глобальную переменную

 

 

Вернёмся в функцию i2c_master_driver_initialize и сохраним в эту переменную адрес микросхемы

 

 

Вызовем функцию, которая окончательно установит драйвер для работы с нашей микросхемой по шине I2C

 

 

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

 

 

Вернёмся в файл at24c.c и ниже функции инициализации i2c_master_driver_initialize (хотя это не обязательно, можно и выше) добавим функцию для записи массива байтов в память микросхемы

 

 

В данной функции мы сначала создадим указатель на переменную типа структуры команды и с помощью специальной функции инициализируем его указателем на дескриптор команды

 

 

Заполним буфер данной структуры последовательностью команд и байтов

 

 

Здесь мы как обычно посылаем условие СТАРТ, затем передаём адрес устройства с нулевым битом записи, что значит мы работаем на запись, далее передаём адрес памяти, с которого начнём запись и затем непосредственно сами записываемые бита. По окончанию передаём команду СТОП.

Теперь применим нашу команду с помощью специальной функции SDK

 

 

Освободим память, назначенную под структуру, и немного подождём

 

 

В main.c в функции app_main отобразим в терминале код возврата из функции инициализации и вызовем нашу функцию, чтобы записать в память микросхемы наш массив, не забыв перед этим добавить на неё прототип в заголовочный файл

 

 

В бесконечном цикле добавим небольшую задержку

 

 

Соберём код, подключим плату, логический анализатор, запустим программу логического анализа, прошьём контроллер и посмотрим результат в программе логического анализа

 

 

 

 

Также мы видим, что частота соответствует заявленной

 

 

В терминале мы также видим успешный результат инициализации шины

 

 

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

 

 

Добавим на данную функцию прототип в заголовочном файле, вызовем её в main.c в функции app_main и отобразим в терминале принятые байты

 

 

Соберём код, прошьём контроллер и посмотрим результат сначала в терминале

 

 

Всё отлично принялось!

А вот и результат в программе логического анализа

 

 

 

Итак, на данном уроке мы научились работать в своих программах с передачей и приёмом данных по интерфейсу I2C контроллера ESP32. На этом, конечно же, работа с данным интерфейсом не заканчивается. Будет ещё много уроков с его использованием.

Всем спасибо за внимание!

 

Данная статья в Дзен.

 

Предыдущий урок Программирование МК ESP32 Следующий урок

Исходный код

 

 

Недорогие отладочные платы ESP32 можно купить здесь: Недорогие отладочные платы ESP32

Логический анализатор 16 каналов можно приобрести здесь

Модуль RTC DS3231 с микросхемой памяти (3 шт)

Модуль RTC DS3231 с микросхемой памяти (1 шт) — так дороже

 

 

Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)

ESP32 I2C. Подключаем внешний EEPROM

 

Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)

ESP32 I2C. Подключаем внешний EEPROM

3 комментария на “ESP32 Урок 6. I2C. Подключаем внешний EEPROM
  1. Ибрагим:

    Добрый день на ваш сайт. Подскажите, уроки по Миландру будут?

  2. Сергей:

    if (ret != ESP_OK) return ret;

    У меня так не компилируется,
    функция все же должна возвращать какое-то значение независимо от условий внутри функции

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

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

*