I2C интерфейс

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

Описание интерфейса I2C

Способ подключения

В интерфейсе используются два провода, это линия тактирования SCL, и линия передачи данных SDA, которые вместе образуют шину данных. Устройства, подключенные к шине подразделяются на ведущего и ведомого. Ведущий инициализирует процесс передачи данных и выдает тактовые импульсы на линию SCL, ведомый принимает команды /данные, а также выдает данные по запросу ведущего. Линии SDA и SCL двунаправленные, устройства подключаемые к шине должны иметь выводы перенастраиваемые на вход и выход . Причем тип выхода должен быть с открытым коллектором или открытым стоком, в связи с чем, обе линии SDA и SCL через резисторы подтягиваются к положительному полюсу источника питания. На следующей картинке приведена схема подключения интерфейса I2C:
Подключение интерфейса I2C
В случае использования микроконтроллера, для установки лог. 1 на линии, достаточно перенастроить порт микроконтроллера на вход, при этом резистор “подтянет” линию к высокому логическому уровню, подача высокого логического уровня с порта микроконтроллера на линию не допускается. Для установки лог. 0 на линии, порт перенастраивается на выход, в выходную защелку заранее записывается значение 0, при этом линия “прижимается” к низкому логическому уровню.

Адресация

В интерфейсе предусмотрена программная адресация устройств подключенных к шине, наиболее распространена длина адреса в 7 бит, теоретически это позволяет подключать на шину до 127 устройств, но часть адресов по спецификации зарезервированы и не могут использоваться разработчиками. Каждое устройство имеет свой уникальный адрес, который заложен производителем и указан в технической документации. Адрес устройства может быть фиксированным, или с возможностью аппаратной настройки, в этом случае устройство имеет дополнительные входы, в зависимости от уровня напряжения на входах (высокое или низкое), можно получить различные адреса. Обычно количество входов варьируется от 1-го до 3-х, которые задают значения определенных битов 7-битного адреса. Аппаратная настройка адреса предусмотрена для возможности подключения нескольких однотипных устройств на одну шину.

Также интерфейс предусматривает более редкую 10-битную адресацию, под которую зарезервирован 7-битный адрес 11110XX (XX-зависят от значения адреса), в этом случае сначала предается зарезервированный адрес, в котором два последних бита представляют собой старшие биты 10-битного адреса, затем передаются младшие 8 бит адреса. При использовании данной адресации на шину можно подключать более 1000 устройств.

Условия “Старт” и “Стоп”

Каждый сеанс передачи данных начинается со специального условия, называемого  “Старт”. В исходном состоянии, когда шина свободна, обе линии SDA и SCL подтянуты к высокому логическому уровню, условие “Старт” подразумевает переключение линии SDA с высокого логического уровня на низкий, в то время когда на линии SCL установлен высокий уровень.

Аналогично, сеанс передачи данных завершается специальным условием “Стоп”, это переключение линии SDA с низкого логического уровня на высокий, при высоком уровне на линии SCL. Данные условия генерирует ведущий (микроконтроллер).

Исходя из условий “Старт” и “Стоп”, во время передачи данных линия SDA может переключаться только при низком уровне на линии SCL, то есть установка новых данных на линии SDA возможна только после спада уровня на SCL.  В течение импульса тактирования (высокий уровень на SCL), состояние линии SDA не должно меняться, в это время выполняется считывание данных на SDA.

Формат передачи данных

Данные по интерфейсу передаются побайтно, старшим битом вперед, за каждым переданным байтом (8 бит) следует бит подтверждения, устройство (ведущий или ведомый) принявшее байт данных, устанавливает низкий уровень на линии SDA на следующем тактовом импульсе SCL, тем самым подтверждая получение байта. В это время передающее устройство должно опрашивать линию SDA, ожидая ответ об успешном получении байта. Ниже на картинке представлена диаграмма передачи данных по шине I2C:
диаграмма передачи I2C
Сначала передается байт с 7-битным адресом ведомого, значение 8-го бита (R/W) определяет направление передачи данных, нулевое значение соответствует записи данных, то есть передача от ведущего к ведомому. Если бит направления равен 1, то выполняется чтение данных из ведомого.

Ведомый сравнивает переданный адрес со своим и при совпадении откликается, устанавливая низкий уровень на линии SDA (бит подтверждения). Ведущий, получив подтверждение, начинает передавать байты с данными, или принимает их, в зависимости от направления передачи. На следующей картинке более подробно представлены различные варианты передачи данных по шине I2C:
I2C описание передачи данных
После передачи адреса ведомого, передается адрес регистра, над которым будут производиться операции чтения/записи. Каждое устройство обладает своим набором внутренних регистров, назначение которых указано в технической документации.

Запись одного байта состоит из следующей последовательности: условие “Старт” – адрес ведомого (бит R/W сброшен) – адрес внутреннего регистра ведомого – данные (1 байт) – условие “Стоп”. Запись нескольких байтов практически ничем не отличается, после отправки первого байта данных, передаются остальные байты, сеанс заканчивается условием “Стоп”. При этом данные записываются в регистры последовательно, начиная с заданного адреса, обычно ведомый выполняет автоматический инкремент адреса внутренних регистров.

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

Во время сеанса передачи данных ведомый может принудительно удерживать на линии SCL низкий уровень, например, если ему требуется время на обработку данных. Ведущий “отпуская” линию SCL должен проверить переход от низкого логического уровня к высокому, если этого не произошло, то необходимо ожидать перехода. Таким образом, ведущий не имеет абсолютного права на управление линией SCL.

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

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

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

  1. Самый распространенный до 100 Кбит/сек, частота тактирования линии SCL до 100 кГц, длительность высокого и низкого уровней не менее 5 мкс.
  2. Скоростной режим до 400 Кбит/сек, частота тактирования до 400 кГц.
  3. Высокоскоростной режим до 3,4 Мбит/сек, частота тактирования до 3,4 МГц.

Стандартный режим до 100 Кбит/сек поддерживают все устройства, возможность функционирования на больших скоростях необходимо уточнять в технической документации на устройство.

Программная реализация интерфейса I2C

Не у всех микроконтроллеров серии PIC16 имеется встроенный аппаратный модуль I2C, но интерфейс можно реализовать программно и использовать на любом микроконтроллере. Ниже представлен код реализующий функции ведущего применительно к микроконтроллеру PIC16F628A:

Для примера в коде под линии sda и scl выбраны линии микроконтроллера RB0 и RB1, также битам направления линий присваиваются названия sda_io и scl_io. В начале основной программы линии настраиваются на вход. Для записи данных используется подпрограмма write_i2c, для чтения read_i2c. Оперирование с байтами данных выполняется через косвенную адресацию, data_i2c это начальный регистр хранения данных.

Перед операциями записи/чтения необходимо записать адрес ведомого в регистр slave_adr, в коде для примера приведен 7-битный адрес 0111111, значение 8-го бита может быть любым.

Для операции записи необходимо задать адрес внутреннего регистра ведомого, в который или начиная с которого необходимо записать данные, то же относится и к операции чтения. Адрес внутреннего регистра ведомого записывается в регистр adr_i2c. Далее нужно задать кол-во передаваемых байтов в регистре tmp_i2c. Перед записью с помощью косвенной адресации необходимо записать в регистры хранения байты, которые нужно передать, в коде передаются числа 10 и 20, после чего вызывается подпрограмма записи write_i2c. После возврата нужно проверить флаг ошибки (flag,6), чтобы удостовериться в успешной передаче данных.

Ошибка может возникнуть в случае, если ведомый не выдаст подтверждение в течение 100 мкс. Данное время можно подкорректировать, для этого необходимо записать другое число в регистр scetbit в подпрограмме peredi2c, в коде записывается число 20.

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

Приведенный код рассчитан на частоту тактового генератора в 4 МГц, для скорости передачи данных до 100 Кбит/сек. При использовании других частот и скоростей, необходимо корректировать подпрограмму паузы pausi2c, в коде пауза составляет 5 мкс.

Используя программный I2C, я подключал датчик давления BMP180, часы реального времени DS3231, модуль АЦП ADS1115, данный интерфейс также поддерживает акселерометр ADXL345.
Для подключения LCD 1602 по I2C, я также применил вышеприведенный код.

Последние записи:

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

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