Шина I2C: как устроена и как работать с ней в Arduino

Два провода, до 127 устройств и строгая иерархия «ведущий — ведомые»: разбираем принцип работы I2C и библиотеку Wire.
Два провода на всю периферию
I2C разработан инженерами Philips более 30 лет назад и с тех пор под разными названиями встречается в самой разной электронике. В проектах на Arduino это второй по распространённости интерфейс после UART: по нему контроллер общается с датчиками и исполнительными устройствами. Шина состоит всего из двух линий — SDA для данных и SCL для тактовых импульсов. К ним можно подключить до 127 устройств одновременно, и из дополнительных деталей понадобятся только два подтягивающих резистора.

По сравнению с UART шина I2C работает быстрее и устойчивее на высоких скоростях. По скорости, надёжности, дальности и расходу ресурсов она занимает промежуточное место между UART и SPI. Рассчитан интерфейс на небольшие расстояния, обычно в пределах одного прибора: длина проводов, как правило, не превышает нескольких метров.
Ведущий и ведомые
I2C — синхронный протокол: все участники обмена ориентируются на общий тактовый сигнал. Формирует его только одно устройство — ведущее (Master). Остальные называются ведомыми (Slave), и у каждого есть уникальный номер-адрес, даже если ведомый на шине единственный. У мастера адреса нет. Ведомые слушают линию и отвечают лишь тогда, когда ведущий обращается к ним по адресу, поэтому одновременно на шине никто не говорит.
Обращение начинается со спада уровня на линии SDA — это стартовый сигнал. Подъём SDA при высоком уровне тактового сигнала означает стоп. Всё, что передано между этими двумя событиями, составляет сообщение.

Внутри сообщения сначала идёт адрес ведомого длиной 7–10 бит в зависимости от разновидности протокола, затем бит чтения или записи, который показывает направление обмена. После каждого кадра следует бит подтверждения ACK или неподтверждения NACK от принимающей стороны. Без дополнительных мер воспользоваться этой информацией о доставке в программе нельзя: подтверждение лишь сокращает время передачи, обрывая её, если приёмник перестал отвечать.

Выводы плат и библиотека Wire
Аппаратная поддержка I2C есть на каждой плате Arduino. У UNO, Nano и Pro Mini линия SDA выведена на A4, SCL — на A5. У Mega это пины 20 (SDA) и 21 (SCL). В среде Arduino для работы с шиной служит стандартная библиотека Wire. Её основные функции:
- begin(address) — подключение к шине; без адреса плата становится мастером, с адресом — ведомым с этим номером;
- write() — отправка байта или последовательности байтов;
- read() — чтение очередного принятого байта;
- available() — количество принятых байтов, готовых к чтению;
- beginTransmission(address) и endTransmission() — начало и завершение передачи ведомому с указанным адресом, только для мастера;
- requestFrom(адрес, количество) — запрос у ведомого заданного числа байтов, только для мастера;
- onReceive(функция) и onRequest(функция) — назначение обработчиков на приём данных от мастера и на его запрос, только для ведомого.
Из этого набора видно, что логика на двух сторонах различается. Мастер действует директивно и подаёт команды, когда ему нужно. Ведомый реагирует автоматически: для приёма пишется функция-обработчик, а для ответа данные должны быть подготовлены заранее. Сам начать передачу ведомый не может, поэтому если мастеру нужна свежая информация, ему приходится регулярно опрашивать устройство.
Что подключают по I2C
Чаще всего контроллер выступает мастером, а ведомыми — индикаторы, дисплеи, часы реального времени, датчики температуры, влажности и других параметров, GPS-приёмники, RFID-считыватели. Для большинства таких модулей есть собственные библиотеки, внутри которых и спрятаны вызовы Wire.
Адрес модуля производитель указывает не всегда. В этом случае помогает пример i2c_scanner из комплекта библиотеки Wire: он перебирает адреса и показывает те, на которых кто-то откликнулся. Если адреса двух устройств совпали, нормально работать не будет ни одно из них. Поэтому почти у всех модулей адрес можно сменить — обычно запаиваемыми перемычками, переключателями или джамперами.
Обмен между двумя платами Arduino
По I2C можно связать и контроллеры между собой: один назначается ведущим, остальные — ведомыми. Платы соединяются линиями SDA–SDA и SCL–SCL. По правилам линии следует подтянуть к плюсу питания резисторами 1–10 кОм, но для пробы на коротких проводах схема работает и без них.
В простейшем стандартном примере мастер запускает шину вызовом Wire.begin() и раз в полсекунды отправляет устройству с номером 8 текст «x is » и байт-счётчик, увеличивая его после каждой посылки. Ведомый запускается как Wire.begin(8), назначает обработчик через onReceive и выводит принятое в монитор порта: каждые полсекунды там появляется строка с новым значением, хотя счёт ведётся на другой плате.

Обратный вариант: мастер вызывает Wire.requestFrom(8, 6), то есть просит у восьмого устройства шесть байтов, а ведомый в обработчике onRequest отправляет строку «hello ». Сообщение может быть короче шести байтов, а более длинное обрежется. Оба направления можно совместить. В примере с кнопкой и светодиодом на каждой плате (кнопка на пине 7, светодиод на пине 6) мастер каждые 100 мс запрашивает у ведомого один байт с состоянием его кнопки и зажигает по нему свой светодиод, а при нажатии и отпускании собственной кнопки отправляет ведомому 1 или 0. В итоге кнопка одной платы управляет светодиодом другой.
Скорость и границы применимости
Скорость обмена зависит от тактовой частоты контроллера и размера пакетов и подстраивается под самое медленное устройство на шине. Для Arduino с частотой 16 МГц это порядка 100 кбит/с. Если этого мало, следует смотреть в сторону SPI: проводов потребуется больше, зато скорость выше.