В статті Цифрові інтерфейси зв'язку: UART ми розглядали можливість комунікації між пристроями через порти мікроконтролера. Протокол UART добре підходить для простого обміну даними між двома пристроями. Він легко реалізується, потребує мінімум налаштувань і часто використовується для налагодження або передачі даних на невеликі відстані. Проте така простота має свою ціну.
Основна ідея UART — це з’єднання типу «точка-точка», коли один пристрій передає, а інший приймає. Але як тільки в системі з’являється більше двох пристроїв, ситуація ускладнюється: доводиться або використовувати додаткові апаратні UART-порти, або вигадувати способи перемикання між пристроями, що робить схему громіздкою та менш надійною. До цього додається асинхронна природа інтерфейсу: обидві сторони повинні заздалегідь домовитися про швидкість обміну, і будь-яке відхилення може призвести до помилок у передачі даних. У підсумку, при спробі побудувати мережу з кількох пристроїв UART швидко перестає бути зручним рішенням.
Саме для таких випадків і був створений I2C (від англ. Inter-Integrated Circuit). На відміну від UART, він одразу проєктувався як шина, до якої можна підключити багато пристроїв одночасно. Усі вони використовують лише дві спільні лінії — для даних і для тактування, що значно спрощує як схему підключення, так і вимоги до кількості пінів мікроконтролера. Ключовою перевагою є те, що кожен пристрій у такій системі має власну адресу, і керуючий пристрій (master) може напряму звертатися до потрібного, не ускладнюючи протокол.
В результаті I2C дозволяє будувати більш гнучкі та масштабовані системи, де десятки компонентів — датчики, пам’ять, дисплеї чи навіть інші мікроконтролери — можуть працювати разом на одній шині. Саме тому, коли задача виходить за межі простого обміну даними між двома пристроями, I2C стає більш природним і ефективним вибором.
Отже, I2C - це послідовний синхронний протокол зв'язку периферійних компонентів, який працює на двох лініях:
- SDA (від англ. Serial Data Line) - послідовна, двонаправлена лінія даних.
- SCL (від англ. Serial Clock Line) - лінія для передачі сигналів тактування (синхронізації).
Протокол широко застосовується для підключення пристроїв, таких як LCD та OLED-дисплеї, RTC-модулі та різноманітні сенсори, і зазвичай використовується на коротких відстанях (в межах одного пристрою або плати).
Загальна схема з'єднання пари пристроїв:

Лінії SDA та SCL в інтерфейсі I²C використовують виходи з відкритим стоком (open-drain). Це означає, що пристрої на шині можуть лише притягувати сигнал до рівня LOW, але не здатні активно встановлювати рівень HIGH. Формування високого рівня відбувається за допомогою підтягувальних резисторів (pull-up), які можуть бути як внутрішніми, так і зовнішніми. Саме вони «підтягують» лінію до HIGH, коли жоден пристрій її не утримує в LOW (детальніше про підтягувальні резистори можна прочитати в статті Підключення приладів вводу/виводу до мікроконтролера).
Якщо підтягувальні резистори відсутні, лінія переходить у невизначений (плаваючий) стан, що призводить до появи некоректних даних на шині. Зазвичай використовують резистори номіналом від 1 кОм до 10 кОм (для довгих ліній або роботи на високих швидкостях доцільно обирати менший опір, щоб забезпечити швидше встановлення рівня HIGH).
Передача даних
Обмін даними в I2C відбувається за принципом master/slave (ведучий/ведений). До однієї шини можуть бути підключені один або кілька ведучих пристроїв і багато ведених, які використовують спільні лінії SDA та SCL. У кожен момент часу передачею керує один ведучий (master): він ініціює обмін, формує тактовий сигнал на лінії SCL і визначає, який саме пристрій братиме участь у передачі.
Кожен ведений пристрій (slave) має унікальну адресу, яка зазвичай вже задана на рівні самого чіпа виробником. Для початку обміну master передає адресу потрібного пристрою разом із бітом операції (читання або запису). У відповідь лише той slave, адреса якого співпадає, активується і бере участь у подальшій передачі, тоді як інші ігнорують запит.
Часова діаграма передачі 1 байту даних I2C:

Опис структури фрейму даних I2C:
Сигнал початку передачі даних- SCL залишається в стані 1, в той час як SDA лінія підтягується ведучим пристроєм до нуля.7-бітна адреса- адреса веденого пристрою, до якого звертається ведучий.Біт Запис/Читання (Read/Write)- біт вказує на бажану операцію: 0 - запис у slave, 1 - зчитування зі slave.Біт підтвердження (ACK/NACK)- якщо до шини приєднано пристрій з вказаною адресою, то цей пристрій підтягує лінію до нуля, вказуючи таким чином ведучому, що адресу прийнята і можна починати передачу.1 Байт даних- якщо вказано операцію Read, то цей байт формує ведений пристрій, в той час як ведучий його зчитує. І навпаки, коли вказана операція Write. Байт починається зі старшого біта (MSB).Біт підтвердження (ACK/NACK)- пристрій, який читав дані підтягує біт до нуля у знак успішного отримання даних.
Далі передача байтів продовжується допоки ведучий не подасть сигнал завершення передачі (SCL тримається на високому рівні, SDA переходить від низького до високого).
Під час передачі даних ведений (slave) пристрій може призупиняти комунікацію, утримуючи лінію SCL на низькому рівні, заставляючи ведучого (master) очікувати. Таким чином slave має час на обробку даних при необхідності.
Швидкість передачі даних у I2C визначається частотою тактової лінії SCL і може відрізнятися залежно від режиму роботи. Найчастіше використовуються стандартний режим (до 100 кГц) і швидкий режим (до 400 кГц), а також більш швидкі варіанти — Fast Mode Plus (до 1 МГц) і High-Speed Mode (до 3.4 МГц). Важливо розуміти, що це не “чиста” швидкість передачі даних, адже кожен байт супроводжується службовими бітами (наприклад, ACK), а також затримками через особливості шини. Тому фактична пропускна здатність буде трохи нижчою, але навіть цього достатньо для більшості задач — роботи з датчиками, пам’яттю чи периферією.
Арбітраж I2C
Арбітраж у I2C виникає в системах із кількома master-пристроями, коли вони одночасно намагаються розпочати передачу. У такій ситуації шину отримує той master, який першим передає біт зі значенням 0 (LOW). Це відбувається тому, що при передачі нуля пристрій примусово тягне лінію SDA до низького рівня, навіть якщо інший master у цей момент намагається передати одиницю (HIGH).
Наприклад, якщо один master передає байт 0xB2 (10110010), а інший — 0xB4 (10110100), то перші біти збігаються, і передача відбувається синхронно. Однак у момент, коли з’являється відмінність, master, який передає 0, виграє арбітраж і продовжує передачу, тоді як інший припиняє спробу керування шиною.
I2C в мікроконтролерах
Як важливий протокол внутрішнього обміну даними, I2C присутній у більшості мікроконтролерів, зокрема таких як AVR, PIC, ARM і RISC.
Зазвичай у мікроконтролерах передбачено набір відповідних регістрів для роботи з цим інтерфейсом (наприклад, у AVR ATmega328P, де він також відомий як TWI — Two-Wire Interface):
| Регістр | Опис |
| TWBR | Регістр швидкості (Bit Rate Register) у I2C використовується для налаштування частоти тактового сигналу шини, тобто визначає швидкість обміну даними. |
| TWCR | Регістр керування (Control Register) у I2C відповідає за керування роботою інтерфейсу: він дозволяє вмикати модуль, обробляти переривання, а також керувати підтвердженням (ACK) і формуванням умов START та STOP. |
| TWSR | Регістр стану (Status Register) у I2C зберігає код поточного стану шини після кожної операції, що дозволяє визначити, на якому етапі знаходиться обмін даними, а також використовується для налаштування дільника тактової частоти. |
| TWDR | Буфер даних (Data Register) у I2C використовується для зберігання байта, який передається, або байта, який був прийнятий через інтерфейс. |
| TWAR | Регістр адреси (Address Register) у I2C зберігає адресу пристрою (slave) та дозволяє реагувати на загальну адресу виклику (general call). |
I2C в Arduino Nano
Arduino Nano має апаратний інтерфейс I2C для обміну даними між платою та периферійними пристроями, такими як датчики, дисплеї або пам’ять. На платі цей інтерфейс виведений на піни A4 (SDA) і A5 (SCL), через які відбувається синхронна передача даних між master і slave-пристроями.

У Arduino для роботи з I2C використовується бібліотека Wire.h, яка спрощує ініціалізацію шини, обмін байтами та керування передачею, дозволяючи взаємодіяти з I2C-пристроями без прямої роботи з регістрами мікроконтролера. Стандартна швидкість становить 100 кГц, але її можна змінити до 400 кГц. В бібліотеці маються такі функції:
Wire.begin()- Ініціалізація I2C у режимі master.Wire.begin(address)- Ініціалізація I2C у режимі slave з заданою адресою.Wire.setClock(fequency)- Встановлення швидкості I2C (у герцах).Wire.beginTransmission(address)- Початок обміну даними з веденим пристроєм (slave) у I2C.Wire.write(data)- Надсилання даних у буфер I2C.Wire.endTransmission()- Завершує передачу, розпочату черезbeginTransmission()та надсилає всі накопичені в черзі байти до пристрою.Wire.requestFrom(address, len)- Запит байтів від веденого пристрою (slave).Wire.available()- Перевірка, чи були отримані дані.Wire.read()- Зчитати прийнятий байт.Wire.onReceive(handler)- Встановлення callback-функції для обробки отриманих даних.Wire.onRequest(handler)- Встановлення callback-функції для обробки запитів на передачу даних.
Приклад коду I2C в Arduino
Передача символу ‘A’ від master-пристрою до slave з адресою 0x08 кожну секунду через I2C.
Код ведучого пристрою:
#include <Wire.h> //підключаємо бібліотеку I2C (Arduino)void setup() {Wire.begin(); //Ініціалізуємо I2C ведучого пристрою}void loop() {Wire.beginTransmission(0x08); //вказуємо slave-пристрій з адресою 0x08Wire.write('A'); //відправляємо даніWire.endTransmission(); //завершуємо передачуdelay(1000); //очікуємо 1 секунду}
Код веденого:
#include <Wire.h>void setup() {Serial.begin(9600); //запускаємо Serial Monitor для виводу даних на ПКWire.begin(0x08); //ініціалізуємо I2C для slave з адресою 0х08Wire.onReceive(receiveEvent); //реєструємо колбек-функцію}void loop() {}//колбек-функція:void receiveEvent(int bytesReceived) {if (Wire.available()) { //якщо дані від ведучого пристроя є...char receiveData = Wire.read(); //зчитуємо їхSerial.print(receiveData); //виводимо отримані дані в консоль}}
