Шина i2c что это

Опубликовано: 04.05.2024

Схема подключений к шине I2C

Любое устройство на шине I2C может быть одного из двух типов: Master (ведущий) или Slave (ведомый). Обмен данными происходит сеансами. "Мастер"-устройство полностью управляет сеансом: инициирует сеанс обмена данными, управляет передачей, подавая тактовые импульсы на линию Clock, и завершает сеанс.

В режиме ожидания (когда не идёт сеанс обмена данными) обе сигнальные линии (Data и Clock) находятся в состоянии высокого уровня (притянуты к питанию).

Поскольку направление передачи данных указывается при открытии сеанса вместе с адресацией устройства, то для того, чтобы изменить это направление, необходимо открывать ещё один сеанс (снова подавать "Старт"-условие, адресовать это же устройство и указывать новое направление передачи).

Заканчивается каждый сеанс обмена подачей "Мастером" так называемого Stop-условия, которое заключается в изменении уровня на линии Data с низкого на высокий, опять же при наличии высокого уровня на линии Clock. Если на шине сформировано Stop-условие, то закрываются все открытые сеансы обмена.

Внутри сеанса любые изменения на линии Data при наличии высокого уровня на линии Clock запрещены, поскольку в это время происходит считывание данных "Приёмником". Если такие изменения произойдут, то они в любом случае будут восприняты либо как "Старт"-условие (что вызовет прекращение обмена данными), либо как "Стоп"-условие (что будет означать окончание текущего сеанса обмена). Соответственно, во время сеанса обмена установка данных "Передатчиком" (выставление нужного уровня на линии Data) может происходить
только при низком уровне на линии Clock.

Несколько слов по поводу того, в чём в данном случае разница между "прекращением обмена данными" и "окончанием сеанса обмена". В принципе "Мастеру" разрешается, не закрыв первый сеанс обмена, открыть ещё один или несколько сеансов обмена с этим же (например, как было сказано выше, для изменения направления передачи данных) или даже с другими "Слэйвами", подав новое "Старт"-условие без подачи "Стоп"-условия для закрытия предыдущего сеанса. Управлять линией Data, для того, чтобы отвечать "Мастеру", в этом случае будет разрешено тому устройству, к которому "Мастер" обратился последним, однако старый сеанс при этом нельзя считать законченным. И вот почему. Многие устройства (например те же eeprom-ки 24Схх) для ускорения работы складывают данные, полученные от "Мастера" в буфер, а разбираться с этими полученными данными начинают только после получения сигнала об окончании сеанса обмена (то есть "Стоп-условия").

То есть, например, если на шине висит 2 микросхемы eeprom 24Cxx и вы открыли сеанс записи в одну микросхему и передали ей данные для записи, а потом, не закрывая этот первый сеанс, открыли новый сеанс для записи в другую микросхему, то реальная запись и в первую и во вторую микросхему произойдёт только после формирования на шине "Стоп-условия", которое закроет оба сеанса. После получения данных от "Мастера" eeprom-ка складывает их во внутренний буфер и ждёт окончания сеанса, для того, чтобы начать собственно процесс записи из своего внутреннего буфера непосредственно в eeprom. То есть, если вы после после передачи данных для записи в первую микруху не закрыли этот сеанс, открыли второй сеанс и отправили данные для записи во вторую микруху, а потом, не сформировав "Стоп-условие", выключили питание, то реально данные не запишутся ни в первую микросхему, ни во вторую. Или, например, если вы пишете данные попеременно в две микрухи, то в принципе вы можете открыть один сеанс для записи в первую, потом другой сеанс для записи во вторую, потом третий сеанс для записи опять в первую и т.д., но если вы не будете закрывать эти сеансы, то в конце концов это приведёт к переполнению внутренних буферов и в итоге к потере данных.

Каждый бит передаётся за один такт. Та половина такта, во время которой на линии Clock установлен низкий уровень, используется для установки бита данных на шину передающим абонентом (если предыдущий бит передавал другой абонент, то он в это время должен отпустить шину данных). Та половина такта, во время которой на линии Clock установлен высокий уровень, используется принимающим абонентом для считывания установленного значения бита с шины данных.

Вот собственно и всё. На рисунках ниже всё это описание показано в графической форме.

3) Диаграммы и тайминги.

Структура сеанса передачи данных по I2C

Тайминги протокола I2C

Параметр Обозн. Мин.знач. Комментарий
Свободная шина tBUF 4,7 мкс это минимальное время, в течении которого обе линии должны находиться в свободном состоянии перед подачей "Старт"-условия
Фиксация
"Старт"- условия
tHD;STA 4,0 мкс минимальное время от подачи "Старт"- условия до начала первого такта передачи
Готовность
"Стоп"- условия
tSU;STO 4,0 мкс минимальное время, через которое можно подавать "Стоп"- условие после освобождения шины Clock
Длительность LOW полупер. шины Clock tLOW 4,7 мкс минимальная длительность полупериода установки данных (когда на шине Clock низкий уровень)
Длительность HIGH полупер. шины Clock tHIGH 4,0 мкс минимальная длительность полупериода считывания данных (когда на шине Clock высокий уровень)
Удержание данных tHD;DAT 0 то есть данные на шину Data можно выставлять сразу после спада на линии Clock
Готовность данных tSU;DAT 250 нс то есть поднимать уровень на шине Clock можно не ранее 250 нс после установки данных на шине Data

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

Программная реализация мастер-абонента шины I2C в режиме single-master, библиотеки процедур: для PIC, для AVR

Программа для устройства копирования микросхем памяти 24Cxx (здесь можно посмотреть пример использования приведённых выше библиотек для реализации режима I2C-Master на PIC-контроллере)

Программа 2 для контроллера I2C-шлюза, режим Slave из терминалки ПК (а тут посмотреть пример того, как можно сделать I2C-Slave на контроллере AVR)

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

Что такое I2C?

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

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

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

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

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

Сеть I2C

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

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

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

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

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

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

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

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

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

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

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

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

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

Использование 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-разъемом:

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

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

    - 2 шт.
  1. Потенциометр (10КОм) - 2 шт.
  2. Перемычки

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

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


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

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

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

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

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

Основная часть кода как для ведущего, так и для ведомых устройств - это то, что я называю логическим кодом мигания. Чтобы мигнуть светодиодом 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 .

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

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

Wire.beginTransmission()

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

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

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

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-связи. Первая разница заключается в адресе 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 уже обрабатывает ответы от ведомых устройств.

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

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

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

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

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


Один из моих самых любимых интерфейсов. Разработан в компании Philips и право на его использование стоит денег, но все на это дружно положили и пользуют в свое удовольствие, называя только по другому. В Atmel его зовут TWI , но от этого ничего не меняется :) Обычно при разборе IIC во всех книгах ограничиваются примером с EEPROM на этом и ограничиваются. Да еще юзают софтверный Master. Не дождетесь, у меня будет подробный разбор работы этой шины как в режиме Master так и Slave, да еще на аппаратных блоках с полным выполнением всей структуры конечного автомата протокола. Но об этом после, а сейчас основы.

Логический уровень
Как передаются отдельные биты понятно, теперь о том что эти биты значат. В отличии от SPI тут умная адресная структура. Данные шлются пакетами, каждый пакет состоит из девяти бит. 8 данных и 1 бит подтверждения/не подтверждения приема.

После адресного пакета идут пакеты с данными в ту или другую сторону, в зависимости от бита RW в заголовочном пакете.
Вот, например, Запись. В квадратиках идут номера битов. W=0

Организация памяти.
Это относится уже не столько к самому протоколу I 2 C , сколько к заморочкам создателей разных EEPROM и прочих I 2 C устройств. Но встречается это повсеместно, поэтому я расскажу про этот момент. Но, повторюсь, это не аксиома, не стандарт и вообще зависит от конкретного Slave устройства. Так что датит в зубы и вкуривать, но обычно так принято.

Скриншот с осциллографа RIGOL 1042CD

Вроде бы все, практический пример с AVR будет потом, а пока помедитируйте над диаграммой работы конечного автомата TWI передатчика ATmega8. Скоро я вас буду этим грузить!

Страшна? ;) На самом деле там все не так брутально. Можно обойтись вообще парой десятков строк кода на ассемблере.

Спасибо. Вы потрясающие! Всего за месяц мы собрали нужную сумму в 500000 на хоккейную коробку для детского дома Аистенок. Из которых 125000+ было от вас, читателей EasyElectronics. Были даже переводы на 25000+ и просто поток платежей на 251 рубль. Это невероятно круто. Сейчас идет заключение договора и подготовка к строительству!

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

193 thoughts on “Интерфейсная шина IIC (I2C)”

(2) Софтового мастера сделать не сложно. А вот слейва как-то просто не получается.

Щас попробую с опен ид поиграться.

Более того, многие предпочитают делать софтовый И2С мастер чтобы не заморачиваться с встроенным в TWI конечным автоматом.

Я сделал софтового слейва на AT89C2051, но работало жутко медленно.

плюсадин. я уже раз 5 жаловался что логины глючат :D нашел выход, что залогинился, пару минуток подождал и рефреш. работает, но бесит :\

DI HALT спасибо за статью. Давно ждал рассказ про I2C. :)

Отличная статья! А есть последняя диаграмма только в чуть лучшем качестве? А то буквы трудно различить.

I²C (и-квадрат-це, ай-ту-си, ай-сквэрд-си), Inter-Integrated Circuit - последовательная шина обмена данными между интегральными схемами. Изобретена и в начале 80-х компанией Philips Semiconductor (теперь NXP), передача данных осуществляется по двум проводам в обе стороны. Ведущий и ведомый могут выполнять как роль приёмника, так и передатчика. Для возможности соединения более двух устройств используются адресация. Опрашивать адреса шины может только ведущий. Адрес последовательно выводится на линию SDA сразу после сигнала Start. В этой статье речь пойдёт только о 7-ми битной адресации, так же не будет затронута тема нескольких ведущих. На каждом устройстве, поддерживающем I²C обычно обозначены два вывода: SDA и SCL. SDA (serial data) - означает последовательные данные, SCL (serial clock) - последовательное тактирование. Эти выводы являются выводами открытого коллектора или открытого стока, это означает что ведущий и ведомый могут только притягивать электрический потенциал к земле, поэтому на каждой линии должны быть подтягивающие резисторы. Сопротивление резисторов рассчитывается в зависимости от паразитной ёмкости линии.

Подключение:

Шина поддерживает подключение до 112 устройств (при 7-ми битной адресации) по двум проводам (плюс GND и Vcc), может иметь несколько ведущих и ведомых. При использовании нескольких ведущих, каждый из них должен поддерживать этот режим и уметь определять состояние занятой шины.

I²C на Arduino

Arduino UNO R3/Piranha UNO

На Arduino UNO R3/Piranha UNO шина I2C находится на выводах A4, A5. Также в эти выводы продублированы на колодке с цифровыми выводами рядом с кнопкой Reset.


Piranha ULTRA

На Piranha ULTRA шина I²C не занимает аналоговые выводы A4, A5 и находится на цифровой колодке рядом с кнопкой Reset, выводы обозначены SDA и SCL


Arduino MEGA R3

На Arduino MEGA R3 шина I²C находится на цифровой колодке на крайних выводах, близких к разъёму USB и на выводах 20, 21. Выводы объединены.


Примеры для Arduino

Работа с шиной с использованием встроенной библиотеки Wire Arduino IDE на примере Trema-модуля LED Матрицы 8x8 - i2c

В этом примере на матрицу выводится изображение стрелки. Стоит заметить, ко всем нашим модулям написаны библиотеки с высокоуровневым интерфейсом и вовсе не обязательно работать с матрицей на низком уровне. Подробнее о работе с библиотекой матрицы можно узнать по этой ссылке.

I²C на Raspberry Pi

На Raspberry Pi I²C выводы это 3-й и 5-й выводы колодки, GPIO2 и GPIO3 по номенклатуре BCM и выводы 8, 9 по номенклатуре WiringPi.


Примеры для Raspberry

Работа с шиной с использованием модуля smbus для Python на примере Trema-модуля Матрицы 8x8 - i2c. Для работы с шиной её необходимо включить в настройках Raspberry при помощи утилиты raspi-config . Ссылка на подробное описание как это сделать.

Так же как и в примере с Arduino, в этом примере на матрицу выводится изображение стрелки. Стоит заметить, к Trema-модулю LED Матрица 8x8 - i2c написана библиотека с высокоуровневым интерфейсом и вовсе не обязательно работать с матрицей на низком уровне. Подробнее о работе с библиотекой матрицы можно узнать по этой ссылке

Подробнее о шине I²C:

Резисторы, ёмкость и длина линий шины

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

При использовании шины на модулях не существует принятого стандарта установки подтягивающих резисторов на ведущем или ведомом. У Arduino подтягивающие резисторы отсутствуют и для работы с шиной нужен хотя бы один модуль с ними. У Raspberry Pi на плате установлены подтягивающие резисторы номиналом 1,7 килоОм и для неё нет необходимости в подтяжке на модулях.

Сигналы и специальные биты шины

В состоянии покоя линии шины находятся на верхнем потенциале (обычно 3,3 В или 5 В, но могут быть и другие напряжения). Бездействие устройства, по умолчанию, воспринимается как логическая 1. Для простоты понимания можно рассмотреть аналогию: Вообразим верхний потенциал как уровень воды, а нижний как дно. Представьте, что Вы на рыбалке - попловок в состоянии покоя остаётся на поверхности, когда клюёт - идёт ко дну. Так же и в здесь, при обмене данными линии прижимаются в нулевому потециалу. Далее рассмотрим поочереди сингалы и специальные биты.

Устанавливаемые только ведущим

  • Start - сигнал начала обмена данными. Линия тактирования SCL отпущена (логическая 1), ведущий пижимает линию данных SDA (переход из логической 1 в логический 0). После этого обмен данными происходит побайтово. Первый байт - семь бит адреса ведомого и бит направления (запись или чтение). Последующие байты - данные. после этого сигнала шина считается занятой.

Краткое обозначание сигнала S - заглавная буква S латинского алфавита.

  • Бит Read - Если ведущий желает получить данные, он устанавливает логическую 1 сразу после адреса, информируя ведомого о том, что данные будут считываться (управление линией данных передаётся ведомому).

Краткое обозначание R - заглавная буква R латинского алфавита.

  • Бит Write - Если ведущий желает записать данные, он устанавливает логический 0 сразу после адреса для информирования ведомого о том что данные будут записываться (управление линией данных остаётся у ведущего).

Краткое обозначение W̅ - заглавная буква W латинского алфавита с чертой сверху.

  • Stop - сигнал окончания обмена данными. Ведущий прекращат тактирование, линия тактирования SCL отпущена (логическая 1), линия данных SDA переведена ведущим из логического 0 в логическую 1. После этого сигнала шина считается свободной.

Краткое обозначение P - заглавная буква P латинского алфавита.

  • Restart - сигнал продолжения обмена данными (используется взамен сигналу Stop с последующим Start для продолжения опрашивания шины ). Используется в основном при наличии нескольких ведущих на шине, чтобы управление не перешло другому ведущему после сигнала Stop. Линия тактирования SCL отпущена ведущим, линия данных SDA переведена ведущим из логической 1 в логический 0.

Обозначается Sr - заглавная буква S и строчная буква r латинского алфавита.

Устанавливаемые ведущим и ведомым

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

  • Бит ACK - (сокращ. англ. acknowledged - подтверждено) каждый девятый импульс тактирования передатчик (ведущий или ведомый) отпускает линию данных. Если линия была прижата приёмником (логический 0) - принятые данные верны, передача может быть продолжена или закончена.

Обозначается A - заглавная буква A латинского алфавита

  • Бит NACK - (сокращ. англ. not acknowledged - не подтверждено) каждый девятый импульс тактирования передатчик (ведущий или ведомый) отпускает линию данных. Если линия была отпущена принимающим или принимающего нет на шине (логическая 1) - принятые данные неверны, произошла ошибка, передача не может быть продолжена. Обозначается A̅ - заглавная буква A латинского алфавита с чертой сверху.

Обмен данными

При обмене данными тактированием занимается только ведущий, а ведомый может удерживать линию тактирования только если не успевает за ведущим, так называемое растягивание тактирования (clock-stretching). Не все модули поддерживают удержание. Установка бита на линии данных может происходит в момент, когда линия тактирования прижата, а считывания, когда линия отпущена (подтянута к Vcc), но в большинстве случаев это происходит по фронту волны на линии тактирования.

Рассмотрим пример простого обмена данными:

Запись в регистры ведомого. Данные взяты из примеров, приведённых выше.

После сигнала Start и указания адреса ведущий записывает адрес регистра с которого будет производиться дальнейшая запись. Стоит заметить, что у ведомого есть внутренний счётчик и каждый последующий байт после подтверждения будет записан в следующий регистр. Таким образом байт со значением 0x00 будет записан в регистр 0x11, байт со значением 0x18 будет записан в регистр 0x12, байт со значением 0x3C будет записан в регистр 0x13 и т. д. В этом примере биты ACK устанавливает ведомый.

Вот так сигналы этого примера выглядят на осциллографе:

  • Канал 1 - тактирование (SCL)
  • Канал 3 - данные (SDA)
  • Линия B1 - декодирование данных осциллографом

Чтение из регистров ведомого. Предположим, мы хотим прочитать байт из регистра 0x13.

После сигнала Start и указания адреса ведущий записывает адрес регистра, который необходимо прочитать. Далее следует сигнал Restart (или Stop, затем Start). Ведущий снова выводит адрес ведомого, но уже с битом Read (чтения). Ведомый устанавливает бит ACK и во время следующих 8-ти импульсов тактирования выводит данные на линию SDA. На девятом импульсе уже ведущий устанавливает бит ACK (или NACK, если данные не удалось прочитать) и завершает обмен сигналом Stop.

Скорость

Первоначальный стандарт I²C был реализован на скорости 100 кГц. С тех пор появились и другие реализации шины, но большинство устройств работают на этой скорости. Так же известны случаи когда скорость шины специально снижена, чтобы увеличить расстояние передачи и уменьшить чувствительность к помехам. Не все модули могут работать на сниженной скорости.

Неудивительно, что общей особенностью электронных систем является необходимость обмена информацией между двумя или тремя или десятью отдельными компонентами. Инженеры разработали ряд стандартных протоколов, которые помогают различным микросхемам успешно общаться, что становится очевидным, когда вы сталкиваетесь с потоком сокращений в разделе «Связь» в списке характеристик микроконтроллера или сигнального процессора: UART, USART, SPI, I2C, CAN. Каждый протокол имеет свои плюсы и минусы, и важно немного знать о каждом из них, чтобы вы могли принимать обоснованные решения при выборе компонентов или интерфейсов.

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

Много названий, а шина одна

Нет сомнений в том, что протокол I2C страдает серьезной терминологической проблемой. Фактическое название – шина Inter–Integrated Circuit. Самая простая и, вероятно, наименее распространенная аббревиатура – IIC. Возможно, эту аббревиатуру недолюбливали из-за того, что две заглавные буквы I выглядят как две единицы, или как две строчных буквы l , или как римская цифра II , или как символ параллельных прямых. В любом случае аббревиатура I 2 C (произносится как «I в квадрате C») приобрела популярность, несмотря на сомнительную логику обращения с обычной буквой, как если бы она была переменной, подверженной возведению в степень. Третий вариант – I2C («I два C»), который позволяет избежать неудобств форматирования верхнего индекса, а также несколько легче в произношении, чем «I в квадрате C»

Дополнительная путаница вносится, когда вы замечаете, что SMB или SMBus явно используется в качестве еще одного способа обращения к шине I2C. Фактически эти сокращения относятся к шине управления системой SMB (System Management Bus), которая отличается, хотя и почти идентична, от шины I2C. Оригинальный протокол I2C был разработан компанией Phillips Semiconductor, а спустя годы Intel определил протокол SMBus как расширение I2C. Эти две шины в значительной степени взаимозаменяемы; если вас интересуют незначительные отличия между ними, то смотрите страницу 57 спецификации шины управления системой SMB.

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

Чтобы оценить ловкие технические приемы, которые делают I2C настолько эффективной, вам нужно подумать о трудностях достижения надежной, но универсальной связи между несколькими независимыми компонентами. Ситуация достаточно проста, если у вас есть одна микросхема, которая всегда является ведущей (master), и одна микросхема, которая всегда является ведомой (slave). Но что, если у вас есть несколько ведомых? Что если ведомые не знают, кто ведущий? Что, если у вас есть несколько ведущих? Что произойдет, если ведущий запросит данные у ведомого устройства, которое по какой-то причине перестало функционировать? Или что, если ведомый перестал функционировать в середине передачи? Что делать, если ведущий утверждает, что шина осуществляет передачу, а затем он выйдет из строя, прежде чем освободить шину?

Возможные проблемы на шине
Возможные проблемы на шине

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

Обзор

Прежде чем вдаваться в детали, давайте рассмотрим ключевые характеристики I2C:

  • Используется только два сигнала (тактовая синхронизация и данные) независимо от токо, сколько устройство подключено к шине.
  • Оба сигнала подтягиваются к положительному напряжению питания через резисторы, соответствующих номиналов.
  • Каждое устройство взаимодействует с сигналами данных и тактовой синхронизации через драйверы вывода с открытым стоком (или с открытым коллектором).
  • Каждое ведомое устройство идентифицируется с помощью 7-битного адреса; устройство мастер должно знать эти адреса, чтобы общаться с конкретным ведомым устройством
  • Все передачи инициируются и прекращаются мастером; масте может передавать данные одному или нескольким ведомым устройствам или запрашивать данные из ведомого устройства.
  • Метки «ведущий/master» и «ведомый/slave» по своей сути непостоянны: любое устройство может функционировать и как ведущее, и как ведомое устройство, если оно содержит необходимое аппаратное и/или программное обеспечение. На практике, однако, встраиваемые системы часто используют архитектуру, в которой одни мастер отправляет команды или собирает данные с нескольких ведомых устройств.
  • Сигнал данных обновляется по заднему фронту тактового сигнала, а его выборка происходит по переднему фронту следующим образом: Временная диаграмма сигналов шины I2C
    Временная диаграмма сигналов шины I2C
  • Данные передаются в однобайтовых секциях, причем каждый байт сопровождается однобитным сигналом подтверждения, называемым битом ACK/NACK (подтверждение или неподтверждение).

I2C против UART и SPI

Преимущества I2C можно резюмировать следующим образом:

  • требует малое количество выводов/сигналов даже с большим количеством устройств на шине;
  • адаптируется к потребностям разных ведомых устройств;
  • легко поддерживает несколько ведущих устройств;
  • включает в себя функционал ACK/NACK для улучшения обработки ошибок.

А вот некоторые недостатки:

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

    С этих точек зрения видно, что I2C особенно подходит, когда у вас сложная, разнообразная или обширная сеть связанных устройств. Интерфейсы UART обычно используются для соединений «точка-точка», потому что не имеют стандартного способа адресации различных устройств и совместного использования линий связи. SPI отлично работает, когда у вас есть одно ведущее и несколько ведомых устройств, но для каждого ведомого устройства требуется отдельный сигнал выбора ведомого, что приводит к большому количеству линий связи и к трудностям разводки печатной платы, когда на шине находится много устройств. И SPI неудобен, когда вам нужно поддерживать несколько ведущих устройств.

    Возможно, вам придется сознательно избегать I2C, если пропускная способность является приоритетом; SPI поддерживает более высокие частоты тактового сигнала и минимизирует накладные расходы. Кроме того, разработка низкоуровнего аппаратного обеспечения для SPI (или UART) намного проще, поэтому, если вы работаете с FPGA и разрабатываете свой последовательный интерфейс с нуля, I2C, вероятно, стоит выбирать последним.

    Заключение

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

    Читайте также: