Микроконтроллер для шины i2c

Реализация интерфейса I2C на базе чипа FT2232H (режим MPSSE)

Интерфейсная микросхема поддерживает работу с интерфейсом I2C (не путать с I2S!) в режиме MPSSE (Multi-Protocol Synchronous Serial Engine). Кроме I2C данный режим поддерживает целый перечень стандартных последовательных интерфейсов, таких как SPI, JTAG и т. п. Есть возможность реализации собственных интерфейсов в случае необходимости. Данное описание рассказывает о ряде нюансов поддержки интерфейса I2C, а так же дает повод поболтать на смежную тематику.

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

Поддерживается независимая работа двух каналов I2C (один канал для FT232H). Пример схемы включения приведен в AN113 — в качестве примера используется коммуникация между FT2232H и микросхемой энергонезависимой памяти 24LC256. Непосредственно к интерфейсу I2C относятся только два сигнала: SCL (тактовый сигнал, на рисунке обозван SCC) и SDA (передаваемые данные). Согласно спецификации, каждый из этих сигналов может быть двунаправленным, однако часто на шине присутствует только одно ведущее устройство (мастер) и именно оно и задает тактовый сигнал. Данные в любом случае двунапраленные — на каждые 8 бит передаваемых данных следует 1 бит подтверждения.

На программном уровне работа с шиной I2C возможна двумя способами:

  • Непосредственно через драйвер D2XX. В этом случае необходимо производить ручное программирование режима MPSSE в соответствии с набором команд, описанных в AN108. Вся работа идет через функции FT_Read() и FT_Write(), а так же сопутствующие функции открытия/закрытия устройства и т.п.
  • С использованием библиотеки LibMPSSE-I2C. Данная библиотека решает все вопросы программирования режима MPSSE и позволяет производить операции чтения и записи указанного числа байт по шине I2C с использованием вызовов I2C_DeviceWrite() и I2C_DeviceRead(). Описание библиотеки приведено в AN177.

Исследования проводились с использованием одной из имеющихся плат. В качестве ведомого устройства на шине I2C использовался измеритель мощности INA219. Поскольку плата была взята та, что оказалась под рукой, между микросхемами FT2232H и INA219 оказалась FPGA (как мне повезло я понял несколько позже). Изначально я планировал ее использовать только для преобразование 3-х проводного интерфейса FT2232H в двухпроводной интерфейс шины I2C — в нормальном устройстве подобное преобразование происходит за счет схемы, см. рисунок выше.

Как уже писалось, при передаче данных на каждые 8 бит передаваемой информации следует подтверждение (ACK) – перевод сигнала в состояние 0 со стороны принимающего устройства. Если передающее устройство видит это подтверждение — значит его услышали и можно продолжать дальше. Если подтверждения нет — значит продолжать дальше бесполезно и обмен можно прекращать.
В процессе исследований было установлено, что библиотека LibMPSSE-I2C имеет ошибку, блокирующую возможность чтения более чем одного байта по шине в большинстве случаев. Ошибка проявляется возникновением глитча на тактовом сигнале перед выставлением подтверждения ACK о первом прочитанном байте со стороны FT2232H на шину. Длина глитча составляет 1 период частоты 12 МГц (порядка 8 нс). Данный глитч воспринимается слэйв-устройством на шине как тактовый сигнал, появляющийся в тот момент, когда признак ACK еще не установлен. После этого слэйв-устройство прерывает транзакцию. Пример подобной транзакции, в рамках которой происходит попытка чтения двух байт из устройства, показан на рисунках (захват данных производился на частоте 120 МГц, глитч присутствует на временных отсчетах 20968-20967). Временные диаграммы получены с использованием встраиваемого логического анализатора SignalTap для альтеровских FPGA (здесь я первый раз порадовался затесавшейся FPGA). На картинках видно, что по линии DI читается первый байт из устройства, далее устройство отваливается (получив некорректное подтверждение от мастера) и вместо второго байта читается 0xFF.

Читайте также: Шина гибкая изолированная иконка

В случае фильтрации данного импульса обеспечивалась правильная процедура чтения по шине. В тестовом окружении фильтрация производилась на FPGA цифровым способом (был написан небольшой модуль на VHDL, отбрасывающий глитчи на входных сигналах). Другой возможный способ — фильтрация с использованием RC-цепочки на шине SCL, однако это ограничит максимальную частоту шины (в виду некоторой маразматичности, глубоко данное решение не анализировалось). Пример успешной транзакции (при условии фильтрации глитча) приведен на следующем рисунке. Теперь по шине DI мы видим корректное чтение двух байт.

Естественно, что хочется добраться до истинных причин такого поведения. К сожалению, исходные коды библиотеки LibMPSSE-I2C недоступны, дизассемблирование бинарников — не наша специализация. Но проанализировать обмен данными по USB при наличии открытого описания протокола — дело 5 минут. Ну часа. Ну уж никак не больше дня.

Детальное исследование показало проблему в формировании команд для контроллера MPSSE, формируемого библиотекой LibMPSSE-I2C. Далее приводится набор команд используемых для чтения 2х байт по шине (стоп-бит в конце не выставляется). Выделенные команды показывают место проблемы и способ решения (запрет перехода в 1 либо изменение фазы подаваемого тактового сигнала для признака ACK):

Видео:Логический анализатор шины i2cСкачать

Логический анализатор шины i2c

Как настроить I2C-связь на Arduino

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

Видео:Шина I2C.Скачать

Шина I2C.

Что такое I2C?

c — это аббревиатура от Inter-Integrated Circuit (меж-интеграционная цепь или последовательная асимметричная шина).

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

Микроконтроллер для шины i2c

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

Вся связь проходит по двум проводам к ведущему и ведомым устройствам и от них. Это очень полезно при выполнении проектов Arduino, так как Arduino имеет ограниченное количество входных/выходных контактов.

Многие датчики и модули, предназначенные для Arduino используют I2C для связи.

Видео:Лекция 308. Шина I2CСкачать

Лекция 308.  Шина I2C

Сеть I2C

Сеть I2C состоит из ведущего и ведомого устройств, соединенных шиной. В сети I2C может быть несколько ведущих и ведомых устройств — мастеров и наследников.

Ведомое устройство (наследник)

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

Ведущее устройство (мастер)

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

Шина в I2C — это просто два провода, которые соединяют все I2C-устройства в сети.

Эти два провода называются SDA и SCL. Провод SDA используется для связи между ведущим и ведомым устройствами.

Линия SCL несет тактовый сигнал, используемый для правильной синхронизации связи. Для поддержания обоих проводов в состоянии HIGH необходимы импульсные или подтягивающие (pull-up) резисторы.

Логические уровни

Будьте внимательны при подключении I2C устройств к Arduino.

Arduino выводит I2C-сигналы на логическом уровне, но I2C-устройства работают с различными напряжениями логического уровня.

Читайте также: Шины в челнах рынок кама

Таким образом, I2C устройство, которое работает на 3,3 В может быть повреждено при подключении к Arduino. В паспорте устройства должно быть указано напряжение логического уровня.

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

Видео:Что такое I2C ??? Подключаем GY-521 и Oled 96*16 к STM 32Скачать

Что такое I2C ??? Подключаем GY-521 и Oled 96*16 к STM 32

Использование I2C

Чтобы продемонстрировать, как использовать I2C в Arduino, давайте создадим проект, который посылает данные туда и обратно между двумя ардуинами.

Мы будем использовать I2C связи для изменения скорости мигания светодиода контакта 13 на одном Arduino, в зависимости от положения потенциометра, подключенного к другому Arduino.

Один Arduino будет выступать в качестве мастера, а другой Arduino будет выступать в качестве ведомого.

Пины I2C Arduino

Arduino имеет специальные контакты для I2C, которые имеют встроенные подтягивающие резисторы в соответствии с требованиями протокола I2C.

Для плат Arduino Uno это контакты A4 и A5. Пин A4 — это контакт SDA, а пин A5 — это контакт SCL. В версии Arduino Uno R3 есть еще один набор контактов I2C рядом с USB-разъемом:

Микроконтроллер для шины i2c

Компоненты оборудования

Чтобы создать этот проект, вам понадобятся следующие компоненты:

Схема соединения

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

Микроконтроллер для шины i2c

Вы могли заметить, что у нас нет подтягивающих резисторов на линиях SDA и SCL. Подтягивающие резисторы уже встроены в I2C контакты Arduino, так что они нам не нужны.

Видео:Урок 24. Узнаём адреса устройств на шине I2CСкачать

Урок 24. Узнаём адреса устройств на шине I2C

Скетч для мастера

У нас есть два Ардуино в нашей сети I2C, так что у нас есть два набора скетчей. Один для мастера Ардуино, а другой для наследника Ардуино. Между двумя эскизами нет большой разницы, как вы увидите позже.

Теперь откройте Arduino IDE и загрузите код ниже на мастер Arduino:

Видео:Шина данных I2C и многозадачность микроконтроллераСкачать

Шина данных I2C и многозадачность микроконтроллера

Объяснение скетча для мастера

Основная часть кода как для ведущего, так и для ведомых устройств — это то, что я называю логическим кодом мигания. Чтобы мигнуть светодиодом 13 на Ардуино, мы должны сделать следующее:

  • Добавим глобальные переменные byte i2c_rcv , int time_start , stat_LED и byte value_pot в верхней части нашего скетча
  • Инициализируйте значения глобальных переменных внутри функции setup()
  • Инициализируйте контакт 13 Arduino как выходной контакт внутри setup() с помощью pinMode()
  • Добавим код логики мигания внутри функции loop()

Библиотека Wire

Для использования встроенного интерфейса I2C Arduino мы будем использовать библиотеку Wire.

Эта библиотека поставляется в стандартной комплектации с Arduino IDE. Как и в других библиотеках Arduino, библиотека Wire имеет готовые I2C функции, чтобы сделать кодирование проще для нас.

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

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

Первое, что нужно сделать, это подключить устройство к шине I2C. Синтаксис для этого — Wire.begin(address) . Адрес является необязательным для мастер-устройств. Итак, для эскиза мастера Arduino, мы просто добавляем код Wire.begin(); внутри setup() .

Теперь мы переходим к циклу loop() . Наш код заставит Arduino прочитать значение потенциометра, подключенного к контакту A0, и сохранить его в переменной value_pot .

Отправка данных

После сохранения значения с пина A0 в переменную value_pot , мы можем отправить значение по I2C. Отправка данных по I2C включает в себя три функции:

  • Wire.beginTransmission()
  • Wire.write()
  • Wire.endTransmission()

Wire.beginTransmission()

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

Для этого мы вызываем функцию Wire.beginTransmission(address) . Адрес — это I2C-адрес ведомого прибора, который будет принимать данные. Эта функция делает две вещи:

  1. Она информирует шину о том, что мы будем посылать данные.
  2. Он информирует предполагаемого получателя о том, что данные готовы к получению.

Читайте также: Норма давления в шинах легкового автомобиля летом r13

Wire.write()

А затем мы отправим значение переменной value_to_send с помощью функции Wire.write(value) .

Wire.endTransmission()

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

Наше ведущее устройство также должно получить положение потенциометра от ведомого устройства. Мы делаем это с помощью Wire.requestFrom() , Wire.available() и Wire.read() .

Wire.requestFrom()

Полным синтаксисом запроса данных от ведомого устройства является Wire.requestFrom(адрес, количество).

Адрес — это I2C-адрес ведомого устройства, от которого мы должны получить данные, а количество — это количество байтов, которое нам нужно. Для нашего проекта, адрес ведомого устройства 0x08 и нам нужен один байт.

Внутри loop() мы используем Wire.requestFrom(0x08, 1); для запроса одного байта данных от ведомого устройства 0x08.

После выдачи команды Wire.requestFrom(0x08, 1) , за ней должна следовать команда чтения для получения ответа от шины I2C.

Write.available()

Сначала мы проверяем, есть ли данные на шине. Это делается с помощью функции Write.available() внутри условного оператора if() . Функция Write.available() возвращает количество байт, ожидающих чтения.

Wire.read();

Для получения доступных данных мы используем функцию Wire.read() и сохраняем возвращаемое значение в переменную i2c_rcv . Каждый вызов функции Wire.read() получает только один байт данных из шины I2C.

Видео:Введение в шину I2CСкачать

Введение в шину I2C

Скетч для наследника (ведомого)

Теперь загрузите этот код ведомому Ардуино:

Видео:Программирование МК AVR. Урок 16. Интерфейс TWI (I2C). Часть 6Скачать

Программирование МК AVR. Урок 16. Интерфейс TWI (I2C). Часть 6

Объяснение скетча для ведомого

Для ведомого устройства существует небольшая разница в кодировании I2C-связи. Первая разница заключается в адресе Wire.begin(address) .

Для ведомых устройств адрес является обязательным. Для нашего проекта адрес для ведомого устройства будет 0x08. Это может быть любой адрес, но убедитесь, что он уникален в сети I2C.

Некоторые I2C ведомые устройства также имеют определенные I2C-адреса, поэтому сначала проверьте спецификацию.

Мы присоединим I2C сеть в качестве ведомого устройства, добавив код Wire.begin(0x08); внутри setup() .

Обработчики событий

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

Обработчики событий — это части кода, которые управляют событиями, с которыми наше устройство, скорее всего, столкнется во время работы.

Wire.onReceive()

В части скетча setup() мы добавляем функцию Wire.onReceive(handler) для регистрации функции (обработчика), которая будет управлять полученными данными.

Мы вызываем нашу функцию-обработчик dataRcv() . Обратите внимание, что имя функции может быть любым. В приведенном выше эскизе Wire.onReceive(dataRcv); вызывается в секции setup() .

В конце эскиза находится код функции-обработчика. Он инициализируется как void dataRcv(int numBytes) . Параметр int numBytes содержит количество байт полученных данных.

Wire.onRequest()

Следующий обработчик события, который мы будем использовать — Wire.onRequest(handler) . Эта функция используется на подчиненных устройствах и работает аналогично Wire.onReceive() .

Единственное отличие заключается в том, что она обрабатывает события запроса данных. Запросы данных поступают от основных устройств.

В функцию setup() мы добавляем код Wire.onRequest(dataRqst); . А в конце нашего эскиза добавляем функцию void dataRqst() . Обратите внимание, что обработчики Wire.onRequest() не принимают никаких параметров. Функция dataRqst() содержит только Wire.write() .

Нам не нужны Wire.beginTransmission() и Wire.endTransmission() , потому что библиотека Wire уже обрабатывает ответы от ведомых устройств.

Видео:Установщик адресов Flash-i2cСкачать

Установщик адресов Flash-i2c

Тестирование Arduino I2C

А вот и самая захватывающая часть — включение питания и тестирование!

Используя Arduino IDE, загрузите эскиз мастера Arduino в одну из Ардуино. Затем загрузите скетч наследника в другую Arduino.

  • Отрегулируйте потенциометр на ведущем устройстве, чтобы регулировать частоту мигания светодиода ведомого устройства.
  • Отрегулируйте потенциометр на ведомом устройстве, чтобы контролировать частоту мигания светодиода ведущего устройства.

Наш код принимает положение потенциометра мастера и посылает его ведомому устройству через I2C. Затем ведомое устройство использует полученное значение для настройки времени задержки мигания светодиода. То же самое происходит и с положением потенциометра ведомого.

  • Свежие записи
    • Нужно ли менять пружины при замене амортизаторов
    • Скрипят амортизаторы на машине что делать
    • Из чего состоит стойка амортизатора передняя
    • Чем стянуть пружину амортизатора без стяжек
    • Для чего нужны амортизаторы в автомобиле
    • Правообладателям
    • Политика конфиденциальности

    🔥 Видео

    AVR 47# Программный I2CСкачать

    AVR 47# Программный I2C

    Программирование МК AVR. Урок 16. Интерфейс TWI (I2C). Часть 4Скачать

    Программирование МК AVR. Урок 16. Интерфейс TWI (I2C). Часть 4

    Программирование МК AVR. Урок 16. Интерфейс TWI (I2C). Часть 3Скачать

    Программирование МК AVR. Урок 16. Интерфейс TWI (I2C). Часть 3

    Шина данных i2c - декодируем/синхронизируем с помощью осциллографа Lecroy!Скачать

    Шина данных i2c - декодируем/синхронизируем   с помощью осциллографа Lecroy!

    Урок №7. Подключаем шину I2C в микроконтроллере Atmega8.Скачать

    Урок №7. Подключаем  шину I2C в микроконтроллере Atmega8.

    I2C интерфейсСкачать

    I2C интерфейс

    Arduino I2C связь между контроллерамиСкачать

    Arduino I2C связь между контроллерами

    Программирование МК AVR. Урок 16. Интерфейс TWI (I2C). Часть 2Скачать

    Программирование МК AVR. Урок 16. Интерфейс TWI (I2C). Часть 2

    MCP2515, контроллер CAN шины с интерфейсом SPIСкачать

    MCP2515, контроллер CAN шины с интерфейсом SPI

    Декодер протоколов | Часть первая I2C снифферСкачать

    Декодер протоколов | Часть первая I2C сниффер

    Программирование МК STM32. УРОК 8. HAL. Шина I2C. Подключаем микросхему RTC DS3231Скачать

    Программирование МК STM32. УРОК 8. HAL. Шина I2C. Подключаем микросхему RTC DS3231
Поделиться или сохранить к себе:
Технарь знаток